@@ -1404,6 +1404,12 @@ def _get_direct_parametrize_args(node: nodes.Node) -> List[str]:
14041404 return parametrize_argnames
14051405
14061406
1407+ def deduplicate_names (seq : Iterable [str ]) -> Tuple [str , ...]:
1408+ """De-duplicate the sequence of names while keeping the original order."""
1409+ # Ideally we would use a set, but it does not preserve insertion order.
1410+ return tuple (dict .fromkeys (seq ))
1411+
1412+
14071413class FixtureManager :
14081414 """pytest fixture definitions and information is stored and managed
14091415 from this class.
@@ -1482,13 +1488,8 @@ def getfixtureinfo(
14821488 usefixtures = tuple (
14831489 arg for mark in node .iter_markers (name = "usefixtures" ) for arg in mark .args
14841490 )
1485- initialnames = cast (
1486- Tuple [str ],
1487- tuple (
1488- dict .fromkeys (
1489- tuple (self ._getautousenames (node .nodeid )) + usefixtures + argnames
1490- )
1491- ),
1491+ initialnames = deduplicate_names (
1492+ tuple (self ._getautousenames (node .nodeid )) + usefixtures + argnames
14921493 )
14931494
14941495 arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]] = {}
@@ -1537,23 +1538,19 @@ def _getautousenames(self, nodeid: str) -> Iterator[str]:
15371538 def getfixtureclosure (
15381539 self ,
15391540 parentnode : nodes .Node ,
1540- initialnames : Tuple [str ],
1541+ initialnames : Tuple [str , ... ],
15411542 arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]],
15421543 ignore_args : Sequence [str ] = (),
15431544 ) -> List [str ]:
15441545 # Collect the closure of all fixtures, starting with the given
1545- # initialnames as the initial set. As we have to visit all
1546- # factory definitions anyway, we also populate arg2fixturedefs
1547- # mapping so that the caller can reuse it and does not have
1548- # to re-discover fixturedefs again for each fixturename
1546+ # initialnames containing function arguments, `usefixture` markers
1547+ # and `autouse` fixtures as the initial set. As we have to visit all
1548+ # factory definitions anyway, we also populate arg2fixturedefs mapping
1549+ # for the args missing therein so that the caller can reuse it and does
1550+ # not have to re-discover fixturedefs again for each fixturename
15491551 # (discovering matching fixtures for a given name/node is expensive).
15501552
1551- fixturenames_closure = list (initialnames )
1552-
1553- def merge (otherlist : Iterable [str ]) -> None :
1554- for arg in otherlist :
1555- if arg not in fixturenames_closure :
1556- fixturenames_closure .append (arg )
1553+ fixturenames_closure = initialnames
15571554
15581555 lastlen = - 1
15591556 parentid = parentnode .nodeid
@@ -1567,7 +1564,9 @@ def merge(otherlist: Iterable[str]) -> None:
15671564 if fixturedefs :
15681565 arg2fixturedefs [argname ] = fixturedefs
15691566 if argname in arg2fixturedefs :
1570- merge (arg2fixturedefs [argname ][- 1 ].argnames )
1567+ fixturenames_closure = deduplicate_names (
1568+ fixturenames_closure + arg2fixturedefs [argname ][- 1 ].argnames
1569+ )
15711570
15721571 def sort_by_scope (arg_name : str ) -> Scope :
15731572 try :
@@ -1577,8 +1576,7 @@ def sort_by_scope(arg_name: str) -> Scope:
15771576 else :
15781577 return fixturedefs [- 1 ]._scope
15791578
1580- fixturenames_closure .sort (key = sort_by_scope , reverse = True )
1581- return fixturenames_closure
1579+ return sorted (fixturenames_closure , key = sort_by_scope , reverse = True )
15821580
15831581 def pytest_generate_tests (self , metafunc : "Metafunc" ) -> None :
15841582 """Generate new tests based on parametrized fixtures used by the given metafunc"""
0 commit comments