From e492b1d567f74588e712265dd8016b2156cf6afa Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 30 Jun 2020 00:05:12 +0300 Subject: [PATCH] python: don't pass entire Item for generating ID Just the nodeid is enough for the error messages. This removes an import cycle. --- src/_pytest/mark/structures.py | 7 ++----- src/_pytest/python.py | 29 +++++++++++++++------------ testing/python/metafunc.py | 36 ++++++++-------------------------- 3 files changed, 27 insertions(+), 45 deletions(-) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 2d756bb42..ca5c03e90 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -27,9 +27,6 @@ from _pytest.config import Config from _pytest.outcomes import fail from _pytest.warning_types import PytestUnknownMarkWarning -if TYPE_CHECKING: - from _pytest.python import FunctionDefinition - EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" @@ -159,7 +156,7 @@ class ParameterSet( argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], func, config: Config, - function_definition: "FunctionDefinition", + nodeid: str, ) -> Tuple[Union[List[str], Tuple[str, ...]], List["ParameterSet"]]: argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) @@ -177,7 +174,7 @@ class ParameterSet( ) fail( msg.format( - nodeid=function_definition.nodeid, + nodeid=nodeid, values=param.values, names=argnames, names_len=len(argnames), diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 7fbd59add..65855ca23 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -980,7 +980,7 @@ class Metafunc: argvalues, self.function, self.config, - function_definition=self.definition, + nodeid=self.definition.nodeid, ) del argvalues @@ -1003,7 +1003,9 @@ class Metafunc: if generated_ids is not None: ids = generated_ids - ids = self._resolve_arg_ids(argnames, ids, parameters, item=self.definition) + ids = self._resolve_arg_ids( + argnames, ids, parameters, nodeid=self.definition.nodeid + ) # Store used (possibly generated) ids with parametrize Marks. if _param_mark and _param_mark._param_ids_from and generated_ids is None: @@ -1042,7 +1044,7 @@ class Metafunc: ] ], parameters: typing.Sequence[ParameterSet], - item, + nodeid: str, ) -> List[str]: """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given to ``parametrize``. @@ -1050,7 +1052,7 @@ class Metafunc: :param List[str] argnames: list of argument names passed to ``parametrize()``. :param ids: the ids parameter of the parametrized call (see docs). :param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``. - :param Item item: the item that generated this parametrized call. + :param str str: the nodeid of the item that generated this parametrized call. :rtype: List[str] :return: the list of ids for each argname given """ @@ -1063,7 +1065,7 @@ class Metafunc: else: idfn = None ids_ = self._validate_ids(ids, parameters, self.function.__name__) - return idmaker(argnames, parameters, idfn, ids_, self.config, item=item) + return idmaker(argnames, parameters, idfn, ids_, self.config, nodeid=nodeid) def _validate_ids( self, @@ -1223,7 +1225,7 @@ def _idval( argname: str, idx: int, idfn: Optional[Callable[[object], Optional[object]]], - item, + nodeid: Optional[str], config: Optional[Config], ) -> str: if idfn: @@ -1232,8 +1234,9 @@ def _idval( if generated_id is not None: val = generated_id except Exception as e: - msg = "{}: error raised while trying to determine id of parameter '{}' at position {}" - msg = msg.format(item.nodeid, argname, idx) + prefix = "{}: ".format(nodeid) if nodeid is not None else "" + msg = "error raised while trying to determine id of parameter '{}' at position {}" + msg = prefix + msg.format(argname, idx) raise ValueError(msg) from e elif config: hook_id = config.hook.pytest_make_parametrize_id( @@ -1263,7 +1266,7 @@ def _idvalset( argnames: Iterable[str], idfn: Optional[Callable[[object], Optional[object]]], ids: Optional[List[Union[None, str]]], - item, + nodeid: Optional[str], config: Optional[Config], ): if parameterset.id is not None: @@ -1271,7 +1274,7 @@ def _idvalset( id = None if ids is None or idx >= len(ids) else ids[idx] if id is None: this_id = [ - _idval(val, argname, idx, idfn, item=item, config=config) + _idval(val, argname, idx, idfn, nodeid=nodeid, config=config) for val, argname in zip(parameterset.values, argnames) ] return "-".join(this_id) @@ -1285,10 +1288,12 @@ def idmaker( idfn: Optional[Callable[[object], Optional[object]]] = None, ids: Optional[List[Union[None, str]]] = None, config: Optional[Config] = None, - item=None, + nodeid: Optional[str] = None, ) -> List[str]: resolved_ids = [ - _idvalset(valindex, parameterset, argnames, idfn, ids, config=config, item=item) + _idvalset( + valindex, parameterset, argnames, idfn, ids, config=config, nodeid=nodeid + ) for valindex, parameterset in enumerate(parametersets) ] diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index c4b5bd222..1f110d419 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -19,6 +19,7 @@ from _pytest import python from _pytest.outcomes import fail from _pytest.pytester import Testdir from _pytest.python import _idval +from _pytest.python import idmaker class TestMetafunc: @@ -35,10 +36,11 @@ class TestMetafunc: @attr.s class DefinitionMock(python.FunctionDefinition): obj = attr.ib() + _nodeid = attr.ib() names = fixtures.getfuncargnames(func) fixtureinfo = FuncFixtureInfoMock(names) # type: Any - definition = DefinitionMock._create(func) # type: Any + definition = DefinitionMock._create(func, "mock::nodeid") # type: Any return python.Metafunc(definition, fixtureinfo, config) def test_no_funcargs(self) -> None: @@ -270,7 +272,7 @@ class TestMetafunc: deadline=400.0 ) # very close to std deadline and CI boxes are not reliable in CPU power def test_idval_hypothesis(self, value) -> None: - escaped = _idval(value, "a", 6, None, item=None, config=None) + escaped = _idval(value, "a", 6, None, nodeid=None, config=None) assert isinstance(escaped, str) escaped.encode("ascii") @@ -292,7 +294,7 @@ class TestMetafunc: ), ] for val, expected in values: - assert _idval(val, "a", 6, None, item=None, config=None) == expected + assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected def test_unicode_idval_with_config(self) -> None: """unittest for expected behavior to obtain ids with @@ -321,7 +323,7 @@ class TestMetafunc: ("ação", MockConfig({option: False}), "a\\xe7\\xe3o"), ] # type: List[Tuple[str, Any, str]] for val, config, expected in values: - actual = _idval(val, "a", 6, None, item=None, config=config) + actual = _idval(val, "a", 6, None, nodeid=None, config=config) assert actual == expected def test_bytes_idval(self) -> None: @@ -338,7 +340,7 @@ class TestMetafunc: ("αρά".encode(), "\\xce\\xb1\\xcf\\x81\\xce\\xac"), ] for val, expected in values: - assert _idval(val, "a", 6, idfn=None, item=None, config=None) == expected + assert _idval(val, "a", 6, idfn=None, nodeid=None, config=None) == expected def test_class_or_function_idval(self) -> None: """unittest for the expected behavior to obtain ids for parametrized @@ -353,12 +355,10 @@ class TestMetafunc: values = [(TestClass, "TestClass"), (test_function, "test_function")] for val, expected in values: - assert _idval(val, "a", 6, None, item=None, config=None) == expected + assert _idval(val, "a", 6, None, nodeid=None, config=None) == expected def test_idmaker_autoname(self) -> None: """#250""" - from _pytest.python import idmaker - result = idmaker( ("a", "b"), [pytest.param("string", 1.0), pytest.param("st-ring", 2.0)] ) @@ -373,14 +373,10 @@ class TestMetafunc: assert result == ["a0-\\xc3\\xb4"] def test_idmaker_with_bytes_regex(self) -> None: - from _pytest.python import idmaker - result = idmaker(("a"), [pytest.param(re.compile(b"foo"), 1.0)]) assert result == ["foo"] def test_idmaker_native_strings(self) -> None: - from _pytest.python import idmaker - result = idmaker( ("a", "b"), [ @@ -414,8 +410,6 @@ class TestMetafunc: ] def test_idmaker_non_printable_characters(self) -> None: - from _pytest.python import idmaker - result = idmaker( ("s", "n"), [ @@ -430,8 +424,6 @@ class TestMetafunc: assert result == ["\\x00-1", "\\x05-2", "\\x00-3", "\\x05-4", "\\t-5", "\\t-6"] def test_idmaker_manual_ids_must_be_printable(self) -> None: - from _pytest.python import idmaker - result = idmaker( ("s",), [ @@ -442,8 +434,6 @@ class TestMetafunc: assert result == ["hello \\x00", "hello \\x05"] def test_idmaker_enum(self) -> None: - from _pytest.python import idmaker - enum = pytest.importorskip("enum") e = enum.Enum("Foo", "one, two") result = idmaker(("a", "b"), [pytest.param(e.one, e.two)]) @@ -451,7 +441,6 @@ class TestMetafunc: def test_idmaker_idfn(self) -> None: """#351""" - from _pytest.python import idmaker def ids(val: object) -> Optional[str]: if isinstance(val, Exception): @@ -471,7 +460,6 @@ class TestMetafunc: def test_idmaker_idfn_unique_names(self) -> None: """#351""" - from _pytest.python import idmaker def ids(val: object) -> str: return "a" @@ -492,7 +480,6 @@ class TestMetafunc: disable_test_id_escaping_and_forfeit_all_rights_to_community_support option. (#5294) """ - from _pytest.python import idmaker class MockConfig: def __init__(self, config): @@ -525,7 +512,6 @@ class TestMetafunc: disable_test_id_escaping_and_forfeit_all_rights_to_community_support option. (#5294) """ - from _pytest.python import idmaker class MockConfig: def __init__(self, config): @@ -607,16 +593,12 @@ class TestMetafunc: ) def test_idmaker_with_ids(self) -> None: - from _pytest.python import idmaker - result = idmaker( ("a", "b"), [pytest.param(1, 2), pytest.param(3, 4)], ids=["a", None] ) assert result == ["a", "3-4"] def test_idmaker_with_paramset_id(self) -> None: - from _pytest.python import idmaker - result = idmaker( ("a", "b"), [pytest.param(1, 2, id="me"), pytest.param(3, 4, id="you")], @@ -625,8 +607,6 @@ class TestMetafunc: assert result == ["me", "you"] def test_idmaker_with_ids_unique_names(self) -> None: - from _pytest.python import idmaker - result = idmaker( ("a"), map(pytest.param, [1, 2, 3, 4, 5]), ids=["a", "a", "b", "c", "b"] )