Skip to content

Commit

Permalink
more improvements
Browse files Browse the repository at this point in the history
gabrielfalcao committed Jan 1, 2024
1 parent 97eb021 commit aadc315
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
@@ -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


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


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

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


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

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


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

10 changes: 5 additions & 5 deletions sure/__init__.py
Original file line number Diff line number Diff line change
@@ -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
@@ -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__:
@@ -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
]
)
2 changes: 2 additions & 0 deletions sure/cli.py
Original file line number Diff line number Diff line change
@@ -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()
29 changes: 5 additions & 24 deletions sure/core.py
Original file line number Diff line number Diff line change
@@ -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
@@ -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
}
@@ -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 = []

@@ -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))
@@ -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
@@ -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')

@@ -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)
4 changes: 2 additions & 2 deletions sure/original.py
Original file line number Diff line number Diff line change
@@ -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):
13 changes: 6 additions & 7 deletions sure/runtime.py
Original file line number Diff line number Diff line change
@@ -38,6 +38,7 @@
)
from sure.loader import (
loader,
collapse_path,
get_type_definition_filename_and_firstlineno,
)
from sure.reporter import Reporter
@@ -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}")

@@ -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):
@@ -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):
@@ -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):
@@ -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
@@ -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():
@@ -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"
),
@@ -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
@@ -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.'

0 comments on commit aadc315

Please sign in to comment.