@@ -1326,6 +1326,12 @@ def _get_direct_parametrize_args(node: nodes.Node) -> List[str]:
13261326 return parametrize_argnames
13271327
13281328
1329+ def deduplicate_names (seq : Iterable [str ]) -> Tuple [str , ...]:
1330+ """De-duplicate the sequence of names while keeping the original order."""
1331+ # Ideally we would use a set, but it does not preserve insertion order.
1332+ return tuple (dict .fromkeys (seq ))
1333+
1334+
13291335class FixtureManager :
13301336 """pytest fixture definitions and information is stored and managed
13311337 from this class.
@@ -1404,13 +1410,8 @@ def getfixtureinfo(
14041410 usefixtures = tuple (
14051411 arg for mark in node .iter_markers (name = "usefixtures" ) for arg in mark .args
14061412 )
1407- initialnames = cast (
1408- Tuple [str ],
1409- tuple (
1410- dict .fromkeys (
1411- tuple (self ._getautousenames (node .nodeid )) + usefixtures + argnames
1412- )
1413- ),
1413+ initialnames = deduplicate_names (
1414+ tuple (self ._getautousenames (node .nodeid )) + usefixtures + argnames
14141415 )
14151416
14161417 arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]] = {}
@@ -1459,23 +1460,19 @@ def _getautousenames(self, nodeid: str) -> Iterator[str]:
14591460 def getfixtureclosure (
14601461 self ,
14611462 parentnode : nodes .Node ,
1462- initialnames : Tuple [str ],
1463+ initialnames : Tuple [str , ... ],
14631464 arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]],
14641465 ignore_args : Sequence [str ] = (),
14651466 ) -> List [str ]:
14661467 # Collect the closure of all fixtures, starting with the given
1467- # initialnames as the initial set. As we have to visit all
1468- # factory definitions anyway, we also populate arg2fixturedefs
1469- # mapping so that the caller can reuse it and does not have
1470- # to re-discover fixturedefs again for each fixturename
1468+ # initialnames containing function arguments, `usefixture` markers
1469+ # and `autouse` fixtures as the initial set. As we have to visit all
1470+ # factory definitions anyway, we also populate arg2fixturedefs mapping
1471+ # for the args missing therein so that the caller can reuse it and does
1472+ # not have to re-discover fixturedefs again for each fixturename
14711473 # (discovering matching fixtures for a given name/node is expensive).
14721474
1473- fixturenames_closure = list (initialnames )
1474-
1475- def merge (otherlist : Iterable [str ]) -> None :
1476- for arg in otherlist :
1477- if arg not in fixturenames_closure :
1478- fixturenames_closure .append (arg )
1475+ fixturenames_closure = initialnames
14791476
14801477 lastlen = - 1
14811478 parentid = parentnode .nodeid
@@ -1489,7 +1486,9 @@ def merge(otherlist: Iterable[str]) -> None:
14891486 if fixturedefs :
14901487 arg2fixturedefs [argname ] = fixturedefs
14911488 if argname in arg2fixturedefs :
1492- merge (arg2fixturedefs [argname ][- 1 ].argnames )
1489+ fixturenames_closure = deduplicate_names (
1490+ fixturenames_closure + arg2fixturedefs [argname ][- 1 ].argnames
1491+ )
14931492
14941493 def sort_by_scope (arg_name : str ) -> Scope :
14951494 try :
@@ -1499,8 +1498,7 @@ def sort_by_scope(arg_name: str) -> Scope:
14991498 else :
15001499 return fixturedefs [- 1 ]._scope
15011500
1502- fixturenames_closure .sort (key = sort_by_scope , reverse = True )
1503- return fixturenames_closure
1501+ return sorted (fixturenames_closure , key = sort_by_scope , reverse = True )
15041502
15051503 def pytest_generate_tests (self , metafunc : "Metafunc" ) -> None :
15061504 """Generate new tests based on parametrized fixtures used by the given metafunc"""
0 commit comments