@@ -348,7 +348,6 @@ def __init__(
348
348
pyfuncitem : "Function" ,
349
349
fixturename : Optional [str ],
350
350
arg2fixturedefs : Dict [str , Sequence ["FixtureDef[Any]" ]],
351
- arg2index : Dict [str , int ],
352
351
fixture_defs : Dict [str , "FixtureDef[Any]" ],
353
352
* ,
354
353
_ispytest : bool = False ,
@@ -362,16 +361,6 @@ def __init__(
362
361
# collection. Dynamically requested fixtures (using
363
362
# `request.getfixturevalue("foo")`) are added dynamically.
364
363
self ._arg2fixturedefs : Final = arg2fixturedefs
365
- # A fixture may override another fixture with the same name, e.g. a fixture
366
- # in a module can override a fixture in a conftest, a fixture in a class can
367
- # override a fixture in the module, and so on.
368
- # An overriding fixture can request its own name; in this case it gets
369
- # the value of the fixture it overrides, one level up.
370
- # The _arg2index state keeps the current depth in the overriding chain.
371
- # The fixturedefs list in _arg2fixturedefs for a given name is ordered from
372
- # furthest to closest, so we use negative indexing -1, -2, ... to go from
373
- # last to first.
374
- self ._arg2index : Final = arg2index
375
364
# The evaluated argnames so far, mapping to the FixtureDef they resolved
376
365
# to.
377
366
self ._fixture_defs : Final = fixture_defs
@@ -427,11 +416,24 @@ def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]":
427
416
# The are no fixtures with this name applicable for the function.
428
417
if not fixturedefs :
429
418
raise FixtureLookupError (argname , self )
430
- index = self ._arg2index .get (argname , 0 ) - 1
431
- # The fixture requested its own name, but no remaining to override.
419
+
420
+ # A fixture may override another fixture with the same name, e.g. a
421
+ # fixture in a module can override a fixture in a conftest, a fixture in
422
+ # a class can override a fixture in the module, and so on.
423
+ # An overriding fixture can request its own name (possibly indirectly);
424
+ # in this case it gets the value of the fixture it overrides, one level
425
+ # up.
426
+ # Check how many `argname`s deep we are, and take the next one.
427
+ # `fixturedefs` is sorted from furthest to closest, so use negative
428
+ # indexing to go in reverse.
429
+ index = - 1
430
+ for request in self ._iter_chain ():
431
+ if request .fixturename == argname :
432
+ index -= 1
433
+ # If already consumed all of the available levels, fail.
432
434
if - index > len (fixturedefs ):
433
435
raise FixtureLookupError (argname , self )
434
- self . _arg2index [ argname ] = index
436
+
435
437
return fixturedefs [index ]
436
438
437
439
@property
@@ -543,6 +545,16 @@ def getfixturevalue(self, argname: str) -> Any:
543
545
)
544
546
return fixturedef .cached_result [0 ]
545
547
548
+ def _iter_chain (self ) -> Iterator ["SubRequest" ]:
549
+ """Yield all SubRequests in the chain, from self up.
550
+
551
+ Note: does *not* yield the TopRequest.
552
+ """
553
+ current = self
554
+ while isinstance (current , SubRequest ):
555
+ yield current
556
+ current = current ._parent_request
557
+
546
558
def _get_active_fixturedef (
547
559
self , argname : str
548
560
) -> Union ["FixtureDef[object]" , PseudoFixtureDef [object ]]:
@@ -560,11 +572,7 @@ def _get_active_fixturedef(
560
572
return fixturedef
561
573
562
574
def _get_fixturestack (self ) -> List ["FixtureDef[Any]" ]:
563
- current = self
564
- values : List [FixtureDef [Any ]] = []
565
- while isinstance (current , SubRequest ):
566
- values .append (current ._fixturedef ) # type: ignore[has-type]
567
- current = current ._parent_request
575
+ values = [request ._fixturedef for request in self ._iter_chain ()]
568
576
values .reverse ()
569
577
return values
570
578
@@ -657,7 +665,6 @@ def __init__(self, pyfuncitem: "Function", *, _ispytest: bool = False) -> None:
657
665
fixturename = None ,
658
666
pyfuncitem = pyfuncitem ,
659
667
arg2fixturedefs = pyfuncitem ._fixtureinfo .name2fixturedefs .copy (),
660
- arg2index = {},
661
668
fixture_defs = {},
662
669
_ispytest = _ispytest ,
663
670
)
@@ -703,12 +710,11 @@ def __init__(
703
710
fixturename = fixturedef .argname ,
704
711
fixture_defs = request ._fixture_defs ,
705
712
arg2fixturedefs = request ._arg2fixturedefs ,
706
- arg2index = request ._arg2index ,
707
713
_ispytest = _ispytest ,
708
714
)
709
715
self ._parent_request : Final [FixtureRequest ] = request
710
716
self ._scope_field : Final = scope
711
- self ._fixturedef : Final = fixturedef
717
+ self ._fixturedef : Final [ FixtureDef [ object ]] = fixturedef
712
718
if param is not NOTSET :
713
719
self .param = param
714
720
self .param_index : Final = param_index
0 commit comments