@@ -348,7 +348,6 @@ def __init__(
348348 pyfuncitem : "Function" ,
349349 fixturename : Optional [str ],
350350 arg2fixturedefs : Dict [str , Sequence ["FixtureDef[Any]" ]],
351- arg2index : Dict [str , int ],
352351 fixture_defs : Dict [str , "FixtureDef[Any]" ],
353352 * ,
354353 _ispytest : bool = False ,
@@ -362,16 +361,6 @@ def __init__(
362361 # collection. Dynamically requested fixtures (using
363362 # `request.getfixturevalue("foo")`) are added dynamically.
364363 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
375364 # The evaluated argnames so far, mapping to the FixtureDef they resolved
376365 # to.
377366 self ._fixture_defs : Final = fixture_defs
@@ -427,11 +416,24 @@ def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]":
427416 # The are no fixtures with this name applicable for the function.
428417 if not fixturedefs :
429418 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.
432434 if - index > len (fixturedefs ):
433435 raise FixtureLookupError (argname , self )
434- self . _arg2index [ argname ] = index
436+
435437 return fixturedefs [index ]
436438
437439 @property
@@ -543,6 +545,16 @@ def getfixturevalue(self, argname: str) -> Any:
543545 )
544546 return fixturedef .cached_result [0 ]
545547
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+
546558 def _get_active_fixturedef (
547559 self , argname : str
548560 ) -> Union ["FixtureDef[object]" , PseudoFixtureDef [object ]]:
@@ -560,11 +572,7 @@ def _get_active_fixturedef(
560572 return fixturedef
561573
562574 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 ()]
568576 values .reverse ()
569577 return values
570578
@@ -657,7 +665,6 @@ def __init__(self, pyfuncitem: "Function", *, _ispytest: bool = False) -> None:
657665 fixturename = None ,
658666 pyfuncitem = pyfuncitem ,
659667 arg2fixturedefs = pyfuncitem ._fixtureinfo .name2fixturedefs .copy (),
660- arg2index = {},
661668 fixture_defs = {},
662669 _ispytest = _ispytest ,
663670 )
@@ -703,12 +710,11 @@ def __init__(
703710 fixturename = fixturedef .argname ,
704711 fixture_defs = request ._fixture_defs ,
705712 arg2fixturedefs = request ._arg2fixturedefs ,
706- arg2index = request ._arg2index ,
707713 _ispytest = _ispytest ,
708714 )
709715 self ._parent_request : Final [FixtureRequest ] = request
710716 self ._scope_field : Final = scope
711- self ._fixturedef : Final = fixturedef
717+ self ._fixturedef : Final [ FixtureDef [ object ]] = fixturedef
712718 if param is not NOTSET :
713719 self .param = param
714720 self .param_index : Final = param_index
0 commit comments