@@ -1404,6 +1404,12 @@ def _get_direct_parametrize_args(node: nodes.Node) -> List[str]:
1404
1404
return parametrize_argnames
1405
1405
1406
1406
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
+
1407
1413
class FixtureManager :
1408
1414
"""pytest fixture definitions and information is stored and managed
1409
1415
from this class.
@@ -1482,13 +1488,8 @@ def getfixtureinfo(
1482
1488
usefixtures = tuple (
1483
1489
arg for mark in node .iter_markers (name = "usefixtures" ) for arg in mark .args
1484
1490
)
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
1492
1493
)
1493
1494
1494
1495
arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]] = {}
@@ -1537,23 +1538,19 @@ def _getautousenames(self, nodeid: str) -> Iterator[str]:
1537
1538
def getfixtureclosure (
1538
1539
self ,
1539
1540
parentnode : nodes .Node ,
1540
- initialnames : Tuple [str ],
1541
+ initialnames : Tuple [str , ... ],
1541
1542
arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]],
1542
1543
ignore_args : Sequence [str ] = (),
1543
1544
) -> List [str ]:
1544
1545
# 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
1549
1551
# (discovering matching fixtures for a given name/node is expensive).
1550
1552
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
1557
1554
1558
1555
lastlen = - 1
1559
1556
parentid = parentnode .nodeid
@@ -1567,7 +1564,9 @@ def merge(otherlist: Iterable[str]) -> None:
1567
1564
if fixturedefs :
1568
1565
arg2fixturedefs [argname ] = fixturedefs
1569
1566
if argname in arg2fixturedefs :
1570
- merge (arg2fixturedefs [argname ][- 1 ].argnames )
1567
+ fixturenames_closure = deduplicate_names (
1568
+ fixturenames_closure + arg2fixturedefs [argname ][- 1 ].argnames
1569
+ )
1571
1570
1572
1571
def sort_by_scope (arg_name : str ) -> Scope :
1573
1572
try :
@@ -1577,8 +1576,7 @@ def sort_by_scope(arg_name: str) -> Scope:
1577
1576
else :
1578
1577
return fixturedefs [- 1 ]._scope
1579
1578
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 )
1582
1580
1583
1581
def pytest_generate_tests (self , metafunc : "Metafunc" ) -> None :
1584
1582
"""Generate new tests based on parametrized fixtures used by the given metafunc"""
0 commit comments