3535import _pytest
3636from _pytest import nodes
3737from _pytest ._code import getfslineno
38+ from _pytest ._code import Source
3839from _pytest ._code .code import FormattedExcinfo
3940from _pytest ._code .code import TerminalRepr
4041from _pytest ._io import TerminalWriter
@@ -410,41 +411,6 @@ def node(self):
410411 """Underlying collection node (depends on current request scope)."""
411412 raise NotImplementedError ()
412413
413- def _getnextfixturedef (self , argname : str ) -> "FixtureDef[Any]" :
414- fixturedefs = self ._arg2fixturedefs .get (argname , None )
415- if fixturedefs is None :
416- # We arrive here because of a dynamic call to
417- # getfixturevalue(argname) usage which was naturally
418- # not known at parsing/collection time.
419- fixturedefs = self ._fixturemanager .getfixturedefs (argname , self ._pyfuncitem )
420- if fixturedefs is not None :
421- self ._arg2fixturedefs [argname ] = fixturedefs
422- # No fixtures defined with this name.
423- if fixturedefs is None :
424- raise FixtureLookupError (argname , self )
425- # The are no fixtures with this name applicable for the function.
426- if not fixturedefs :
427- raise FixtureLookupError (argname , self )
428-
429- # A fixture may override another fixture with the same name, e.g. a
430- # fixture in a module can override a fixture in a conftest, a fixture in
431- # a class can override a fixture in the module, and so on.
432- # An overriding fixture can request its own name (possibly indirectly);
433- # in this case it gets the value of the fixture it overrides, one level
434- # up.
435- # Check how many `argname`s deep we are, and take the next one.
436- # `fixturedefs` is sorted from furthest to closest, so use negative
437- # indexing to go in reverse.
438- index = - 1
439- for request in self ._iter_chain ():
440- if request .fixturename == argname :
441- index -= 1
442- # If already consumed all of the available levels, fail.
443- if - index > len (fixturedefs ):
444- raise FixtureLookupError (argname , self )
445-
446- return fixturedefs [index ]
447-
448414 @property
449415 def config (self ) -> Config :
450416 """The pytest config object associated with this request."""
@@ -569,39 +535,53 @@ def _iter_chain(self) -> Iterator["SubRequest"]:
569535 def _get_active_fixturedef (
570536 self , argname : str
571537 ) -> Union ["FixtureDef[object]" , PseudoFixtureDef [object ]]:
538+ if argname == "request" :
539+ cached_result = (self , [0 ], None )
540+ return PseudoFixtureDef (cached_result , Scope .Function )
541+
542+ # If we already finished computing a fixture by this name in this item,
543+ # return it.
572544 fixturedef = self ._fixture_defs .get (argname )
573- if fixturedef is None :
574- try :
575- fixturedef = self ._getnextfixturedef (argname )
576- except FixtureLookupError :
577- if argname == "request" :
578- cached_result = (self , [0 ], None )
579- return PseudoFixtureDef (cached_result , Scope .Function )
580- raise
581- self ._compute_fixture_value (fixturedef )
582- self ._fixture_defs [argname ] = fixturedef
583- else :
545+ if fixturedef is not None :
584546 self ._check_scope (fixturedef , fixturedef ._scope )
585- return fixturedef
586-
587- def _get_fixturestack (self ) -> List ["FixtureDef[Any]" ]:
588- values = [request ._fixturedef for request in self ._iter_chain ()]
589- values .reverse ()
590- return values
547+ return fixturedef
591548
592- def _compute_fixture_value (self , fixturedef : "FixtureDef[object]" ) -> None :
593- """Create a SubRequest based on "self" and call the execute method
594- of the given FixtureDef object.
549+ # Find the appropriate fixturedef.
550+ fixturedefs = self ._arg2fixturedefs .get (argname , None )
551+ if fixturedefs is None :
552+ # We arrive here because of a dynamic call to
553+ # getfixturevalue(argname) which was naturally
554+ # not known at parsing/collection time.
555+ fixturedefs = self ._fixturemanager .getfixturedefs (argname , self ._pyfuncitem )
556+ if fixturedefs is not None :
557+ self ._arg2fixturedefs [argname ] = fixturedefs
558+ # No fixtures defined with this name.
559+ if fixturedefs is None :
560+ raise FixtureLookupError (argname , self )
561+ # The are no fixtures with this name applicable for the function.
562+ if not fixturedefs :
563+ raise FixtureLookupError (argname , self )
564+ # A fixture may override another fixture with the same name, e.g. a
565+ # fixture in a module can override a fixture in a conftest, a fixture in
566+ # a class can override a fixture in the module, and so on.
567+ # An overriding fixture can request its own name (possibly indirectly);
568+ # in this case it gets the value of the fixture it overrides, one level
569+ # up.
570+ # Check how many `argname`s deep we are, and take the next one.
571+ # `fixturedefs` is sorted from furthest to closest, so use negative
572+ # indexing to go in reverse.
573+ index = - 1
574+ for request in self ._iter_chain ():
575+ if request .fixturename == argname :
576+ index -= 1
577+ # If already consumed all of the available levels, fail.
578+ if - index > len (fixturedefs ):
579+ raise FixtureLookupError (argname , self )
580+ fixturedef = fixturedefs [index ]
595581
596- If the FixtureDef has cached the result it will do nothing, otherwise it will
597- setup and run the fixture, cache the value, and schedule a finalizer for it.
598- """
599- # prepare a subrequest object before calling fixture function
600- # (latter managed by fixturedef)
601- argname = fixturedef .argname
602- funcitem = self ._pyfuncitem
582+ # Prepare a SubRequest object for calling the fixture.
603583 try :
604- callspec = funcitem .callspec
584+ callspec = self . _pyfuncitem .callspec
605585 except AttributeError :
606586 callspec = None
607587 if callspec is not None and argname in callspec .params :
@@ -613,48 +593,56 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None:
613593 param = NOTSET
614594 param_index = 0
615595 scope = fixturedef ._scope
616-
617- has_params = fixturedef .params is not None
618- fixtures_not_supported = getattr (funcitem , "nofuncargs" , False )
619- if has_params and fixtures_not_supported :
620- msg = (
621- f"{ funcitem .name } does not support fixtures, maybe unittest.TestCase subclass?\n "
622- f"Node id: { funcitem .nodeid } \n "
623- f"Function type: { type (funcitem ).__name__ } "
624- )
625- fail (msg , pytrace = False )
626- if has_params :
627- frame = inspect .stack ()[3 ]
628- frameinfo = inspect .getframeinfo (frame [0 ])
629- source_path = absolutepath (frameinfo .filename )
630- source_lineno = frameinfo .lineno
631- try :
632- source_path_str = str (
633- source_path .relative_to (funcitem .config .rootpath )
634- )
635- except ValueError :
636- source_path_str = str (source_path )
637- location = getlocation (fixturedef .func , funcitem .config .rootpath )
638- msg = (
639- "The requested fixture has no parameter defined for test:\n "
640- f" { funcitem .nodeid } \n \n "
641- f"Requested fixture '{ fixturedef .argname } ' defined in:\n "
642- f"{ location } \n \n "
643- f"Requested here:\n "
644- f"{ source_path_str } :{ source_lineno } "
645- )
646- fail (msg , pytrace = False )
647-
648- # Check if a higher-level scoped fixture accesses a lower level one.
596+ self ._check_fixturedef_without_param (fixturedef )
649597 self ._check_scope (fixturedef , scope )
650-
651598 subrequest = SubRequest (
652599 self , scope , param , param_index , fixturedef , _ispytest = True
653600 )
654601
655602 # Make sure the fixture value is cached, running it if it isn't
656603 fixturedef .execute (request = subrequest )
657604
605+ self ._fixture_defs [argname ] = fixturedef
606+ return fixturedef
607+
608+ def _check_fixturedef_without_param (self , fixturedef : "FixtureDef[object]" ) -> None :
609+ """Check that this request is allowed to execute this fixturedef without
610+ a param."""
611+ funcitem = self ._pyfuncitem
612+ has_params = fixturedef .params is not None
613+ fixtures_not_supported = getattr (funcitem , "nofuncargs" , False )
614+ if has_params and fixtures_not_supported :
615+ msg = (
616+ f"{ funcitem .name } does not support fixtures, maybe unittest.TestCase subclass?\n "
617+ f"Node id: { funcitem .nodeid } \n "
618+ f"Function type: { type (funcitem ).__name__ } "
619+ )
620+ fail (msg , pytrace = False )
621+ if has_params :
622+ frame = inspect .stack ()[3 ]
623+ frameinfo = inspect .getframeinfo (frame [0 ])
624+ source_path = absolutepath (frameinfo .filename )
625+ source_lineno = frameinfo .lineno
626+ try :
627+ source_path_str = str (source_path .relative_to (funcitem .config .rootpath ))
628+ except ValueError :
629+ source_path_str = str (source_path )
630+ location = getlocation (fixturedef .func , funcitem .config .rootpath )
631+ msg = (
632+ "The requested fixture has no parameter defined for test:\n "
633+ f" { funcitem .nodeid } \n \n "
634+ f"Requested fixture '{ fixturedef .argname } ' defined in:\n "
635+ f"{ location } \n \n "
636+ f"Requested here:\n "
637+ f"{ source_path_str } :{ source_lineno } "
638+ )
639+ fail (msg , pytrace = False )
640+
641+ def _get_fixturestack (self ) -> List ["FixtureDef[Any]" ]:
642+ values = [request ._fixturedef for request in self ._iter_chain ()]
643+ values .reverse ()
644+ return values
645+
658646
659647@final
660648class TopRequest (FixtureRequest ):
@@ -877,13 +865,6 @@ def toterminal(self, tw: TerminalWriter) -> None:
877865 tw .line ("%s:%d" % (os .fspath (self .filename ), self .firstlineno + 1 ))
878866
879867
880- def fail_fixturefunc (fixturefunc , msg : str ) -> NoReturn :
881- fs , lineno = getfslineno (fixturefunc )
882- location = f"{ fs } :{ lineno + 1 } "
883- source = _pytest ._code .Source (fixturefunc )
884- fail (msg + ":\n \n " + str (source .indent ()) + "\n " + location , pytrace = False )
885-
886-
887868def call_fixture_func (
888869 fixturefunc : "_FixtureFunc[FixtureValue]" , request : FixtureRequest , kwargs
889870) -> FixtureValue :
@@ -913,7 +894,13 @@ def _teardown_yield_fixture(fixturefunc, it) -> None:
913894 except StopIteration :
914895 pass
915896 else :
916- fail_fixturefunc (fixturefunc , "fixture function has more than one 'yield'" )
897+ fs , lineno = getfslineno (fixturefunc )
898+ fail (
899+ f"fixture function has more than one 'yield':\n \n "
900+ f"{ Source (fixturefunc ).indent ()} \n "
901+ f"{ fs } :{ lineno + 1 } " ,
902+ pytrace = False ,
903+ )
917904
918905
919906def _eval_scope_callable (
0 commit comments