Skip to content

Commit

Permalink
more improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
gabrielfalcao committed Jan 1, 2024
1 parent 97eb021 commit aadc315
Show file tree
Hide file tree
Showing 8 changed files with 71 additions and 48 deletions.
5 changes: 2 additions & 3 deletions docs/source/api-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ API Reference
.. autoclass:: sure.core.DeepExplanation
.. _deep comparison:
.. autoclass:: sure.core.DeepComparison
.. autofunction:: sure.core._get_file_name
.. autofunction:: sure.core._get_line_number
.. autofunction:: sure.core.itemize_length


Expand Down Expand Up @@ -66,6 +64,7 @@ API Reference
.. py:module:: sure.reporters
.. autoclass:: sure.reporters.feature.FeatureReporter


``sure.original``
-----------------

Expand All @@ -75,7 +74,6 @@ API Reference
.. autofunction:: sure.original.all_integers
.. autofunction:: sure.original.explanation


``sure.doubles``
----------------

Expand All @@ -84,6 +82,7 @@ API Reference
.. autoclass:: sure.doubles.FakeOrderedDict
.. autoattribute:: sure.doubles.anything


``sure.doubles.dummies``
------------------------

Expand Down
10 changes: 5 additions & 5 deletions sure/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@
from sure import runtime
from sure.core import DeepComparison
from sure.core import DeepExplanation
from sure.core import _get_file_name
from sure.core import _get_line_number
from sure.errors import SpecialSyntaxDisabledError
from sure.errors import InternalRuntimeError
from sure.doubles.dummies import anything
from sure.loader import get_file_name
from sure.loader import get_line_number
from sure.version import version
from sure.special import is_cpython, patchable_builtin
from sure.registry import context as _registry
Expand Down Expand Up @@ -339,8 +339,8 @@ def ensure_providers(func, attr, args, kwargs):

def check_dependencies(func):
action = func.__name__
filename = _get_file_name(func)
lineno = _get_line_number(func)
filename = get_file_name(func)
lineno = get_line_number(func)

for dependency in depends_on:
if dependency in context.__sure_providers_of__:
Expand All @@ -354,7 +354,7 @@ def check_dependencies(func):
err += "\n".join(
[
" -> %s at %s:%d"
% (p.__name__, _get_file_name(p), _get_line_number(p))
% (p.__name__, get_file_name(p), get_line_number(p))
for p in providers
]
)
Expand Down
2 changes: 2 additions & 0 deletions sure/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ def entrypoint(paths, reporter, immediate, log_level, log_file, special_syntax,
raise ExitError(runner.context, result)

elif cov:
sys.stdout = sys.__stdout__
sys.stderr = sys.__stderr__
cov.stop()
cov.save()
cov.report()
Expand Down
29 changes: 5 additions & 24 deletions sure/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import inspect

from collections import OrderedDict
from functools import cache
from six import (
text_type, integer_types, string_types, binary_type,
get_function_code
Expand Down Expand Up @@ -50,6 +48,8 @@ def __init__(self, X, Y, epsilon=None, parent=None):
float: self.compare_floats,
dict: self.compare_ordered_dicts,
list: self.compare_iterables,
set: self.compare_iterables,
frozenset: self.compare_iterables,
tuple: self.compare_iterables,
OrderedDict: self.compare_ordered_dicts
}
Expand Down Expand Up @@ -144,10 +144,8 @@ def compare_ordered_dicts(self, X, Y):
return DeepExplanation(msg)
return True

@cache
def get_context(self):
if self._context:
return self._context

X_keys = []
Y_keys = []

Expand All @@ -168,8 +166,7 @@ class ComparisonContext:
current_Y_keys = get_keys(Y_keys)
parent = comp

self._context = ComparisonContext()
return self._context
return ComparisonContext()

def compare_iterables(self, X, Y):
len_X, len_Y = map(len, (X, Y))
Expand Down Expand Up @@ -242,22 +239,6 @@ def explanation(self):
return self._explanation


def _get_file_name(func):
try:
name = inspect.getfile(func)
except AttributeError:
name = get_function_code(func).co_filename

return os.path.abspath(name)


def _get_line_number(func):
try:
return inspect.getlineno(func)
except AttributeError:
return get_function_code(func).co_firstlineno


def itemize_length(items):
length = len(items)
return '{0} item{1}'.format(length, length > 1 and "s" or "")
44 changes: 43 additions & 1 deletion sure/loader.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import os
import sys
import ast
import types
import importlib
import importlib.util
from typing import Dict, List, Union, Tuple

from typing import Dict, List, Optional, Tuple, Union
from importlib.machinery import PathFinder
from pathlib import Path
from sure.errors import InternalRuntimeError
Expand All @@ -13,6 +15,42 @@
__TEST_CLASSES__ = {}


def get_file_name(func) -> str:
"""returns the file name of a given function or method"""
return FunMeta.from_function_or_method(func).filename


def get_line_number(func) -> str:
"""returns the first line number of a given function or method"""
return FunMeta.from_function_or_method(func).line_number


class FunMeta(object):
"""container for metadata specific to Python functions or methods"""
filename: str
line_number: int
name: str

def __init__(self, filename: str, line_number: int, name: str):
self.filename = collapse_path(filename)
self.line_number = line_number
self.name = name

def __repr__(self):
return f'<FunMeta filename={repr(self.filename)} line_number={repr(self.line_number)} name={repr(self.name)}>'

@classmethod
def from_function_or_method(cls, func):
if not isinstance(func, (types.FunctionType, types.MethodType)):
raise TypeError(f'get_function_or_method_metadata received an unexpected object: {func}')

return cls(
filename=func.__code__.co_filename,
line_number=func.__code__.co_firstlineno,
name=func.__name__,
)


def name_appears_to_indicate_test(name: str) -> bool:
return name.startswith('Test') or name.endswith('Test')

Expand Down Expand Up @@ -93,6 +131,10 @@ def resolve_path(path, relative_to="~") -> Path:
return Path(path).absolute().relative_to(Path(relative_to).expanduser())


def collapse_path(e: Union[str, Path]) -> str:
return str(e).replace(os.getenv("HOME"), "~")


def get_package(path) -> Path:
if not isinstance(path, Path):
path = Path(path)
Expand Down
4 changes: 2 additions & 2 deletions sure/original.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@
from six import string_types, text_type

from sure.core import DeepComparison
from sure.core import _get_file_name
from sure.core import _get_line_number
from sure.core import itemize_length
from sure.loader import get_file_name
from sure.loader import get_line_number


def identify_callable_location(callable_object):
Expand Down
13 changes: 6 additions & 7 deletions sure/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
)
from sure.loader import (
loader,
collapse_path,
get_type_definition_filename_and_firstlineno,
)
from sure.reporter import Reporter
Expand Down Expand Up @@ -117,10 +118,12 @@ def __init__(self, test, module_or_instance=None):
self.name = test.__class__.__name__
self.filename, self.line = get_type_definition_filename_and_firstlineno(test.__class__)
self.kind = test.__class__

