@@ -1402,6 +1402,12 @@ def _get_direct_parametrize_args(node: nodes.Node) -> Set[str]:
14021402 return parametrize_argnames
14031403
14041404
1405+ def deduplicate_names (* seqs : Iterable [str ]) -> Tuple [str , ...]:
1406+ """De-duplicate the sequence of names while keeping the original order."""
1407+ # Ideally we would use a set, but it does not preserve insertion order.
1408+ return tuple (dict .fromkeys (name for seq in seqs for name in seq ))
1409+
1410+
14051411class FixtureManager :
14061412 """pytest fixture definitions and information is stored and managed
14071413 from this class.
@@ -1476,14 +1482,18 @@ def getfixtureinfo(
14761482 argnames = getfuncargnames (func , name = node .name , cls = cls )
14771483 else :
14781484 argnames = ()
1485+ usefixturesnames = self ._getusefixturesnames (node )
1486+ autousenames = self ._getautousenames (node .nodeid )
1487+ initialnames = deduplicate_names (autousenames , usefixturesnames , argnames )
14791488
1480- usefixtures = tuple (
1481- arg for mark in node . iter_markers ( name = "usefixtures" ) for arg in mark . args
1482- )
1483- initialnames = usefixtures + argnames
1484- initialnames , names_closure , arg2fixturedefs = self . getfixtureclosure (
1485- initialnames , node , ignore_args = _get_direct_parametrize_args ( node )
1489+ direct_parametrize_args = _get_direct_parametrize_args ( node )
1490+
1491+ names_closure , arg2fixturedefs = self . getfixtureclosure (
1492+ parentnode = node ,
1493+ initialnames = initialnames ,
1494+ ignore_args = direct_parametrize_args ,
14861495 )
1496+
14871497 return FuncFixtureInfo (argnames , initialnames , names_closure , arg2fixturedefs )
14881498
14891499 def pytest_plugin_registered (self , plugin : _PluggyPlugin ) -> None :
@@ -1515,12 +1525,17 @@ def _getautousenames(self, nodeid: str) -> Iterator[str]:
15151525 if basenames :
15161526 yield from basenames
15171527
1528+ def _getusefixturesnames (self , node : nodes .Item ) -> Iterator [str ]:
1529+ """Return the names of usefixtures fixtures applicable to node."""
1530+ for mark in node .iter_markers (name = "usefixtures" ):
1531+ yield from mark .args
1532+
15181533 def getfixtureclosure (
15191534 self ,
1520- fixturenames : Tuple [str , ...],
15211535 parentnode : nodes .Node ,
1536+ initialnames : Tuple [str , ...],
15221537 ignore_args : AbstractSet [str ],
1523- ) -> Tuple [Tuple [ str , ...], List [str ], Dict [str , Sequence [FixtureDef [Any ]]]]:
1538+ ) -> Tuple [List [str ], Dict [str , Sequence [FixtureDef [Any ]]]]:
15241539 # Collect the closure of all fixtures, starting with the given
15251540 # fixturenames as the initial set. As we have to visit all
15261541 # factory definitions anyway, we also return an arg2fixturedefs
@@ -1529,19 +1544,7 @@ def getfixtureclosure(
15291544 # (discovering matching fixtures for a given name/node is expensive).
15301545
15311546 parentid = parentnode .nodeid
1532- fixturenames_closure = list (self ._getautousenames (parentid ))
1533-
1534- def merge (otherlist : Iterable [str ]) -> None :
1535- for arg in otherlist :
1536- if arg not in fixturenames_closure :
1537- fixturenames_closure .append (arg )
1538-
1539- merge (fixturenames )
1540-
1541- # At this point, fixturenames_closure contains what we call "initialnames",
1542- # which is a set of fixturenames the function immediately requests. We
1543- # need to return it as well, so save this.
1544- initialnames = tuple (fixturenames_closure )
1547+ fixturenames_closure = list (initialnames )
15451548
15461549 arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]] = {}
15471550 lastlen = - 1
@@ -1555,7 +1558,9 @@ def merge(otherlist: Iterable[str]) -> None:
15551558 fixturedefs = self .getfixturedefs (argname , parentid )
15561559 if fixturedefs :
15571560 arg2fixturedefs [argname ] = fixturedefs
1558- merge (fixturedefs [- 1 ].argnames )
1561+ for arg in fixturedefs [- 1 ].argnames :
1562+ if arg not in fixturenames_closure :
1563+ fixturenames_closure .append (arg )
15591564
15601565 def sort_by_scope (arg_name : str ) -> Scope :
15611566 try :
@@ -1566,7 +1571,7 @@ def sort_by_scope(arg_name: str) -> Scope:
15661571 return fixturedefs [- 1 ]._scope
15671572
15681573 fixturenames_closure .sort (key = sort_by_scope , reverse = True )
1569- return initialnames , fixturenames_closure , arg2fixturedefs
1574+ return fixturenames_closure , arg2fixturedefs
15701575
15711576 def pytest_generate_tests (self , metafunc : "Metafunc" ) -> None :
15721577 """Generate new tests based on parametrized fixtures used by the given metafunc"""
0 commit comments