6060from _pytest .outcomes import TEST_OUTCOME
6161from _pytest .pathlib import absolutepath
6262from _pytest .pathlib import bestrelpath
63+ from _pytest .scope import _ScopeName
6364from _pytest .scope import HIGH_SCOPES
6465from _pytest .scope import Scope
6566
6667
6768if TYPE_CHECKING :
6869 from typing import Deque
6970
70- from _pytest .scope import _ScopeName
7171 from _pytest .main import Session
7272 from _pytest .python import CallSpec2
7373 from _pytest .python import Function
@@ -274,16 +274,27 @@ def reorder_items_atscope(
274274
275275@dataclasses .dataclass (frozen = True )
276276class FuncFixtureInfo :
277+ """Fixture-related information for a fixture-requesting item (e.g. test
278+ function).
279+
280+ This is used to examine the fixtures which an item requests statically
281+ (known during collection). This includes autouse fixtures, fixtures
282+ requested by the `usefixtures` marker, fixtures requested in the function
283+ parameters, and the transitive closure of these.
284+
285+ An item may also request fixtures dynamically (using `request.getfixturevalue`);
286+ these are not reflected here.
287+ """
288+
277289 __slots__ = ("argnames" , "initialnames" , "names_closure" , "name2fixturedefs" )
278290
279- # Original function argument names, i.e. fixture names that the function
280- # requests directly.
291+ # Fixture names that the item requests directly by function parameters.
281292 argnames : Tuple [str , ...]
282- # Fixture names that the function immediately requires. These include
293+ # Fixture names that the item immediately requires. These include
283294 # argnames + fixture names specified via usefixtures and via autouse=True in
284295 # fixture definitions.
285296 initialnames : Tuple [str , ...]
286- # The transitive closure of the fixture names that the function requires.
297+ # The transitive closure of the fixture names that the item requires.
287298 # Note: can't include dynamic dependencies (`request.getfixturevalue` calls).
288299 names_closure : List [str ]
289300 # A map from a fixture name in the transitive closure to the FixtureDefs
@@ -364,7 +375,7 @@ def __init__(self, pyfuncitem: "Function", *, _ispytest: bool = False) -> None:
364375 self .param : Any
365376
366377 @property
367- def scope (self ) -> " _ScopeName" :
378+ def scope (self ) -> _ScopeName :
368379 """Scope string, one of "function", "class", "module", "package", "session"."""
369380 return self ._scope .value
370381
@@ -467,8 +478,7 @@ def path(self) -> Path:
467478 """Path where the test function was collected."""
468479 if self .scope not in ("function" , "class" , "module" , "package" ):
469480 raise AttributeError (f"path not available in { self .scope } -scoped context" )
470- # TODO: Remove ignore once _pyfuncitem is properly typed.
471- return self ._pyfuncitem .path # type: ignore
481+ return self ._pyfuncitem .path
472482
473483 @property
474484 def keywords (self ) -> MutableMapping [str , Any ]:
@@ -540,20 +550,17 @@ def getfixturevalue(self, argname: str) -> Any:
540550 def _get_active_fixturedef (
541551 self , argname : str
542552 ) -> Union ["FixtureDef[object]" , PseudoFixtureDef [object ]]:
543- try :
544- return self ._fixture_defs [argname ]
545- except KeyError :
553+ fixturedef = self ._fixture_defs .get (argname )
554+ if fixturedef is None :
546555 try :
547556 fixturedef = self ._getnextfixturedef (argname )
548557 except FixtureLookupError :
549558 if argname == "request" :
550559 cached_result = (self , [0 ], None )
551560 return PseudoFixtureDef (cached_result , Scope .Function )
552561 raise
553- # Remove indent to prevent the python3 exception
554- # from leaking into the call.
555- self ._compute_fixture_value (fixturedef )
556- self ._fixture_defs [argname ] = fixturedef
562+ self ._compute_fixture_value (fixturedef )
563+ self ._fixture_defs [argname ] = fixturedef
557564 return fixturedef
558565
559566 def _get_fixturestack (self ) -> List ["FixtureDef[Any]" ]:
@@ -869,10 +876,10 @@ def _teardown_yield_fixture(fixturefunc, it) -> None:
869876
870877
871878def _eval_scope_callable (
872- scope_callable : " Callable[[str, Config], _ScopeName]" ,
879+ scope_callable : Callable [[str , Config ], _ScopeName ],
873880 fixture_name : str ,
874881 config : Config ,
875- ) -> " _ScopeName" :
882+ ) -> _ScopeName :
876883 try :
877884 # Type ignored because there is no typing mechanism to specify
878885 # keyword arguments, currently.
@@ -907,7 +914,7 @@ def __init__(
907914 baseid : Optional [str ],
908915 argname : str ,
909916 func : "_FixtureFunc[FixtureValue]" ,
910- scope : Union [Scope , " _ScopeName" , Callable [[str , Config ], " _ScopeName" ], None ],
917+ scope : Union [Scope , _ScopeName , Callable [[str , Config ], _ScopeName ], None ],
911918 params : Optional [Sequence [object ]],
912919 unittest : bool = False ,
913920 ids : Optional [
@@ -960,8 +967,6 @@ def __init__(
960967 # The names requested by the fixtures.
961968 self .argnames : Final = getfuncargnames (func , name = argname , is_method = unittest )
962969 # Whether the fixture was collected from a unittest TestCase class.
963- # Note that it really only makes sense to define autouse fixtures in
964- # unittest TestCases.
965970 self .unittest : Final = unittest
966971 # If the fixture was executed, the current value of the fixture.
967972 # Can change if the fixture is executed with different parameters.
@@ -972,7 +977,7 @@ def __init__(
972977 self .is_pseudo = is_pseudo
973978
974979 @property
975- def scope (self ) -> " _ScopeName" :
980+ def scope (self ) -> _ScopeName :
976981 """Scope string, one of "function", "class", "module", "package", "session"."""
977982 return self ._scope .value
978983
@@ -1086,9 +1091,10 @@ def pytest_fixture_setup(
10861091 try :
10871092 result = call_fixture_func (fixturefunc , request , kwargs )
10881093 except TEST_OUTCOME as e :
1089- if isinstance (e , skip .Exception ) and not fixturefunc .__name__ .startswith (
1090- "xunit_setup"
1091- ):
1094+ if isinstance (e , skip .Exception ):
1095+ # The test requested a fixture which caused a skip.
1096+ # Don't show the fixture as the skip location, as then the user
1097+ # wouldn't know which test skipped.
10921098 e ._use_item_location = True
10931099 fixturedef .cached_result = (None , my_cache_key , e )
10941100 raise
@@ -1391,8 +1397,26 @@ def _get_direct_parametrize_args(self, node: nodes.Node) -> List[str]:
13911397 return parametrize_argnames
13921398
13931399 def getfixtureinfo (
1394- self , node : nodes .Node , func , cls , funcargs : bool = True
1400+ self ,
1401+ node : nodes .Item ,
1402+ func : Callable [..., object ],
1403+ cls : Optional [type ],
1404+ funcargs : bool = True ,
13951405 ) -> FuncFixtureInfo :
1406+ """Calculate the :class:`FuncFixtureInfo` for an item.
1407+
1408+ If ``funcargs`` is false, or if the item sets an attribute
1409+ ``nofuncargs = True``, then ``func`` is not examined at all.
1410+
1411+ :param node:
1412+ The item requesting the fixtures.
1413+ :param func:
1414+ The item's function.
1415+ :param cls:
1416+ If the function is a method, the method's class.
1417+ :param funcargs:
1418+ Whether to look into func's parameters as fixture requests.
1419+ """
13961420 if funcargs and not getattr (node , "nofuncargs" , False ):
13971421 argnames = getfuncargnames (func , name = node .name , cls = cls )
13981422 else :
@@ -1402,8 +1426,7 @@ def getfixtureinfo(
14021426 arg for mark in node .iter_markers (name = "usefixtures" ) for arg in mark .args
14031427 )
14041428 initialnames = usefixtures + argnames
1405- fm = node .session ._fixturemanager
1406- initialnames , names_closure , arg2fixturedefs = fm .getfixtureclosure (
1429+ initialnames , names_closure , arg2fixturedefs = self .getfixtureclosure (
14071430 initialnames , node , ignore_args = self ._get_direct_parametrize_args (node )
14081431 )
14091432 return FuncFixtureInfo (argnames , initialnames , names_closure , arg2fixturedefs )
0 commit comments