elif isinstance(test, type):
self.name = test.__name__
self.filename, self.line = get_type_definition_filename_and_firstlineno(test)
self.kind = test

else:
raise NotImplementedError(f"{test} of type {type(test)} is not yet supported by {TestLocation}")

Expand Down Expand Up @@ -765,7 +768,7 @@ def __getattr__(self, attr):
try:
return self.__getattribute__(attr)
except AttributeError:
return getattr(self.scenario_results[-1], attr, fallback)
return getattr(self.scenario_results[-1], attr)

@property
def is_failure(self):
Expand Down Expand Up @@ -843,7 +846,7 @@ def __getattr__(self, attr):
try:
return self.__getattribute__(attr)
except AttributeError:
return getattr(self.scenario_results[-1], attr, fallback)
return getattr(self.scenario_results[-1], attr)

@property
def is_failure(self):
Expand Down Expand Up @@ -920,7 +923,7 @@ def __getattr__(self, attr):
try:
return self.__getattribute__(attr)
except AttributeError:
return getattr(self.feature_results[-1], attr, fallback)
return getattr(self.feature_results[-1], attr)

@property
def is_failure(self):
Expand Down Expand Up @@ -957,7 +960,3 @@ def first_nonsuccessful_result(self) -> Optional[FeatureResult]:

def stripped(string):
return collapse_path("\n".join(filter(bool, [s.strip() for s in string.splitlines()])))


def collapse_path(e: str):
return str(e).replace(os.getenv("HOME"), "~")
12 changes: 6 additions & 6 deletions tests/test_original_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import sure
from sure import that
from sure import expect
from sure import VariablesBag
from sure.special import is_cpython
from sure.loader import collapse_path


def test_setup_with_context():
Expand Down Expand Up @@ -52,7 +54,7 @@ def it_crashes():
assert that(it_crashes).raises(
TypeError,
(
"the function it_crashes defined at test_original_api.py line 49, is being "
"the function it_crashes defined at test_original_api.py line 51, is being "
"decorated by either @that_with_context or @scenario, so it should "
"take at least 1 parameter, which is the test context"
),
Expand Down Expand Up @@ -925,12 +927,11 @@ def the_providers_are_working(the):

def test_depends_on_failing_due_to_lack_of_attribute_in_context():
"it fails when an action depends on some attribute that is not " "provided by any other previous action"
import os
from sure import action_for, scenario

fullpath = os.path.abspath(__file__)
fullpath = collapse_path(collapse_path(os.path.abspath(__file__)))
error = (
'the action "variant_action" defined at %s:940 '
'the action "variant_action" defined at %s:941 '
'depends on the attribute "data_structure" to be available in the'
" context. It turns out that there are no actions providing "
"that. Please double-check the implementation" % fullpath
Expand All @@ -952,10 +953,9 @@ def depends_on_fails(the):
def test_depends_on_failing_due_not_calling_a_previous_action():
"it fails when an action depends on some attribute that is being " "provided by other actions"

import os
from sure import action_for, scenario

fullpath = os.path.abspath(__file__)
fullpath = collapse_path(os.path.abspath(__file__))
error = (
'the action "my_action" defined at {0}:971 '
'depends on the attribute "some_attr" to be available in the context.'
Expand Down

0 comments on commit aadc315

Please sign in to comment.