diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index e6f666dda..c3dbf84c2 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -579,9 +579,11 @@ def _setup_fixtures(doctest_item: DoctestItem) -> TopRequest: doctest_item.funcargs = {} # type: ignore[attr-defined] fm = doctest_item.session._fixturemanager - doctest_item._fixtureinfo = fm.getfixtureinfo( # type: ignore[attr-defined] + fixtureinfo = fm.getfixtureinfo( node=doctest_item, func=func, cls=None, funcargs=False ) + doctest_item._fixtureinfo = fixtureinfo # type: ignore[attr-defined] + doctest_item.fixturenames = fixtureinfo.names_closure # type: ignore[attr-defined] fixture_request = TopRequest(doctest_item, _ispytest=True) # type: ignore[arg-type] fixture_request._fillfixtures() return fixture_request diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index c7fc28adb..e692c9489 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -210,16 +210,14 @@ def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[FixtureArgKey, None]]] = {} items_by_argkey: Dict[Scope, Dict[FixtureArgKey, Deque[nodes.Item]]] = {} for scope in HIGH_SCOPES: - d: Dict[nodes.Item, Dict[FixtureArgKey, None]] = {} - argkeys_cache[scope] = d - item_d: Dict[FixtureArgKey, Deque[nodes.Item]] = defaultdict(deque) - items_by_argkey[scope] = item_d + scoped_argkeys_cache = argkeys_cache[scope] = {} + scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(deque) for item in items: keys = dict.fromkeys(get_parametrized_fixture_keys(item, scope), None) if keys: - d[item] = keys + scoped_argkeys_cache[item] = keys for key in keys: - item_d[key].append(item) + scoped_items_by_argkey[key].append(item) items_dict = dict.fromkeys(items, None) return list( reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, Scope.Session) @@ -407,7 +405,7 @@ class FixtureRequest(abc.ABC): @property def fixturenames(self) -> List[str]: """Names of all active fixtures in this request.""" - result = list(self._pyfuncitem._fixtureinfo.names_closure) + result = list(self._pyfuncitem.fixturenames) result.extend(set(self._fixture_defs).difference(result)) return result @@ -687,8 +685,7 @@ class TopRequest(FixtureRequest): def _fillfixtures(self) -> None: item = self._pyfuncitem - fixturenames = getattr(item, "fixturenames", self.fixturenames) - for argname in fixturenames: + for argname in item.fixturenames: if argname not in item.funcargs: item.funcargs[argname] = self.getfixturevalue(argname) @@ -794,7 +791,10 @@ class SubRequest(FixtureRequest): # If the executing fixturedef was not explicitly requested in the argument list (via # getfixturevalue inside the fixture call) then ensure this fixture def will be finished # first. - if fixturedef.argname not in self.fixturenames: + if ( + fixturedef.argname not in self._fixture_defs + and fixturedef.argname not in self._pyfuncitem.fixturenames + ): fixturedef.addfinalizer( functools.partial(self._fixturedef.finish, request=self) ) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index df1eb854d..e8d55c929 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -473,7 +473,9 @@ class PyCollector(PyobjMixin, nodes.Collector): clscol = self.getparent(Class) cls = clscol and clscol.obj or None - definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj) + definition: FunctionDefinition = FunctionDefinition.from_parent( + self, name=name, callobj=funcobj + ) fixtureinfo = definition._fixtureinfo # pytest_generate_tests impls call metafunc.parametrize() which fills @@ -1123,9 +1125,9 @@ class CallSpec2: # arg name -> arg index. indices: Dict[str, int] = dataclasses.field(default_factory=dict) # Used for sorting parametrized resources. - _arg2scope: Dict[str, Scope] = dataclasses.field(default_factory=dict) + _arg2scope: Mapping[str, Scope] = dataclasses.field(default_factory=dict) # Parts which will be added to the item's name in `[..]` separated by "-". - _idlist: List[str] = dataclasses.field(default_factory=list) + _idlist: Sequence[str] = dataclasses.field(default_factory=tuple) # Marks which will be applied to the item. marks: List[Mark] = dataclasses.field(default_factory=list) @@ -1141,7 +1143,7 @@ class CallSpec2: ) -> "CallSpec2": params = self.params.copy() indices = self.indices.copy() - arg2scope = self._arg2scope.copy() + arg2scope = dict(self._arg2scope) for arg, val in zip(argnames, valset): if arg in params: raise ValueError(f"duplicate parametrization of {arg!r}")