@@ -1402,6 +1402,12 @@ def _get_direct_parametrize_args(node: nodes.Node) -> Set[str]:
1402
1402
return parametrize_argnames
1403
1403
1404
1404
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
+
1405
1411
class FixtureManager :
1406
1412
"""pytest fixture definitions and information is stored and managed
1407
1413
from this class.
@@ -1476,14 +1482,18 @@ def getfixtureinfo(
1476
1482
argnames = getfuncargnames (func , name = node .name , cls = cls )
1477
1483
else :
1478
1484
argnames = ()
1485
+ usefixturesnames = self ._getusefixturesnames (node )
1486
+ autousenames = self ._getautousenames (node .nodeid )
1487
+ initialnames = deduplicate_names (autousenames , usefixturesnames , argnames )
1479
1488
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 ,
1486
1495
)
1496
+
1487
1497
return FuncFixtureInfo (argnames , initialnames , names_closure , arg2fixturedefs )
1488
1498
1489
1499
def pytest_plugin_registered (self , plugin : _PluggyPlugin ) -> None :
@@ -1515,12 +1525,17 @@ def _getautousenames(self, nodeid: str) -> Iterator[str]:
1515
1525
if basenames :
1516
1526
yield from basenames
1517
1527
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
+
1518
1533
def getfixtureclosure (
1519
1534
self ,
1520
- fixturenames : Tuple [str , ...],
1521
1535
parentnode : nodes .Node ,
1536
+ initialnames : Tuple [str , ...],
1522
1537
ignore_args : AbstractSet [str ],
1523
- ) -> Tuple [Tuple [ str , ...], List [str ], Dict [str , Sequence [FixtureDef [Any ]]]]:
1538
+ ) -> Tuple [List [str ], Dict [str , Sequence [FixtureDef [Any ]]]]:
1524
1539
# Collect the closure of all fixtures, starting with the given
1525
1540
# fixturenames as the initial set. As we have to visit all
1526
1541
# factory definitions anyway, we also return an arg2fixturedefs
@@ -1529,19 +1544,7 @@ def getfixtureclosure(
1529
1544
# (discovering matching fixtures for a given name/node is expensive).
1530
1545
1531
1546
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 )
1545
1548
1546
1549
arg2fixturedefs : Dict [str , Sequence [FixtureDef [Any ]]] = {}
1547
1550
lastlen = - 1
@@ -1555,7 +1558,9 @@ def merge(otherlist: Iterable[str]) -> None:
1555
1558
fixturedefs = self .getfixturedefs (argname , parentid )
1556
1559
if fixturedefs :
1557
1560
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 )
1559
1564
1560
1565
def sort_by_scope (arg_name : str ) -> Scope :
1561
1566
try :
@@ -1566,7 +1571,7 @@ def sort_by_scope(arg_name: str) -> Scope:
1566
1571
return fixturedefs [- 1 ]._scope
1567
1572
1568
1573
fixturenames_closure .sort (key = sort_by_scope , reverse = True )
1569
- return initialnames , fixturenames_closure , arg2fixturedefs
1574
+ return fixturenames_closure , arg2fixturedefs
1570
1575
1571
1576
def pytest_generate_tests (self , metafunc : "Metafunc" ) -> None :
1572
1577
"""Generate new tests based on parametrized fixtures used by the given metafunc"""
0 commit comments