python: don't pass entire Item for generating ID

Just the nodeid is enough for the error messages.
This removes an import cycle.
This commit is contained in:
Ran Benita 2020-06-30 00:05:12 +03:00
parent e6e300e729
commit e492b1d567
3 changed files with 27 additions and 45 deletions

View File

@ -27,9 +27,6 @@ from _pytest.config import Config
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.warning_types import PytestUnknownMarkWarning from _pytest.warning_types import PytestUnknownMarkWarning
if TYPE_CHECKING:
from _pytest.python import FunctionDefinition
EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark" EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
@ -159,7 +156,7 @@ class ParameterSet(
argvalues: Iterable[Union["ParameterSet", Sequence[object], object]], argvalues: Iterable[Union["ParameterSet", Sequence[object], object]],
func, func,
config: Config, config: Config,
function_definition: "FunctionDefinition", nodeid: str,
) -> Tuple[Union[List[str], Tuple[str, ...]], List["ParameterSet"]]: ) -> Tuple[Union[List[str], Tuple[str, ...]], List["ParameterSet"]]:
argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues) argnames, force_tuple = cls._parse_parametrize_args(argnames, argvalues)
parameters = cls._parse_parametrize_parameters(argvalues, force_tuple) parameters = cls._parse_parametrize_parameters(argvalues, force_tuple)
@ -177,7 +174,7 @@ class ParameterSet(
) )
fail( fail(
msg.format( msg.format(
nodeid=function_definition.nodeid, nodeid=nodeid,
values=param.values, values=param.values,
names=argnames, names=argnames,
names_len=len(argnames), names_len=len(argnames),

View File

@ -980,7 +980,7 @@ class Metafunc:
argvalues, argvalues,
self.function, self.function,
self.config, self.config,
function_definition=self.definition, nodeid=self.definition.nodeid,
) )
del argvalues del argvalues
@ -1003,7 +1003,9 @@ class Metafunc:
if generated_ids is not None: if generated_ids is not None:
ids = generated_ids 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. # Store used (possibly generated) ids with parametrize Marks.
if _param_mark and _param_mark._param_ids_from and generated_ids is None: if _param_mark and _param_mark._param_ids_from and generated_ids is None:
@ -1042,7 +1044,7 @@ class Metafunc:
] ]
], ],
parameters: typing.Sequence[ParameterSet], parameters: typing.Sequence[ParameterSet],
item, nodeid: str,
) -> List[str]: ) -> List[str]:
"""Resolves the actual ids for the given argnames, based on the ``ids`` parameter given """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given
to ``parametrize``. to ``parametrize``.
@ -1050,7 +1052,7 @@ class Metafunc:
:param List[str] argnames: list of argument names passed to ``parametrize()``. :param List[str] argnames: list of argument names passed to ``parametrize()``.
:param ids: the ids parameter of the parametrized call (see docs). :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 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] :rtype: List[str]
:return: the list of ids for each argname given :return: the list of ids for each argname given
""" """
@ -1063,7 +1065,7 @@ class Metafunc:
else: else:
idfn = None idfn = None
ids_ = self._validate_ids(ids, parameters, self.function.__name__) 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( def _validate_ids(
self, self,
@ -1223,7 +1225,7 @@ def _idval(
argname: str, argname: str,
idx: int, idx: int,
idfn: Optional[Callable[[object], Optional[object]]], idfn: Optional[Callable[[object], Optional[object]]],
item, nodeid: Optional[str],
config: Optional[Config], config: Optional[Config],
) -> str: ) -> str:
if idfn: if idfn:
@ -1232,8 +1234,9 @@ def _idval(
if generated_id is not None: if generated_id is not None:
val = generated_id val = generated_id
except Exception as e: except Exception as e:
msg = "{}: error raised while trying to determine id of parameter '{}' at position {}" prefix = "{}: ".format(nodeid) if nodeid is not None else ""
msg = msg.format(item.nodeid, argname, idx) msg = "error raised while trying to determine id of parameter '{}' at position {}"
msg = prefix + msg.format(argname, idx)
raise ValueError(msg) from e raise ValueError(msg) from e
elif config: elif config:
hook_id = config.hook.pytest_make_parametrize_id( hook_id = config.hook.pytest_make_parametrize_id(
@ -1263,7 +1266,7 @@ def _idvalset(
argnames: Iterable[str], argnames: Iterable[str],
idfn: Optional[Callable[[object], Optional[object]]], idfn: Optional[Callable[[object], Optional[object]]],
ids: Optional[List[Union[None, str]]], ids: Optional[List[Union[None, str]]],
item, nodeid: Optional[str],
config: Optional[Config], config: Optional[Config],
): ):
if parameterset.id is not None: 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] id = None if ids is None or idx >= len(ids) else ids[idx]
if id is None: if id is None:
this_id = [ 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) for val, argname in zip(parameterset.values, argnames)
] ]
return "-".join(this_id) return "-".join(this_id)
@ -1285,10 +1288,12 @@ def idmaker(
idfn: Optional[Callable[[object], Optional[object]]] = None, idfn: Optional[Callable[[object], Optional[object]]] = None,
ids: Optional[List[Union[None, str]]] = None, ids: Optional[List[Union[None, str]]] = None,
config: Optional[Config] = None, config: Optional[Config] = None,
item=None, nodeid: Optional[str] = None,
) -> List[str]: ) -> List[str]:
resolved_ids = [ 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) for valindex, parameterset in enumerate(parametersets)
] ]

