-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Remove prune_dependency_tree
and reuse getfixtureclosure
logic
#11243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
aa5d09d
bbf5949
80b4f8b
0b725e7
fc92f9f
08d8b71
0c163ec
8b75507
c8e5277
57ad1e8
120be26
5e02d9c
c72507c
7c56b16
20ea215
1491a6e
946c32b
c962339
95f53e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -314,33 +314,6 @@ class FuncFixtureInfo: | |
# sequence is ordered from furthest to closes to the function. | ||
name2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]] | ||
|
||
def prune_dependency_tree(self) -> None: | ||
"""Recompute names_closure from initialnames and name2fixturedefs. | ||
|
||
Can only reduce names_closure, which means that the new closure will | ||
always be a subset of the old one. The order is preserved. | ||
|
||
This method is needed because direct parametrization may shadow some | ||
of the fixtures that were included in the originally built dependency | ||
tree. In this way the dependency tree can get pruned, and the closure | ||
of argnames may get reduced. | ||
""" | ||
closure: Set[str] = set() | ||
working_set = set(self.initialnames) | ||
while working_set: | ||
argname = working_set.pop() | ||
# Argname may be smth not included in the original names_closure, | ||
# in which case we ignore it. This currently happens with pseudo | ||
# FixtureDefs which wrap 'get_direct_param_fixture_func(request)'. | ||
# So they introduce the new dependency 'request' which might have | ||
# been missing in the original tree (closure). | ||
if argname not in closure and argname in self.names_closure: | ||
closure.add(argname) | ||
if argname in self.name2fixturedefs: | ||
working_set.update(self.name2fixturedefs[argname][-1].argnames) | ||
|
||
self.names_closure[:] = sorted(closure, key=self.names_closure.index) | ||
|
||
|
||
class FixtureRequest(abc.ABC): | ||
"""The type of the ``request`` fixture. | ||
|
@@ -961,7 +934,6 @@ def _eval_scope_callable( | |
return result | ||
|
||
|
||
@final | ||
class FixtureDef(Generic[FixtureValue]): | ||
"""A container for a fixture definition. | ||
|
||
|
@@ -1104,6 +1076,26 @@ def __repr__(self) -> str: | |
) | ||
|
||
|
||
class IdentityFixtureDef(FixtureDef[FixtureValue]): | ||
def __init__( | ||
self, | ||
fixturemanager: "FixtureManager", | ||
argname: str, | ||
scope: Union[Scope, _ScopeName, Callable[[str, Config], _ScopeName], None], | ||
*, | ||
_ispytest: bool = False, | ||
): | ||
super().__init__( | ||
fixturemanager, | ||
"", | ||
argname, | ||
lambda request: request.param, | ||
scope, | ||
None, | ||
_ispytest=_ispytest, | ||
) | ||
|
||
|
||
def resolve_fixture_function( | ||
fixturedef: FixtureDef[FixtureValue], request: FixtureRequest | ||
) -> "_FixtureFunc[FixtureValue]": | ||
|
@@ -1486,10 +1478,11 @@ def getfixtureinfo( | |
initialnames = deduplicate_names(autousenames, usefixturesnames, argnames) | ||
|
||
direct_parametrize_args = _get_direct_parametrize_args(node) | ||
|
||
names_closure, arg2fixturedefs = self.getfixtureclosure( | ||
arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} | ||
names_closure = self.getfixtureclosure( | ||
parentnode=node, | ||
initialnames=initialnames, | ||
arg2fixturedefs=arg2fixturedefs, | ||
ignore_args=direct_parametrize_args, | ||
) | ||
|
||
|
@@ -1533,30 +1526,33 @@ def getfixtureclosure( | |
self, | ||
parentnode: nodes.Node, | ||
initialnames: Tuple[str, ...], | ||
arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]], | ||
ignore_args: AbstractSet[str], | ||
) -> Tuple[List[str], Dict[str, Sequence[FixtureDef[Any]]]]: | ||
) -> List[str]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer to keep returning a brand new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the second fixture closure computation needs # ........ we also populate arg2fixturedefs mapping
# for the args missing therein so that the caller can reuse it and does
# not have to re-discover fixturedefs again for each fixturename
# (discovering matching fixtures for a given name/node is expensive). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about this? def getfixtureclosure(
self,
parentnode: nodes.Node,
initialnames: Tuple[str, ...],
arg2fixturedefs: Union[Dict[str, Sequence[FixtureDef[Any]]], None],
ignore_args: Sequence[str] = (),
) -> Tuple[List[str], Dict[str, Sequence[FixtureDef[Any]]]]: If |
||
# Collect the closure of all fixtures, starting with the given | ||
# fixturenames as the initial set. As we have to visit all | ||
# factory definitions anyway, we also return an arg2fixturedefs | ||
# mapping so that the caller can reuse it and does not have | ||
# to re-discover fixturedefs again for each fixturename | ||
# initialnames containing function arguments, `usefixture` markers | ||
# and `autouse` fixtures as the initial set. As we have to visit all | ||
# factory definitions anyway, we also populate arg2fixturedefs mapping | ||
# for the args missing therein so that the caller can reuse it and does | ||
# not have to re-discover fixturedefs again for each fixturename | ||
# (discovering matching fixtures for a given name/node is expensive). | ||
|
||
parentid = parentnode.nodeid | ||
fixturenames_closure = list(initialnames) | ||
|
||
arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {} | ||
lastlen = -1 | ||
while lastlen != len(fixturenames_closure): | ||
lastlen = len(fixturenames_closure) | ||
for argname in fixturenames_closure: | ||
if argname in ignore_args: | ||
continue | ||
if argname in arg2fixturedefs: | ||
continue | ||
fixturedefs = self.getfixturedefs(argname, parentid) | ||
if fixturedefs: | ||
arg2fixturedefs[argname] = fixturedefs | ||
if argname not in arg2fixturedefs: | ||
fixturedefs = self.getfixturedefs(argname, parentid) | ||
if fixturedefs: | ||
arg2fixturedefs[argname] = fixturedefs | ||
else: | ||
fixturedefs = arg2fixturedefs[argname] | ||
if fixturedefs and not isinstance(fixturedefs[-1], IdentityFixtureDef): | ||
for arg in fixturedefs[-1].argnames: | ||
if arg not in fixturenames_closure: | ||
fixturenames_closure.append(arg) | ||
|
@@ -1570,7 +1566,7 @@ def sort_by_scope(arg_name: str) -> Scope: | |
return fixturedefs[-1]._scope | ||
|
||
fixturenames_closure.sort(key=sort_by_scope, reverse=True) | ||
return fixturenames_closure, arg2fixturedefs | ||
return fixturenames_closure | ||
|
||
def pytest_generate_tests(self, metafunc: "Metafunc") -> None: | ||
"""Generate new tests based on parametrized fixtures used by the given metafunc""" | ||
|
Uh oh!
There was an error while loading. Please reload this page.