35
35
import _pytest
36
36
from _pytest import nodes
37
37
from _pytest ._code import getfslineno
38
+ from _pytest ._code import Source
38
39
from _pytest ._code .code import FormattedExcinfo
39
40
from _pytest ._code .code import TerminalRepr
40
41
from _pytest ._io import TerminalWriter
@@ -410,41 +411,6 @@ def node(self):
410
411
"""Underlying collection node (depends on current request scope)."""
411
412
raise NotImplementedError ()
412
413
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
-
448
414
@property
449
415
def config (self ) -> Config :
450
416
"""The pytest config object associated with this request."""
@@ -569,39 +535,53 @@ def _iter_chain(self) -> Iterator["SubRequest"]:
569
535
def _get_active_fixturedef (
570
536
self , argname : str
571
537
) -> 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.
572
544
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 :
584
546
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
591
548
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 ]
595
581
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.
603
583
try :
604
- callspec = funcitem .callspec
584
+ callspec = self . _pyfuncitem .callspec
605
585
except AttributeError :
606
586
callspec = None
607
587
if callspec is not None and argname in callspec .params :
@@ -613,48 +593,56 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None:
613
593
param = NOTSET
614
594
param_index = 0
615
595
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 )
649
597
self ._check_scope (fixturedef , scope )
650
-
651
598
subrequest = SubRequest (
652
599
self , scope , param , param_index , fixturedef , _ispytest = True
653
600
)
654
601
655
602
# Make sure the fixture value is cached, running it if it isn't
656
603
fixturedef .execute (request = subrequest )
657
604
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
+
658
646
659
647
@final
660
648
class TopRequest (FixtureRequest ):
@@ -877,13 +865,6 @@ def toterminal(self, tw: TerminalWriter) -> None:
877
865
tw .line ("%s:%d" % (os .fspath (self .filename ), self .firstlineno + 1 ))
878
866
879
867
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
-
887
868
def call_fixture_func (
888
869
fixturefunc : "_FixtureFunc[FixtureValue]" , request : FixtureRequest , kwargs
889
870
) -> FixtureValue :
@@ -913,7 +894,13 @@ def _teardown_yield_fixture(fixturefunc, it) -> None:
913
894
except StopIteration :
914
895
pass
915
896
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
+ )
917
904
918
905
919
906
def _eval_scope_callable (
0 commit comments