View File

@ -19,6 +19,7 @@ from _pytest import python
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.pytester import Testdir from _pytest.pytester import Testdir
from _pytest.python import _idval from _pytest.python import _idval
from _pytest.python import idmaker
class TestMetafunc: class TestMetafunc:
@ -35,10 +36,11 @@ class TestMetafunc:
@attr.s @attr.s
class DefinitionMock(python.FunctionDefinition): class DefinitionMock(python.FunctionDefinition):
obj = attr.ib() obj = attr.ib()
_nodeid = attr.ib()
names = fixtures.getfuncargnames(func) names = fixtures.getfuncargnames(func)
fixtureinfo = FuncFixtureInfoMock(names) # type: Any 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) return python.Metafunc(definition, fixtureinfo, config)
def test_no_funcargs(self) -> None: def test_no_funcargs(self) -> None:
@ -270,7 +272,7 @@ class TestMetafunc:
deadline=400.0 deadline=400.0
) # very close to std deadline and CI boxes are not reliable in CPU power ) # very close to std deadline and CI boxes are not reliable in CPU power
def test_idval_hypothesis(self, value) -> None: 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) assert isinstance(escaped, str)
escaped.encode("ascii") escaped.encode("ascii")
@ -292,7 +294,7 @@ class TestMetafunc:
), ),
] ]
for val, expected in values: 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: def test_unicode_idval_with_config(self) -> None:
"""unittest for expected behavior to obtain ids with """unittest for expected behavior to obtain ids with
@ -321,7 +323,7 @@ class TestMetafunc:
("ação", MockConfig({option: False}), "a\\xe7\\xe3o"), ("ação", MockConfig({option: False}), "a\\xe7\\xe3o"),
] # type: List[Tuple[str, Any, str]] ] # type: List[Tuple[str, Any, str]]
for val, config, expected in values: 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 assert actual == expected
def test_bytes_idval(self) -> None: def test_bytes_idval(self) -> None:
@ -338,7 +340,7 @@ class TestMetafunc:
("αρά".encode(), "\\xce\\xb1\\xcf\\x81\\xce\\xac"), ("αρά".encode(), "\\xce\\xb1\\xcf\\x81\\xce\\xac"),
] ]
for val, expected in values: 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: def test_class_or_function_idval(self) -> None:
"""unittest for the expected behavior to obtain ids for parametrized """unittest for the expected behavior to obtain ids for parametrized
@ -353,12 +355,10 @@ class TestMetafunc:
values = [(TestClass, "TestClass"), (test_function, "test_function")] values = [(TestClass, "TestClass"), (test_function, "test_function")]
for val, expected in values: 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: def test_idmaker_autoname(self) -> None:
"""#250""" """#250"""
from _pytest.python import idmaker
result = idmaker( result = idmaker(
("a", "b"), [pytest.param("string", 1.0), pytest.param("st-ring", 2.0)] ("a", "b"), [pytest.param("string", 1.0), pytest.param("st-ring", 2.0)]
) )
@ -373,14 +373,10 @@ class TestMetafunc:
assert result == ["a0-\\xc3\\xb4"] assert result == ["a0-\\xc3\\xb4"]
def test_idmaker_with_bytes_regex(self) -> None: def test_idmaker_with_bytes_regex(self) -> None:
from _pytest.python import idmaker
result = idmaker(("a"), [pytest.param(re.compile(b"foo"), 1.0)]) result = idmaker(("a"), [pytest.param(re.compile(b"foo"), 1.0)])
assert result == ["foo"] assert result == ["foo"]
def test_idmaker_native_strings(self) -> None: def test_idmaker_native_strings(self) -> None:
from _pytest.python import idmaker
result = idmaker( result = idmaker(
("a", "b"), ("a", "b"),
[ [
@ -414,8 +410,6 @@ class TestMetafunc:
] ]
def test_idmaker_non_printable_characters(self) -> None: def test_idmaker_non_printable_characters(self) -> None:
from _pytest.python import idmaker
result = idmaker( result = idmaker(
("s", "n"), ("s", "n"),
[ [
@ -430,8 +424,6 @@ class TestMetafunc:
assert result == ["\\x00-1", "\\x05-2", "\\x00-3", "\\x05-4", "\\t-5", "\\t-6"] assert result == ["\\x00-1", "\\x05-2", "\\x00-3", "\\x05-4", "\\t-5", "\\t-6"]
def test_idmaker_manual_ids_must_be_printable(self) -> None: def test_idmaker_manual_ids_must_be_printable(self) -> None:
from _pytest.python import idmaker
result = idmaker( result = idmaker(
("s",), ("s",),
[ [
@ -442,8 +434,6 @@ class TestMetafunc:
assert result == ["hello \\x00", "hello \\x05"] assert result == ["hello \\x00", "hello \\x05"]
def test_idmaker_enum(self) -> None: def test_idmaker_enum(self) -> None:
from _pytest.python import idmaker
enum = pytest.importorskip("enum") enum = pytest.importorskip("enum")
e = enum.Enum("Foo", "one, two") e = enum.Enum("Foo", "one, two")
result = idmaker(("a", "b"), [pytest.param(e.one, e.two)]) result = idmaker(("a", "b"), [pytest.param(e.one, e.two)])
@ -451,7 +441,6 @@ class TestMetafunc:
def test_idmaker_idfn(self) -> None: def test_idmaker_idfn(self) -> None:
"""#351""" """#351"""
from _pytest.python import idmaker
def ids(val: object) -> Optional[str]: def ids(val: object) -> Optional[str]:
if isinstance(val, Exception): if isinstance(val, Exception):
@ -471,7 +460,6 @@ class TestMetafunc:
def test_idmaker_idfn_unique_names(self) -> None: def test_idmaker_idfn_unique_names(self) -> None:
"""#351""" """#351"""
from _pytest.python import idmaker
def ids(val: object) -> str: def ids(val: object) -> str:
return "a" return "a"
@ -492,7 +480,6 @@ class TestMetafunc:
disable_test_id_escaping_and_forfeit_all_rights_to_community_support disable_test_id_escaping_and_forfeit_all_rights_to_community_support
option. (#5294) option. (#5294)
""" """
from _pytest.python import idmaker
class MockConfig: class MockConfig:
def __init__(self, config): def __init__(self, config):
@ -525,7 +512,6 @@ class TestMetafunc:
disable_test_id_escaping_and_forfeit_all_rights_to_community_support disable_test_id_escaping_and_forfeit_all_rights_to_community_support
option. (#5294) option. (#5294)
""" """
from _pytest.python import idmaker
class MockConfig: class MockConfig:
def __init__(self, config): def __init__(self, config):
@ -607,16 +593,12 @@ class TestMetafunc:
) )
def test_idmaker_with_ids(self) -> None: def test_idmaker_with_ids(self) -> None:
from _pytest.python import idmaker
result = idmaker( result = idmaker(
("a", "b"), [pytest.param(1, 2), pytest.param(3, 4)], ids=["a", None] ("a", "b"), [pytest.param(1, 2), pytest.param(3, 4)], ids=["a", None]
) )
assert result == ["a", "3-4"] assert result == ["a", "3-4"]
def test_idmaker_with_paramset_id(self) -> None: def test_idmaker_with_paramset_id(self) -> None:
from _pytest.python import idmaker
result = idmaker( result = idmaker(
("a", "b"), ("a", "b"),
[pytest.param(1, 2, id="me"), pytest.param(3, 4, id="you")], [pytest.param(1, 2, id="me"), pytest.param(3, 4, id="you")],
@ -625,8 +607,6 @@ class TestMetafunc:
assert result == ["me", "you"] assert result == ["me", "you"]
def test_idmaker_with_ids_unique_names(self) -> None: def test_idmaker_with_ids_unique_names(self) -> None:
from _pytest.python import idmaker
result = idmaker( result = idmaker(
("a"), map(pytest.param, [1, 2, 3, 4, 5]), ids=["a", "a", "b", "c", "b"] ("a"), map(pytest.param, [1, 2, 3, 4, 5]), ids=["a", "a", "b", "c", "b"]
) )