@@ -383,33 +383,6 @@ class FuncFixtureInfo:
383383 # sequence is ordered from furthest to closes to the function.
384384 name2fixturedefs : Dict [str , Sequence ["FixtureDef[Any]" ]]
385385
386- def prune_dependency_tree (self ) -> None :
387- """Recompute names_closure from initialnames and name2fixturedefs.
388-
389- Can only reduce names_closure, which means that the new closure will
390- always be a subset of the old one. The order is preserved.
391-
392- This method is needed because direct parametrization may shadow some
393- of the fixtures that were included in the originally built dependency
394- tree. In this way the dependency tree can get pruned, and the closure
395- of argnames may get reduced.
396- """
397- closure : Set [str ] = set ()
398- working_set = set (self .initialnames )
399- while working_set :
400- argname = working_set .pop ()
401- # Argname may be smth not included in the original names_closure,
402- # in which case we ignore it. This currently happens with pseudo
403- # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'.
404- # So they introduce the new dependency 'request' which might have
405- # been missing in the original tree (closure).
406- if argname not in closure and argname in self .names_closure :
407- closure .add (argname )
408- if argname in self .name2fixturedefs :
409- working_set .update (self .name2fixturedefs [argname ][- 1 ].argnames )
410-
411- self .names_closure [:] = sorted (closure , key = self .names_closure .index )
412-
413386
414387class FixtureRequest :
415388 """A request for a fixture from a test or fixture function.
@@ -1404,6 +1377,26 @@ def pytest_addoption(parser: Parser) -> None:
14041377 )
14051378
14061379
1380+ def _get_direct_parametrize_args (node : nodes .Node ) -> List [str ]:
1381+ """Return all direct parametrization arguments of a node, so we don't
1382+ mistake them for fixtures.
1383+
1384+ Check https://github.com/pytest-dev/pytest/issues/5036.
1385+
1386+ These things are done later as well when dealing with parametrization
1387+ so this could be improved.
1388+ """
1389+ parametrize_argnames : List [str ] = []
1390+ for marker in node .iter_markers (name = "parametrize" ):
1391+ if not marker .kwargs .get ("indirect" , False ):
1392+ p_argnames , _ = ParameterSet ._parse_parametrize_args (
1393+ * marker .args , ** marker .kwargs
1394+ )
1395+ parametrize_argnames .extend (p_argnames )
1396+
1397+ return parametrize_argnames
1398+
1399+
14071400class FixtureManager :
14081401 """pytest fixture definitions and information is stored and managed
14091402 from this class.
@@ -1453,25 +1446,6 @@ def __init__(self, session: "Session") -> None:
14531446 }
14541447 session .config .pluginmanager .register (self , "funcmanage" )
14551448
1456- def _get_direct_parametrize_args (self , node : nodes .Node ) -> List [str ]:
1457- """Return all direct parametrization arguments of a node, so we don't
1458- mistake them for fixtures.
1459-
1460- Check https://github.com/pytest-dev/pytest/issues/5036.
1461-
1462- These things are done later as well when dealing with parametrization
1463- so this could be improved.
1464- """
1465- parametrize_argnames : List [str ] = []
1466- for marker in node .iter_markers (name = "parametrize" ):
1467- if not marker .kwargs .get ("indirect" , False ):
1468- p_argnames , _ = ParameterSet ._parse_parametrize_args (
1469- * marker .args , ** marker .kwargs
1470- )
1471- parametrize_argnames .extend (p_argnames )
1472-
1473- return parametrize_argnames
1474-
14751449 def getfixtureinfo (
14761450 self ,
14771451 node : nodes .Item ,
@@ -1501,11 +1475,28 @@ def getfixtureinfo(
15011475 usefixtures = tuple (
15021476 arg for mark in node .iter_markers (name = "usefixtures" ) for arg in mark .args
15031477 )
1504- initialnames = usefixtures + argnames
1505- initialnames , names_closure , arg2fixturedefs = self .getfixtureclosure (
1506- initialnames , node , ignore_args = self ._get_direct_parametrize_args (node )
1478+ initialnames = cast (
1479+ Tuple [str ],
1480+ tuple (
1481+ dict .fromkeys (
1482+ tuple (self ._getautousenames (node .nodeid )) + usefixtures + argnames
1483+ )
1484+ ),
1485+ )
1486+
1487+ arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]] = {}
1488+ names_closure = self .getfixtureclosure (
1489+ node ,
1490+ initialnames ,
1491+ arg2fixturedefs ,
1492+ ignore_args = _get_direct_parametrize_args (node ),
1493+ )
1494+ return FuncFixtureInfo (
1495+ argnames ,
1496+ initialnames ,
1497+ names_closure ,
1498+ arg2fixturedefs ,
15071499 )
1508- return FuncFixtureInfo (argnames , initialnames , names_closure , arg2fixturedefs )
15091500
15101501 def pytest_plugin_registered (self , plugin : _PluggyPlugin ) -> None :
15111502 nodeid = None
@@ -1538,45 +1529,38 @@ def _getautousenames(self, nodeid: str) -> Iterator[str]:
15381529
15391530 def getfixtureclosure (
15401531 self ,
1541- fixturenames : Tuple [str , ...],
15421532 parentnode : nodes .Node ,
1533+ initialnames : Tuple [str ],
1534+ arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]],
15431535 ignore_args : Sequence [str ] = (),
1544- ) -> Tuple [ Tuple [ str , ...], List [str ], Dict [ str , Sequence [ FixtureDef [ Any ]]] ]:
1536+ ) -> List [str ]:
15451537 # Collect the closure of all fixtures, starting with the given
1546- # fixturenames as the initial set. As we have to visit all
1547- # factory definitions anyway, we also return an arg2fixturedefs
1538+ # initialnames as the initial set. As we have to visit all
1539+ # factory definitions anyway, we also populate arg2fixturedefs
15481540 # mapping so that the caller can reuse it and does not have
15491541 # to re-discover fixturedefs again for each fixturename
15501542 # (discovering matching fixtures for a given name/node is expensive).
15511543
1552- parentid = parentnode .nodeid
1553- fixturenames_closure = list (self ._getautousenames (parentid ))
1544+ fixturenames_closure = list (initialnames )
15541545
15551546 def merge (otherlist : Iterable [str ]) -> None :
15561547 for arg in otherlist :
15571548 if arg not in fixturenames_closure :
15581549 fixturenames_closure .append (arg )
15591550
1560- merge (fixturenames )
1561-
1562- # At this point, fixturenames_closure contains what we call "initialnames",
1563- # which is a set of fixturenames the function immediately requests. We
1564- # need to return it as well, so save this.
1565- initialnames = tuple (fixturenames_closure )
1566-
1567- arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]] = {}
15681551 lastlen = - 1
1552+ parentid = parentnode .nodeid
15691553 while lastlen != len (fixturenames_closure ):
15701554 lastlen = len (fixturenames_closure )
15711555 for argname in fixturenames_closure :
15721556 if argname in ignore_args :
15731557 continue
1558+ if argname not in arg2fixturedefs :
1559+ fixturedefs = self .getfixturedefs (argname , parentid )
1560+ if fixturedefs :
1561+ arg2fixturedefs [argname ] = fixturedefs
15741562 if argname in arg2fixturedefs :
1575- continue
1576- fixturedefs = self .getfixturedefs (argname , parentid )
1577- if fixturedefs :
1578- arg2fixturedefs [argname ] = fixturedefs
1579- merge (fixturedefs [- 1 ].argnames )
1563+ merge (arg2fixturedefs [argname ][- 1 ].argnames )
15801564
15811565 def sort_by_scope (arg_name : str ) -> Scope :
15821566 try :
@@ -1587,7 +1571,7 @@ def sort_by_scope(arg_name: str) -> Scope:
15871571 return fixturedefs [- 1 ]._scope
15881572
15891573 fixturenames_closure .sort (key = sort_by_scope , reverse = True )
1590- return initialnames , fixturenames_closure , arg2fixturedefs
1574+ return fixturenames_closure
15911575
15921576 def pytest_generate_tests (self , metafunc : "Metafunc" ) -> None :
15931577 """Generate new tests based on parametrized fixtures used by the given metafunc"""
0 commit comments