fixtures.py 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047
  1. # mypy: allow-untyped-defs
  2. from __future__ import annotations
  3. import abc
  4. from collections import defaultdict
  5. from collections import deque
  6. from collections import OrderedDict
  7. from collections.abc import Callable
  8. from collections.abc import Generator
  9. from collections.abc import Iterable
  10. from collections.abc import Iterator
  11. from collections.abc import Mapping
  12. from collections.abc import MutableMapping
  13. from collections.abc import Sequence
  14. from collections.abc import Set as AbstractSet
  15. import dataclasses
  16. import functools
  17. import inspect
  18. import os
  19. from pathlib import Path
  20. import sys
  21. import types
  22. from typing import Any
  23. from typing import cast
  24. from typing import Final
  25. from typing import final
  26. from typing import Generic
  27. from typing import NoReturn
  28. from typing import overload
  29. from typing import TYPE_CHECKING
  30. from typing import TypeVar
  31. import warnings
  32. import _pytest
  33. from _pytest import nodes
  34. from _pytest._code import getfslineno
  35. from _pytest._code import Source
  36. from _pytest._code.code import FormattedExcinfo
  37. from _pytest._code.code import TerminalRepr
  38. from _pytest._io import TerminalWriter
  39. from _pytest.compat import assert_never
  40. from _pytest.compat import get_real_func
  41. from _pytest.compat import getfuncargnames
  42. from _pytest.compat import getimfunc
  43. from _pytest.compat import getlocation
  44. from _pytest.compat import NOTSET
  45. from _pytest.compat import NotSetType
  46. from _pytest.compat import safe_getattr
  47. from _pytest.compat import safe_isclass
  48. from _pytest.compat import signature
  49. from _pytest.config import _PluggyPlugin
  50. from _pytest.config import Config
  51. from _pytest.config import ExitCode
  52. from _pytest.config.argparsing import Parser
  53. from _pytest.deprecated import check_ispytest
  54. from _pytest.deprecated import MARKED_FIXTURE
  55. from _pytest.deprecated import YIELD_FIXTURE
  56. from _pytest.main import Session
  57. from _pytest.mark import Mark
  58. from _pytest.mark import ParameterSet
  59. from _pytest.mark.structures import MarkDecorator
  60. from _pytest.outcomes import fail
  61. from _pytest.outcomes import skip
  62. from _pytest.outcomes import TEST_OUTCOME
  63. from _pytest.pathlib import absolutepath
  64. from _pytest.pathlib import bestrelpath
  65. from _pytest.scope import _ScopeName
  66. from _pytest.scope import HIGH_SCOPES
  67. from _pytest.scope import Scope
  68. from _pytest.warning_types import PytestRemovedIn9Warning
  69. from _pytest.warning_types import PytestWarning
  70. if sys.version_info < (3, 11):
  71. from exceptiongroup import BaseExceptionGroup
  72. if TYPE_CHECKING:
  73. from _pytest.python import CallSpec2
  74. from _pytest.python import Function
  75. from _pytest.python import Metafunc
  76. # The value of the fixture -- return/yield of the fixture function (type variable).
  77. FixtureValue = TypeVar("FixtureValue", covariant=True)
  78. # The type of the fixture function (type variable).
  79. FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object])
  80. # The type of a fixture function (type alias generic in fixture value).
  81. _FixtureFunc = Callable[..., FixtureValue] | Callable[..., Generator[FixtureValue]]
  82. # The type of FixtureDef.cached_result (type alias generic in fixture value).
  83. _FixtureCachedResult = (
  84. tuple[
  85. # The result.
  86. FixtureValue,
  87. # Cache key.
  88. object,
  89. None,
  90. ]
  91. | tuple[
  92. None,
  93. # Cache key.
  94. object,
  95. # The exception and the original traceback.
  96. tuple[BaseException, types.TracebackType | None],
  97. ]
  98. )
  99. def pytest_sessionstart(session: Session) -> None:
  100. session._fixturemanager = FixtureManager(session)
  101. def get_scope_package(
  102. node: nodes.Item,
  103. fixturedef: FixtureDef[object],
  104. ) -> nodes.Node | None:
  105. from _pytest.python import Package
  106. for parent in node.iter_parents():
  107. if isinstance(parent, Package) and parent.nodeid == fixturedef.baseid:
  108. return parent
  109. return node.session
  110. def get_scope_node(node: nodes.Node, scope: Scope) -> nodes.Node | None:
  111. """Get the closest parent node (including self) which matches the given
  112. scope.
  113. If there is no parent node for the scope (e.g. asking for class scope on a
  114. Module, or on a Function when not defined in a class), returns None.
  115. """
  116. import _pytest.python
  117. if scope is Scope.Function:
  118. # Type ignored because this is actually safe, see:
  119. # https://github.com/python/mypy/issues/4717
  120. return node.getparent(nodes.Item) # type: ignore[type-abstract]
  121. elif scope is Scope.Class:
  122. return node.getparent(_pytest.python.Class)
  123. elif scope is Scope.Module:
  124. return node.getparent(_pytest.python.Module)
  125. elif scope is Scope.Package:
  126. return node.getparent(_pytest.python.Package)
  127. elif scope is Scope.Session:
  128. return node.getparent(_pytest.main.Session)
  129. else:
  130. assert_never(scope)
  131. # TODO: Try to use FixtureFunctionDefinition instead of the marker
  132. def getfixturemarker(obj: object) -> FixtureFunctionMarker | None:
  133. """Return fixturemarker or None if it doesn't exist"""
  134. if isinstance(obj, FixtureFunctionDefinition):
  135. return obj._fixture_function_marker
  136. return None
  137. # Algorithm for sorting on a per-parametrized resource setup basis.
  138. # It is called for Session scope first and performs sorting
  139. # down to the lower scopes such as to minimize number of "high scope"
  140. # setups and teardowns.
  141. @dataclasses.dataclass(frozen=True)
  142. class ParamArgKey:
  143. """A key for a high-scoped parameter used by an item.
  144. For use as a hashable key in `reorder_items`. The combination of fields
  145. is meant to uniquely identify a particular "instance" of a param,
  146. potentially shared by multiple items in a scope.
  147. """
  148. #: The param name.
  149. argname: str
  150. param_index: int
  151. #: For scopes Package, Module, Class, the path to the file (directory in
  152. #: Package's case) of the package/module/class where the item is defined.
  153. scoped_item_path: Path | None
  154. #: For Class scope, the class where the item is defined.
  155. item_cls: type | None
  156. _V = TypeVar("_V")
  157. OrderedSet = dict[_V, None]
  158. def get_param_argkeys(item: nodes.Item, scope: Scope) -> Iterator[ParamArgKey]:
  159. """Return all ParamArgKeys for item matching the specified high scope."""
  160. assert scope is not Scope.Function
  161. try:
  162. callspec: CallSpec2 = item.callspec # type: ignore[attr-defined]
  163. except AttributeError:
  164. return
  165. item_cls = None
  166. if scope is Scope.Session:
  167. scoped_item_path = None
  168. elif scope is Scope.Package:
  169. # Package key = module's directory.
  170. scoped_item_path = item.path.parent
  171. elif scope is Scope.Module:
  172. scoped_item_path = item.path
  173. elif scope is Scope.Class:
  174. scoped_item_path = item.path
  175. item_cls = item.cls # type: ignore[attr-defined]
  176. else:
  177. assert_never(scope)
  178. for argname in callspec.indices:
  179. if callspec._arg2scope[argname] != scope:
  180. continue
  181. param_index = callspec.indices[argname]
  182. yield ParamArgKey(argname, param_index, scoped_item_path, item_cls)
  183. def reorder_items(items: Sequence[nodes.Item]) -> list[nodes.Item]:
  184. argkeys_by_item: dict[Scope, dict[nodes.Item, OrderedSet[ParamArgKey]]] = {}
  185. items_by_argkey: dict[Scope, dict[ParamArgKey, OrderedDict[nodes.Item, None]]] = {}
  186. for scope in HIGH_SCOPES:
  187. scoped_argkeys_by_item = argkeys_by_item[scope] = {}
  188. scoped_items_by_argkey = items_by_argkey[scope] = defaultdict(OrderedDict)
  189. for item in items:
  190. argkeys = dict.fromkeys(get_param_argkeys(item, scope))
  191. if argkeys:
  192. scoped_argkeys_by_item[item] = argkeys
  193. for argkey in argkeys:
  194. scoped_items_by_argkey[argkey][item] = None
  195. items_set = dict.fromkeys(items)
  196. return list(
  197. reorder_items_atscope(
  198. items_set, argkeys_by_item, items_by_argkey, Scope.Session
  199. )
  200. )
  201. def reorder_items_atscope(
  202. items: OrderedSet[nodes.Item],
  203. argkeys_by_item: Mapping[Scope, Mapping[nodes.Item, OrderedSet[ParamArgKey]]],
  204. items_by_argkey: Mapping[
  205. Scope, Mapping[ParamArgKey, OrderedDict[nodes.Item, None]]
  206. ],
  207. scope: Scope,
  208. ) -> OrderedSet[nodes.Item]:
  209. if scope is Scope.Function or len(items) < 3:
  210. return items
  211. scoped_items_by_argkey = items_by_argkey[scope]
  212. scoped_argkeys_by_item = argkeys_by_item[scope]
  213. ignore: set[ParamArgKey] = set()
  214. items_deque = deque(items)
  215. items_done: OrderedSet[nodes.Item] = {}
  216. while items_deque:
  217. no_argkey_items: OrderedSet[nodes.Item] = {}
  218. slicing_argkey = None
  219. while items_deque:
  220. item = items_deque.popleft()
  221. if item in items_done or item in no_argkey_items:
  222. continue
  223. argkeys = dict.fromkeys(
  224. k for k in scoped_argkeys_by_item.get(item, ()) if k not in ignore
  225. )
  226. if not argkeys:
  227. no_argkey_items[item] = None
  228. else:
  229. slicing_argkey, _ = argkeys.popitem()
  230. # We don't have to remove relevant items from later in the
  231. # deque because they'll just be ignored.
  232. matching_items = [
  233. i for i in scoped_items_by_argkey[slicing_argkey] if i in items
  234. ]
  235. for i in reversed(matching_items):
  236. items_deque.appendleft(i)
  237. # Fix items_by_argkey order.
  238. for other_scope in HIGH_SCOPES:
  239. other_scoped_items_by_argkey = items_by_argkey[other_scope]
  240. for argkey in argkeys_by_item[other_scope].get(i, ()):
  241. argkey_dict = other_scoped_items_by_argkey[argkey]
  242. if not hasattr(sys, "pypy_version_info"):
  243. argkey_dict[i] = None
  244. argkey_dict.move_to_end(i, last=False)
  245. else:
  246. # Work around a bug in PyPy:
  247. # https://github.com/pypy/pypy/issues/5257
  248. # https://github.com/pytest-dev/pytest/issues/13312
  249. bkp = argkey_dict.copy()
  250. argkey_dict.clear()
  251. argkey_dict[i] = None
  252. argkey_dict.update(bkp)
  253. break
  254. if no_argkey_items:
  255. reordered_no_argkey_items = reorder_items_atscope(
  256. no_argkey_items, argkeys_by_item, items_by_argkey, scope.next_lower()
  257. )
  258. items_done.update(reordered_no_argkey_items)
  259. if slicing_argkey is not None:
  260. ignore.add(slicing_argkey)
  261. return items_done
  262. @dataclasses.dataclass(frozen=True)
  263. class FuncFixtureInfo:
  264. """Fixture-related information for a fixture-requesting item (e.g. test
  265. function).
  266. This is used to examine the fixtures which an item requests statically
  267. (known during collection). This includes autouse fixtures, fixtures
  268. requested by the `usefixtures` marker, fixtures requested in the function
  269. parameters, and the transitive closure of these.
  270. An item may also request fixtures dynamically (using `request.getfixturevalue`);
  271. these are not reflected here.
  272. """
  273. __slots__ = ("argnames", "initialnames", "name2fixturedefs", "names_closure")
  274. # Fixture names that the item requests directly by function parameters.
  275. argnames: tuple[str, ...]
  276. # Fixture names that the item immediately requires. These include
  277. # argnames + fixture names specified via usefixtures and via autouse=True in
  278. # fixture definitions.
  279. initialnames: tuple[str, ...]
  280. # The transitive closure of the fixture names that the item requires.
  281. # Note: can't include dynamic dependencies (`request.getfixturevalue` calls).
  282. names_closure: list[str]
  283. # A map from a fixture name in the transitive closure to the FixtureDefs
  284. # matching the name which are applicable to this function.
  285. # There may be multiple overriding fixtures with the same name. The
  286. # sequence is ordered from furthest to closes to the function.
  287. name2fixturedefs: dict[str, Sequence[FixtureDef[Any]]]
  288. def prune_dependency_tree(self) -> None:
  289. """Recompute names_closure from initialnames and name2fixturedefs.
  290. Can only reduce names_closure, which means that the new closure will
  291. always be a subset of the old one. The order is preserved.
  292. This method is needed because direct parametrization may shadow some
  293. of the fixtures that were included in the originally built dependency
  294. tree. In this way the dependency tree can get pruned, and the closure
  295. of argnames may get reduced.
  296. """
  297. closure: set[str] = set()
  298. working_set = set(self.initialnames)
  299. while working_set:
  300. argname = working_set.pop()
  301. # Argname may be something not included in the original names_closure,
  302. # in which case we ignore it. This currently happens with pseudo
  303. # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'.
  304. # So they introduce the new dependency 'request' which might have
  305. # been missing in the original tree (closure).
  306. if argname not in closure and argname in self.names_closure:
  307. closure.add(argname)
  308. if argname in self.name2fixturedefs:
  309. working_set.update(self.name2fixturedefs[argname][-1].argnames)
  310. self.names_closure[:] = sorted(closure, key=self.names_closure.index)
  311. class FixtureRequest(abc.ABC):
  312. """The type of the ``request`` fixture.
  313. A request object gives access to the requesting test context and has a
  314. ``param`` attribute in case the fixture is parametrized.
  315. """
  316. def __init__(
  317. self,
  318. pyfuncitem: Function,
  319. fixturename: str | None,
  320. arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]],
  321. fixture_defs: dict[str, FixtureDef[Any]],
  322. *,
  323. _ispytest: bool = False,
  324. ) -> None:
  325. check_ispytest(_ispytest)
  326. #: Fixture for which this request is being performed.
  327. self.fixturename: Final = fixturename
  328. self._pyfuncitem: Final = pyfuncitem
  329. # The FixtureDefs for each fixture name requested by this item.
  330. # Starts from the statically-known fixturedefs resolved during
  331. # collection. Dynamically requested fixtures (using
  332. # `request.getfixturevalue("foo")`) are added dynamically.
  333. self._arg2fixturedefs: Final = arg2fixturedefs
  334. # The evaluated argnames so far, mapping to the FixtureDef they resolved
  335. # to.
  336. self._fixture_defs: Final = fixture_defs
  337. # Notes on the type of `param`:
  338. # -`request.param` is only defined in parametrized fixtures, and will raise
  339. # AttributeError otherwise. Python typing has no notion of "undefined", so
  340. # this cannot be reflected in the type.
  341. # - Technically `param` is only (possibly) defined on SubRequest, not
  342. # FixtureRequest, but the typing of that is still in flux so this cheats.
  343. # - In the future we might consider using a generic for the param type, but
  344. # for now just using Any.
  345. self.param: Any
  346. @property
  347. def _fixturemanager(self) -> FixtureManager:
  348. return self._pyfuncitem.session._fixturemanager
  349. @property
  350. @abc.abstractmethod
  351. def _scope(self) -> Scope:
  352. raise NotImplementedError()
  353. @property
  354. def scope(self) -> _ScopeName:
  355. """Scope string, one of "function", "class", "module", "package", "session"."""
  356. return self._scope.value
  357. @abc.abstractmethod
  358. def _check_scope(
  359. self,
  360. requested_fixturedef: FixtureDef[object],
  361. requested_scope: Scope,
  362. ) -> None:
  363. raise NotImplementedError()
  364. @property
  365. def fixturenames(self) -> list[str]:
  366. """Names of all active fixtures in this request."""
  367. result = list(self._pyfuncitem.fixturenames)
  368. result.extend(set(self._fixture_defs).difference(result))
  369. return result
  370. @property
  371. @abc.abstractmethod
  372. def node(self):
  373. """Underlying collection node (depends on current request scope)."""
  374. raise NotImplementedError()
  375. @property
  376. def config(self) -> Config:
  377. """The pytest config object associated with this request."""
  378. return self._pyfuncitem.config
  379. @property
  380. def function(self):
  381. """Test function object if the request has a per-function scope."""
  382. if self.scope != "function":
  383. raise AttributeError(
  384. f"function not available in {self.scope}-scoped context"
  385. )
  386. return self._pyfuncitem.obj
  387. @property
  388. def cls(self):
  389. """Class (can be None) where the test function was collected."""
  390. if self.scope not in ("class", "function"):
  391. raise AttributeError(f"cls not available in {self.scope}-scoped context")
  392. clscol = self._pyfuncitem.getparent(_pytest.python.Class)
  393. if clscol:
  394. return clscol.obj
  395. @property
  396. def instance(self):
  397. """Instance (can be None) on which test function was collected."""
  398. if self.scope != "function":
  399. return None
  400. return getattr(self._pyfuncitem, "instance", None)
  401. @property
  402. def module(self):
  403. """Python module object where the test function was collected."""
  404. if self.scope not in ("function", "class", "module"):
  405. raise AttributeError(f"module not available in {self.scope}-scoped context")
  406. mod = self._pyfuncitem.getparent(_pytest.python.Module)
  407. assert mod is not None
  408. return mod.obj
  409. @property
  410. def path(self) -> Path:
  411. """Path where the test function was collected."""
  412. if self.scope not in ("function", "class", "module", "package"):
  413. raise AttributeError(f"path not available in {self.scope}-scoped context")
  414. return self._pyfuncitem.path
  415. @property
  416. def keywords(self) -> MutableMapping[str, Any]:
  417. """Keywords/markers dictionary for the underlying node."""
  418. node: nodes.Node = self.node
  419. return node.keywords
  420. @property
  421. def session(self) -> Session:
  422. """Pytest session object."""
  423. return self._pyfuncitem.session
  424. @abc.abstractmethod
  425. def addfinalizer(self, finalizer: Callable[[], object]) -> None:
  426. """Add finalizer/teardown function to be called without arguments after
  427. the last test within the requesting test context finished execution."""
  428. raise NotImplementedError()
  429. def applymarker(self, marker: str | MarkDecorator) -> None:
  430. """Apply a marker to a single test function invocation.
  431. This method is useful if you don't want to have a keyword/marker
  432. on all function invocations.
  433. :param marker:
  434. An object created by a call to ``pytest.mark.NAME(...)``.
  435. """
  436. self.node.add_marker(marker)
  437. def raiseerror(self, msg: str | None) -> NoReturn:
  438. """Raise a FixtureLookupError exception.
  439. :param msg:
  440. An optional custom error message.
  441. """
  442. raise FixtureLookupError(None, self, msg)
  443. def getfixturevalue(self, argname: str) -> Any:
  444. """Dynamically run a named fixture function.
  445. Declaring fixtures via function argument is recommended where possible.
  446. But if you can only decide whether to use another fixture at test
  447. setup time, you may use this function to retrieve it inside a fixture
  448. or test function body.
  449. This method can be used during the test setup phase or the test run
  450. phase, but during the test teardown phase a fixture's value may not
  451. be available.
  452. :param argname:
  453. The fixture name.
  454. :raises pytest.FixtureLookupError:
  455. If the given fixture could not be found.
  456. """
  457. # Note that in addition to the use case described in the docstring,
  458. # getfixturevalue() is also called by pytest itself during item and fixture
  459. # setup to evaluate the fixtures that are requested statically
  460. # (using function parameters, autouse, etc).
  461. fixturedef = self._get_active_fixturedef(argname)
  462. assert fixturedef.cached_result is not None, (
  463. f'The fixture value for "{argname}" is not available. '
  464. "This can happen when the fixture has already been torn down."
  465. )
  466. return fixturedef.cached_result[0]
  467. def _iter_chain(self) -> Iterator[SubRequest]:
  468. """Yield all SubRequests in the chain, from self up.
  469. Note: does *not* yield the TopRequest.
  470. """
  471. current = self
  472. while isinstance(current, SubRequest):
  473. yield current
  474. current = current._parent_request
  475. def _get_active_fixturedef(self, argname: str) -> FixtureDef[object]:
  476. if argname == "request":
  477. return RequestFixtureDef(self)
  478. # If we already finished computing a fixture by this name in this item,
  479. # return it.
  480. fixturedef = self._fixture_defs.get(argname)
  481. if fixturedef is not None:
  482. self._check_scope(fixturedef, fixturedef._scope)
  483. return fixturedef
  484. # Find the appropriate fixturedef.
  485. fixturedefs = self._arg2fixturedefs.get(argname, None)
  486. if fixturedefs is None:
  487. # We arrive here because of a dynamic call to
  488. # getfixturevalue(argname) which was naturally
  489. # not known at parsing/collection time.
  490. fixturedefs = self._fixturemanager.getfixturedefs(argname, self._pyfuncitem)
  491. if fixturedefs is not None:
  492. self._arg2fixturedefs[argname] = fixturedefs
  493. # No fixtures defined with this name.
  494. if fixturedefs is None:
  495. raise FixtureLookupError(argname, self)
  496. # The are no fixtures with this name applicable for the function.
  497. if not fixturedefs:
  498. raise FixtureLookupError(argname, self)
  499. # A fixture may override another fixture with the same name, e.g. a
  500. # fixture in a module can override a fixture in a conftest, a fixture in
  501. # a class can override a fixture in the module, and so on.
  502. # An overriding fixture can request its own name (possibly indirectly);
  503. # in this case it gets the value of the fixture it overrides, one level
  504. # up.
  505. # Check how many `argname`s deep we are, and take the next one.
  506. # `fixturedefs` is sorted from furthest to closest, so use negative
  507. # indexing to go in reverse.
  508. index = -1
  509. for request in self._iter_chain():
  510. if request.fixturename == argname:
  511. index -= 1
  512. # If already consumed all of the available levels, fail.
  513. if -index > len(fixturedefs):
  514. raise FixtureLookupError(argname, self)
  515. fixturedef = fixturedefs[index]
  516. # Prepare a SubRequest object for calling the fixture.
  517. try:
  518. callspec = self._pyfuncitem.callspec
  519. except AttributeError:
  520. callspec = None
  521. if callspec is not None and argname in callspec.params:
  522. param = callspec.params[argname]
  523. param_index = callspec.indices[argname]
  524. # The parametrize invocation scope overrides the fixture's scope.
  525. scope = callspec._arg2scope[argname]
  526. else:
  527. param = NOTSET
  528. param_index = 0
  529. scope = fixturedef._scope
  530. self._check_fixturedef_without_param(fixturedef)
  531. # The parametrize invocation scope only controls caching behavior while
  532. # allowing wider-scoped fixtures to keep depending on the parametrized
  533. # fixture. Scope control is enforced for parametrized fixtures
  534. # by recreating the whole fixture tree on parameter change.
  535. # Hence `fixturedef._scope`, not `scope`.
  536. self._check_scope(fixturedef, fixturedef._scope)
  537. subrequest = SubRequest(
  538. self, scope, param, param_index, fixturedef, _ispytest=True
  539. )
  540. # Make sure the fixture value is cached, running it if it isn't
  541. fixturedef.execute(request=subrequest)
  542. self._fixture_defs[argname] = fixturedef
  543. return fixturedef
  544. def _check_fixturedef_without_param(self, fixturedef: FixtureDef[object]) -> None:
  545. """Check that this request is allowed to execute this fixturedef without
  546. a param."""
  547. funcitem = self._pyfuncitem
  548. has_params = fixturedef.params is not None
  549. fixtures_not_supported = getattr(funcitem, "nofuncargs", False)
  550. if has_params and fixtures_not_supported:
  551. msg = (
  552. f"{funcitem.name} does not support fixtures, maybe unittest.TestCase subclass?\n"
  553. f"Node id: {funcitem.nodeid}\n"
  554. f"Function type: {type(funcitem).__name__}"
  555. )
  556. fail(msg, pytrace=False)
  557. if has_params:
  558. frame = inspect.stack()[3]
  559. frameinfo = inspect.getframeinfo(frame[0])
  560. source_path = absolutepath(frameinfo.filename)
  561. source_lineno = frameinfo.lineno
  562. try:
  563. source_path_str = str(source_path.relative_to(funcitem.config.rootpath))
  564. except ValueError:
  565. source_path_str = str(source_path)
  566. location = getlocation(fixturedef.func, funcitem.config.rootpath)
  567. msg = (
  568. "The requested fixture has no parameter defined for test:\n"
  569. f" {funcitem.nodeid}\n\n"
  570. f"Requested fixture '{fixturedef.argname}' defined in:\n"
  571. f"{location}\n\n"
  572. f"Requested here:\n"
  573. f"{source_path_str}:{source_lineno}"
  574. )
  575. fail(msg, pytrace=False)
  576. def _get_fixturestack(self) -> list[FixtureDef[Any]]:
  577. values = [request._fixturedef for request in self._iter_chain()]
  578. values.reverse()
  579. return values
  580. @final
  581. class TopRequest(FixtureRequest):
  582. """The type of the ``request`` fixture in a test function."""
  583. def __init__(self, pyfuncitem: Function, *, _ispytest: bool = False) -> None:
  584. super().__init__(
  585. fixturename=None,
  586. pyfuncitem=pyfuncitem,
  587. arg2fixturedefs=pyfuncitem._fixtureinfo.name2fixturedefs.copy(),
  588. fixture_defs={},
  589. _ispytest=_ispytest,
  590. )
  591. @property
  592. def _scope(self) -> Scope:
  593. return Scope.Function
  594. def _check_scope(
  595. self,
  596. requested_fixturedef: FixtureDef[object],
  597. requested_scope: Scope,
  598. ) -> None:
  599. # TopRequest always has function scope so always valid.
  600. pass
  601. @property
  602. def node(self):
  603. return self._pyfuncitem
  604. def __repr__(self) -> str:
  605. return f"<FixtureRequest for {self.node!r}>"
  606. def _fillfixtures(self) -> None:
  607. item = self._pyfuncitem
  608. for argname in item.fixturenames:
  609. if argname not in item.funcargs:
  610. item.funcargs[argname] = self.getfixturevalue(argname)
  611. def addfinalizer(self, finalizer: Callable[[], object]) -> None:
  612. self.node.addfinalizer(finalizer)
  613. @final
  614. class SubRequest(FixtureRequest):
  615. """The type of the ``request`` fixture in a fixture function requested
  616. (transitively) by a test function."""
  617. def __init__(
  618. self,
  619. request: FixtureRequest,
  620. scope: Scope,
  621. param: Any,
  622. param_index: int,
  623. fixturedef: FixtureDef[object],
  624. *,
  625. _ispytest: bool = False,
  626. ) -> None:
  627. super().__init__(
  628. pyfuncitem=request._pyfuncitem,
  629. fixturename=fixturedef.argname,
  630. fixture_defs=request._fixture_defs,
  631. arg2fixturedefs=request._arg2fixturedefs,
  632. _ispytest=_ispytest,
  633. )
  634. self._parent_request: Final[FixtureRequest] = request
  635. self._scope_field: Final = scope
  636. self._fixturedef: Final[FixtureDef[object]] = fixturedef
  637. if param is not NOTSET:
  638. self.param = param
  639. self.param_index: Final = param_index
  640. def __repr__(self) -> str:
  641. return f"<SubRequest {self.fixturename!r} for {self._pyfuncitem!r}>"
  642. @property
  643. def _scope(self) -> Scope:
  644. return self._scope_field
  645. @property
  646. def node(self):
  647. scope = self._scope
  648. if scope is Scope.Function:
  649. # This might also be a non-function Item despite its attribute name.
  650. node: nodes.Node | None = self._pyfuncitem
  651. elif scope is Scope.Package:
  652. node = get_scope_package(self._pyfuncitem, self._fixturedef)
  653. else:
  654. node = get_scope_node(self._pyfuncitem, scope)
  655. if node is None and scope is Scope.Class:
  656. # Fallback to function item itself.
  657. node = self._pyfuncitem
  658. assert node, (
  659. f'Could not obtain a node for scope "{scope}" for function {self._pyfuncitem!r}'
  660. )
  661. return node
  662. def _check_scope(
  663. self,
  664. requested_fixturedef: FixtureDef[object],
  665. requested_scope: Scope,
  666. ) -> None:
  667. if self._scope > requested_scope:
  668. # Try to report something helpful.
  669. argname = requested_fixturedef.argname
  670. fixture_stack = "\n".join(
  671. self._format_fixturedef_line(fixturedef)
  672. for fixturedef in self._get_fixturestack()
  673. )
  674. requested_fixture = self._format_fixturedef_line(requested_fixturedef)
  675. fail(
  676. f"ScopeMismatch: You tried to access the {requested_scope.value} scoped "
  677. f"fixture {argname} with a {self._scope.value} scoped request object. "
  678. f"Requesting fixture stack:\n{fixture_stack}\n"
  679. f"Requested fixture:\n{requested_fixture}",
  680. pytrace=False,
  681. )
  682. def _format_fixturedef_line(self, fixturedef: FixtureDef[object]) -> str:
  683. factory = fixturedef.func
  684. path, lineno = getfslineno(factory)
  685. if isinstance(path, Path):
  686. path = bestrelpath(self._pyfuncitem.session.path, path)
  687. sig = signature(factory)
  688. return f"{path}:{lineno + 1}: def {factory.__name__}{sig}"
  689. def addfinalizer(self, finalizer: Callable[[], object]) -> None:
  690. self._fixturedef.addfinalizer(finalizer)
  691. @final
  692. class FixtureLookupError(LookupError):
  693. """Could not return a requested fixture (missing or invalid)."""
  694. def __init__(
  695. self, argname: str | None, request: FixtureRequest, msg: str | None = None
  696. ) -> None:
  697. self.argname = argname
  698. self.request = request
  699. self.fixturestack = request._get_fixturestack()
  700. self.msg = msg
  701. def formatrepr(self) -> FixtureLookupErrorRepr:
  702. tblines: list[str] = []
  703. addline = tblines.append
  704. stack = [self.request._pyfuncitem.obj]
  705. stack.extend(map(lambda x: x.func, self.fixturestack))
  706. msg = self.msg
  707. # This function currently makes an assumption that a non-None msg means we
  708. # have a non-empty `self.fixturestack`. This is currently true, but if
  709. # somebody at some point want to extend the use of FixtureLookupError to
  710. # new cases it might break.
  711. # Add the assert to make it clearer to developer that this will fail, otherwise
  712. # it crashes because `fspath` does not get set due to `stack` being empty.
  713. assert self.msg is None or self.fixturestack, (
  714. "formatrepr assumptions broken, rewrite it to handle it"
  715. )
  716. if msg is not None:
  717. # The last fixture raise an error, let's present
  718. # it at the requesting side.
  719. stack = stack[:-1]
  720. for function in stack:
  721. fspath, lineno = getfslineno(function)
  722. try:
  723. lines, _ = inspect.getsourcelines(get_real_func(function))
  724. except (OSError, IndexError, TypeError):
  725. error_msg = "file %s, line %s: source code not available"
  726. addline(error_msg % (fspath, lineno + 1))
  727. else:
  728. addline(f"file {fspath}, line {lineno + 1}")
  729. for i, line in enumerate(lines):
  730. line = line.rstrip()
  731. addline(" " + line)
  732. if line.lstrip().startswith("def"):
  733. break
  734. if msg is None:
  735. fm = self.request._fixturemanager
  736. available = set()
  737. parent = self.request._pyfuncitem.parent
  738. assert parent is not None
  739. for name, fixturedefs in fm._arg2fixturedefs.items():
  740. faclist = list(fm._matchfactories(fixturedefs, parent))
  741. if faclist:
  742. available.add(name)
  743. if self.argname in available:
  744. msg = (
  745. f" recursive dependency involving fixture '{self.argname}' detected"
  746. )
  747. else:
  748. msg = f"fixture '{self.argname}' not found"
  749. msg += "\n available fixtures: {}".format(", ".join(sorted(available)))
  750. msg += "\n use 'pytest --fixtures [testpath]' for help on them."
  751. return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
  752. class FixtureLookupErrorRepr(TerminalRepr):
  753. def __init__(
  754. self,
  755. filename: str | os.PathLike[str],
  756. firstlineno: int,
  757. tblines: Sequence[str],
  758. errorstring: str,
  759. argname: str | None,
  760. ) -> None:
  761. self.tblines = tblines
  762. self.errorstring = errorstring
  763. self.filename = filename
  764. self.firstlineno = firstlineno
  765. self.argname = argname
  766. def toterminal(self, tw: TerminalWriter) -> None:
  767. # tw.line("FixtureLookupError: %s" %(self.argname), red=True)
  768. for tbline in self.tblines:
  769. tw.line(tbline.rstrip())
  770. lines = self.errorstring.split("\n")
  771. if lines:
  772. tw.line(
  773. f"{FormattedExcinfo.fail_marker} {lines[0].strip()}",
  774. red=True,
  775. )
  776. for line in lines[1:]:
  777. tw.line(
  778. f"{FormattedExcinfo.flow_marker} {line.strip()}",
  779. red=True,
  780. )
  781. tw.line()
  782. tw.line(f"{os.fspath(self.filename)}:{self.firstlineno + 1}")
  783. def call_fixture_func(
  784. fixturefunc: _FixtureFunc[FixtureValue], request: FixtureRequest, kwargs
  785. ) -> FixtureValue:
  786. if inspect.isgeneratorfunction(fixturefunc):
  787. fixturefunc = cast(Callable[..., Generator[FixtureValue]], fixturefunc)
  788. generator = fixturefunc(**kwargs)
  789. try:
  790. fixture_result = next(generator)
  791. except StopIteration:
  792. raise ValueError(f"{request.fixturename} did not yield a value") from None
  793. finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)
  794. request.addfinalizer(finalizer)
  795. else:
  796. fixturefunc = cast(Callable[..., FixtureValue], fixturefunc)
  797. fixture_result = fixturefunc(**kwargs)
  798. return fixture_result
  799. def _teardown_yield_fixture(fixturefunc, it) -> None:
  800. """Execute the teardown of a fixture function by advancing the iterator
  801. after the yield and ensure the iteration ends (if not it means there is
  802. more than one yield in the function)."""
  803. try:
  804. next(it)
  805. except StopIteration:
  806. pass
  807. else:
  808. fs, lineno = getfslineno(fixturefunc)
  809. fail(
  810. f"fixture function has more than one 'yield':\n\n"
  811. f"{Source(fixturefunc).indent()}\n"
  812. f"{fs}:{lineno + 1}",
  813. pytrace=False,
  814. )
  815. def _eval_scope_callable(
  816. scope_callable: Callable[[str, Config], _ScopeName],
  817. fixture_name: str,
  818. config: Config,
  819. ) -> _ScopeName:
  820. try:
  821. # Type ignored because there is no typing mechanism to specify
  822. # keyword arguments, currently.
  823. result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg]
  824. except Exception as e:
  825. raise TypeError(
  826. f"Error evaluating {scope_callable} while defining fixture '{fixture_name}'.\n"
  827. "Expected a function with the signature (*, fixture_name, config)"
  828. ) from e
  829. if not isinstance(result, str):
  830. fail(
  831. f"Expected {scope_callable} to return a 'str' while defining fixture '{fixture_name}', but it returned:\n"
  832. f"{result!r}",
  833. pytrace=False,
  834. )
  835. return result
  836. class FixtureDef(Generic[FixtureValue]):
  837. """A container for a fixture definition.
  838. Note: At this time, only explicitly documented fields and methods are
  839. considered public stable API.
  840. """
  841. def __init__(
  842. self,
  843. config: Config,
  844. baseid: str | None,
  845. argname: str,
  846. func: _FixtureFunc[FixtureValue],
  847. scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] | None,
  848. params: Sequence[object] | None,
  849. ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None,
  850. *,
  851. _ispytest: bool = False,
  852. # only used in a deprecationwarning msg, can be removed in pytest9
  853. _autouse: bool = False,
  854. ) -> None:
  855. check_ispytest(_ispytest)
  856. # The "base" node ID for the fixture.
  857. #
  858. # This is a node ID prefix. A fixture is only available to a node (e.g.
  859. # a `Function` item) if the fixture's baseid is a nodeid of a parent of
  860. # node.
  861. #
  862. # For a fixture found in a Collector's object (e.g. a `Module`s module,
  863. # a `Class`'s class), the baseid is the Collector's nodeid.
  864. #
  865. # For a fixture found in a conftest plugin, the baseid is the conftest's
  866. # directory path relative to the rootdir.
  867. #
  868. # For other plugins, the baseid is the empty string (always matches).
  869. self.baseid: Final = baseid or ""
  870. # Whether the fixture was found from a node or a conftest in the
  871. # collection tree. Will be false for fixtures defined in non-conftest
  872. # plugins.
  873. self.has_location: Final = baseid is not None
  874. # The fixture factory function.
  875. self.func: Final = func
  876. # The name by which the fixture may be requested.
  877. self.argname: Final = argname
  878. if scope is None:
  879. scope = Scope.Function
  880. elif callable(scope):
  881. scope = _eval_scope_callable(scope, argname, config)
  882. if isinstance(scope, str):
  883. scope = Scope.from_user(
  884. scope, descr=f"Fixture '{func.__name__}'", where=baseid
  885. )
  886. self._scope: Final = scope
  887. # If the fixture is directly parametrized, the parameter values.
  888. self.params: Final = params
  889. # If the fixture is directly parametrized, a tuple of explicit IDs to
  890. # assign to the parameter values, or a callable to generate an ID given
  891. # a parameter value.
  892. self.ids: Final = ids
  893. # The names requested by the fixtures.
  894. self.argnames: Final = getfuncargnames(func, name=argname)
  895. # If the fixture was executed, the current value of the fixture.
  896. # Can change if the fixture is executed with different parameters.
  897. self.cached_result: _FixtureCachedResult[FixtureValue] | None = None
  898. self._finalizers: Final[list[Callable[[], object]]] = []
  899. # only used to emit a deprecationwarning, can be removed in pytest9
  900. self._autouse = _autouse
  901. @property
  902. def scope(self) -> _ScopeName:
  903. """Scope string, one of "function", "class", "module", "package", "session"."""
  904. return self._scope.value
  905. def addfinalizer(self, finalizer: Callable[[], object]) -> None:
  906. self._finalizers.append(finalizer)
  907. def finish(self, request: SubRequest) -> None:
  908. exceptions: list[BaseException] = []
  909. while self._finalizers:
  910. fin = self._finalizers.pop()
  911. try:
  912. fin()
  913. except BaseException as e:
  914. exceptions.append(e)
  915. node = request.node
  916. node.ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
  917. # Even if finalization fails, we invalidate the cached fixture
  918. # value and remove all finalizers because they may be bound methods
  919. # which will keep instances alive.
  920. self.cached_result = None
  921. self._finalizers.clear()
  922. if len(exceptions) == 1:
  923. raise exceptions[0]
  924. elif len(exceptions) > 1:
  925. msg = f'errors while tearing down fixture "{self.argname}" of {node}'
  926. raise BaseExceptionGroup(msg, exceptions[::-1])
  927. def execute(self, request: SubRequest) -> FixtureValue:
  928. """Return the value of this fixture, executing it if not cached."""
  929. # Ensure that the dependent fixtures requested by this fixture are loaded.
  930. # This needs to be done before checking if we have a cached value, since
  931. # if a dependent fixture has their cache invalidated, e.g. due to
  932. # parametrization, they finalize themselves and fixtures depending on it
  933. # (which will likely include this fixture) setting `self.cached_result = None`.
  934. # See #4871
  935. requested_fixtures_that_should_finalize_us = []
  936. for argname in self.argnames:
  937. fixturedef = request._get_active_fixturedef(argname)
  938. # Saves requested fixtures in a list so we later can add our finalizer
  939. # to them, ensuring that if a requested fixture gets torn down we get torn
  940. # down first. This is generally handled by SetupState, but still currently
  941. # needed when this fixture is not parametrized but depends on a parametrized
  942. # fixture.
  943. requested_fixtures_that_should_finalize_us.append(fixturedef)
  944. # Check for (and return) cached value/exception.
  945. if self.cached_result is not None:
  946. request_cache_key = self.cache_key(request)
  947. cache_key = self.cached_result[1]
  948. try:
  949. # Attempt to make a normal == check: this might fail for objects
  950. # which do not implement the standard comparison (like numpy arrays -- #6497).
  951. cache_hit = bool(request_cache_key == cache_key)
  952. except (ValueError, RuntimeError):
  953. # If the comparison raises, use 'is' as fallback.
  954. cache_hit = request_cache_key is cache_key
  955. if cache_hit:
  956. if self.cached_result[2] is not None:
  957. exc, exc_tb = self.cached_result[2]
  958. raise exc.with_traceback(exc_tb)
  959. else:
  960. return self.cached_result[0]
  961. # We have a previous but differently parametrized fixture instance
  962. # so we need to tear it down before creating a new one.
  963. self.finish(request)
  964. assert self.cached_result is None
  965. # Add finalizer to requested fixtures we saved previously.
  966. # We make sure to do this after checking for cached value to avoid
  967. # adding our finalizer multiple times. (#12135)
  968. finalizer = functools.partial(self.finish, request=request)
  969. for parent_fixture in requested_fixtures_that_should_finalize_us:
  970. parent_fixture.addfinalizer(finalizer)
  971. ihook = request.node.ihook
  972. try:
  973. # Setup the fixture, run the code in it, and cache the value
  974. # in self.cached_result.
  975. result: FixtureValue = ihook.pytest_fixture_setup(
  976. fixturedef=self, request=request
  977. )
  978. finally:
  979. # Schedule our finalizer, even if the setup failed.
  980. request.node.addfinalizer(finalizer)
  981. return result
  982. def cache_key(self, request: SubRequest) -> object:
  983. return getattr(request, "param", None)
  984. def __repr__(self) -> str:
  985. return f"<FixtureDef argname={self.argname!r} scope={self.scope!r} baseid={self.baseid!r}>"
  986. class RequestFixtureDef(FixtureDef[FixtureRequest]):
  987. """A custom FixtureDef for the special "request" fixture.
  988. A new one is generated on-demand whenever "request" is requested.
  989. """
  990. def __init__(self, request: FixtureRequest) -> None:
  991. super().__init__(
  992. config=request.config,
  993. baseid=None,
  994. argname="request",
  995. func=lambda: request,
  996. scope=Scope.Function,
  997. params=None,
  998. _ispytest=True,
  999. )
  1000. self.cached_result = (request, [0], None)
  1001. def addfinalizer(self, finalizer: Callable[[], object]) -> None:
  1002. pass
  1003. def resolve_fixture_function(
  1004. fixturedef: FixtureDef[FixtureValue], request: FixtureRequest
  1005. ) -> _FixtureFunc[FixtureValue]:
  1006. """Get the actual callable that can be called to obtain the fixture
  1007. value."""
  1008. fixturefunc = fixturedef.func
  1009. # The fixture function needs to be bound to the actual
  1010. # request.instance so that code working with "fixturedef" behaves
  1011. # as expected.
  1012. instance = request.instance
  1013. if instance is not None:
  1014. # Handle the case where fixture is defined not in a test class, but some other class
  1015. # (for example a plugin class with a fixture), see #2270.
  1016. if hasattr(fixturefunc, "__self__") and not isinstance(
  1017. instance,
  1018. fixturefunc.__self__.__class__,
  1019. ):
  1020. return fixturefunc
  1021. fixturefunc = getimfunc(fixturedef.func)
  1022. if fixturefunc != fixturedef.func:
  1023. fixturefunc = fixturefunc.__get__(instance)
  1024. return fixturefunc
  1025. def pytest_fixture_setup(
  1026. fixturedef: FixtureDef[FixtureValue], request: SubRequest
  1027. ) -> FixtureValue:
  1028. """Execution of fixture setup."""
  1029. kwargs = {}
  1030. for argname in fixturedef.argnames:
  1031. kwargs[argname] = request.getfixturevalue(argname)
  1032. fixturefunc = resolve_fixture_function(fixturedef, request)
  1033. my_cache_key = fixturedef.cache_key(request)
  1034. if inspect.isasyncgenfunction(fixturefunc) or inspect.iscoroutinefunction(
  1035. fixturefunc
  1036. ):
  1037. auto_str = " with autouse=True" if fixturedef._autouse else ""
  1038. warnings.warn(
  1039. PytestRemovedIn9Warning(
  1040. f"{request.node.name!r} requested an async fixture "
  1041. f"{request.fixturename!r}{auto_str}, with no plugin or hook that "
  1042. "handled it. This is usually an error, as pytest does not natively "
  1043. "support it. "
  1044. "This will turn into an error in pytest 9.\n"
  1045. "See: https://docs.pytest.org/en/stable/deprecations.html#sync-test-depending-on-async-fixture"
  1046. ),
  1047. # no stacklevel will point at users code, so we just point here
  1048. stacklevel=1,
  1049. )
  1050. try:
  1051. result = call_fixture_func(fixturefunc, request, kwargs)
  1052. except TEST_OUTCOME as e:
  1053. if isinstance(e, skip.Exception):
  1054. # The test requested a fixture which caused a skip.
  1055. # Don't show the fixture as the skip location, as then the user
  1056. # wouldn't know which test skipped.
  1057. e._use_item_location = True
  1058. fixturedef.cached_result = (None, my_cache_key, (e, e.__traceback__))
  1059. raise
  1060. fixturedef.cached_result = (result, my_cache_key, None)
  1061. return result
  1062. @final
  1063. @dataclasses.dataclass(frozen=True)
  1064. class FixtureFunctionMarker:
  1065. scope: _ScopeName | Callable[[str, Config], _ScopeName]
  1066. params: tuple[object, ...] | None
  1067. autouse: bool = False
  1068. ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None
  1069. name: str | None = None
  1070. _ispytest: dataclasses.InitVar[bool] = False
  1071. def __post_init__(self, _ispytest: bool) -> None:
  1072. check_ispytest(_ispytest)
  1073. def __call__(self, function: FixtureFunction) -> FixtureFunctionDefinition:
  1074. if inspect.isclass(function):
  1075. raise ValueError("class fixtures not supported (maybe in the future)")
  1076. if isinstance(function, FixtureFunctionDefinition):
  1077. raise ValueError(
  1078. f"@pytest.fixture is being applied more than once to the same function {function.__name__!r}"
  1079. )
  1080. if hasattr(function, "pytestmark"):
  1081. warnings.warn(MARKED_FIXTURE, stacklevel=2)
  1082. fixture_definition = FixtureFunctionDefinition(
  1083. function=function, fixture_function_marker=self, _ispytest=True
  1084. )
  1085. name = self.name or function.__name__
  1086. if name == "request":
  1087. location = getlocation(function)
  1088. fail(
  1089. f"'request' is a reserved word for fixtures, use another name:\n {location}",
  1090. pytrace=False,
  1091. )
  1092. return fixture_definition
  1093. # TODO: paramspec/return type annotation tracking and storing
  1094. class FixtureFunctionDefinition:
  1095. def __init__(
  1096. self,
  1097. *,
  1098. function: Callable[..., Any],
  1099. fixture_function_marker: FixtureFunctionMarker,
  1100. instance: object | None = None,
  1101. _ispytest: bool = False,
  1102. ) -> None:
  1103. check_ispytest(_ispytest)
  1104. self.name = fixture_function_marker.name or function.__name__
  1105. # In order to show the function that this fixture contains in messages.
  1106. # Set the __name__ to be same as the function __name__ or the given fixture name.
  1107. self.__name__ = self.name
  1108. self._fixture_function_marker = fixture_function_marker
  1109. if instance is not None:
  1110. self._fixture_function = cast(
  1111. Callable[..., Any], function.__get__(instance)
  1112. )
  1113. else:
  1114. self._fixture_function = function
  1115. functools.update_wrapper(self, function)
  1116. def __repr__(self) -> str:
  1117. return f"<pytest_fixture({self._fixture_function})>"
  1118. def __get__(self, instance, owner=None):
  1119. """Behave like a method if the function it was applied to was a method."""
  1120. return FixtureFunctionDefinition(
  1121. function=self._fixture_function,
  1122. fixture_function_marker=self._fixture_function_marker,
  1123. instance=instance,
  1124. _ispytest=True,
  1125. )
  1126. def __call__(self, *args: Any, **kwds: Any) -> Any:
  1127. message = (
  1128. f'Fixture "{self.name}" called directly. Fixtures are not meant to be called directly,\n'
  1129. "but are created automatically when test functions request them as parameters.\n"
  1130. "See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n"
  1131. "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly"
  1132. )
  1133. fail(message, pytrace=False)
  1134. def _get_wrapped_function(self) -> Callable[..., Any]:
  1135. return self._fixture_function
  1136. @overload
  1137. def fixture(
  1138. fixture_function: Callable[..., object],
  1139. *,
  1140. scope: _ScopeName | Callable[[str, Config], _ScopeName] = ...,
  1141. params: Iterable[object] | None = ...,
  1142. autouse: bool = ...,
  1143. ids: Sequence[object | None] | Callable[[Any], object | None] | None = ...,
  1144. name: str | None = ...,
  1145. ) -> FixtureFunctionDefinition: ...
  1146. @overload
  1147. def fixture(
  1148. fixture_function: None = ...,
  1149. *,
  1150. scope: _ScopeName | Callable[[str, Config], _ScopeName] = ...,
  1151. params: Iterable[object] | None = ...,
  1152. autouse: bool = ...,
  1153. ids: Sequence[object | None] | Callable[[Any], object | None] | None = ...,
  1154. name: str | None = None,
  1155. ) -> FixtureFunctionMarker: ...
  1156. def fixture(
  1157. fixture_function: FixtureFunction | None = None,
  1158. *,
  1159. scope: _ScopeName | Callable[[str, Config], _ScopeName] = "function",
  1160. params: Iterable[object] | None = None,
  1161. autouse: bool = False,
  1162. ids: Sequence[object | None] | Callable[[Any], object | None] | None = None,
  1163. name: str | None = None,
  1164. ) -> FixtureFunctionMarker | FixtureFunctionDefinition:
  1165. """Decorator to mark a fixture factory function.
  1166. This decorator can be used, with or without parameters, to define a
  1167. fixture function.
  1168. The name of the fixture function can later be referenced to cause its
  1169. invocation ahead of running tests: test modules or classes can use the
  1170. ``pytest.mark.usefixtures(fixturename)`` marker.
  1171. Test functions can directly use fixture names as input arguments in which
  1172. case the fixture instance returned from the fixture function will be
  1173. injected.
  1174. Fixtures can provide their values to test functions using ``return`` or
  1175. ``yield`` statements. When using ``yield`` the code block after the
  1176. ``yield`` statement is executed as teardown code regardless of the test
  1177. outcome, and must yield exactly once.
  1178. :param scope:
  1179. The scope for which this fixture is shared; one of ``"function"``
  1180. (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``.
  1181. This parameter may also be a callable which receives ``(fixture_name, config)``
  1182. as parameters, and must return a ``str`` with one of the values mentioned above.
  1183. See :ref:`dynamic scope` in the docs for more information.
  1184. :param params:
  1185. An optional list of parameters which will cause multiple invocations
  1186. of the fixture function and all of the tests using it. The current
  1187. parameter is available in ``request.param``.
  1188. :param autouse:
  1189. If True, the fixture func is activated for all tests that can see it.
  1190. If False (the default), an explicit reference is needed to activate
  1191. the fixture.
  1192. :param ids:
  1193. Sequence of ids each corresponding to the params so that they are
  1194. part of the test id. If no ids are provided they will be generated
  1195. automatically from the params.
  1196. :param name:
  1197. The name of the fixture. This defaults to the name of the decorated
  1198. function. If a fixture is used in the same module in which it is
  1199. defined, the function name of the fixture will be shadowed by the
  1200. function arg that requests the fixture; one way to resolve this is to
  1201. name the decorated function ``fixture_<fixturename>`` and then use
  1202. ``@pytest.fixture(name='<fixturename>')``.
  1203. """
  1204. fixture_marker = FixtureFunctionMarker(
  1205. scope=scope,
  1206. params=tuple(params) if params is not None else None,
  1207. autouse=autouse,
  1208. ids=None if ids is None else ids if callable(ids) else tuple(ids),
  1209. name=name,
  1210. _ispytest=True,
  1211. )
  1212. # Direct decoration.
  1213. if fixture_function:
  1214. return fixture_marker(fixture_function)
  1215. return fixture_marker
  1216. def yield_fixture(
  1217. fixture_function=None,
  1218. *args,
  1219. scope="function",
  1220. params=None,
  1221. autouse=False,
  1222. ids=None,
  1223. name=None,
  1224. ):
  1225. """(Return a) decorator to mark a yield-fixture factory function.
  1226. .. deprecated:: 3.0
  1227. Use :py:func:`pytest.fixture` directly instead.
  1228. """
  1229. warnings.warn(YIELD_FIXTURE, stacklevel=2)
  1230. return fixture(
  1231. fixture_function,
  1232. *args,
  1233. scope=scope,
  1234. params=params,
  1235. autouse=autouse,
  1236. ids=ids,
  1237. name=name,
  1238. )
  1239. @fixture(scope="session")
  1240. def pytestconfig(request: FixtureRequest) -> Config:
  1241. """Session-scoped fixture that returns the session's :class:`pytest.Config`
  1242. object.
  1243. Example::
  1244. def test_foo(pytestconfig):
  1245. if pytestconfig.get_verbosity() > 0:
  1246. ...
  1247. """
  1248. return request.config
  1249. def pytest_addoption(parser: Parser) -> None:
  1250. parser.addini(
  1251. "usefixtures",
  1252. type="args",
  1253. default=[],
  1254. help="List of default fixtures to be used with this project",
  1255. )
  1256. group = parser.getgroup("general")
  1257. group.addoption(
  1258. "--fixtures",
  1259. "--funcargs",
  1260. action="store_true",
  1261. dest="showfixtures",
  1262. default=False,
  1263. help="Show available fixtures, sorted by plugin appearance "
  1264. "(fixtures with leading '_' are only shown with '-v')",
  1265. )
  1266. group.addoption(
  1267. "--fixtures-per-test",
  1268. action="store_true",
  1269. dest="show_fixtures_per_test",
  1270. default=False,
  1271. help="Show fixtures per test",
  1272. )
  1273. def pytest_cmdline_main(config: Config) -> int | ExitCode | None:
  1274. if config.option.showfixtures:
  1275. showfixtures(config)
  1276. return 0
  1277. if config.option.show_fixtures_per_test:
  1278. show_fixtures_per_test(config)
  1279. return 0
  1280. return None
  1281. def _get_direct_parametrize_args(node: nodes.Node) -> set[str]:
  1282. """Return all direct parametrization arguments of a node, so we don't
  1283. mistake them for fixtures.
  1284. Check https://github.com/pytest-dev/pytest/issues/5036.
  1285. These things are done later as well when dealing with parametrization
  1286. so this could be improved.
  1287. """
  1288. parametrize_argnames: set[str] = set()
  1289. for marker in node.iter_markers(name="parametrize"):
  1290. if not marker.kwargs.get("indirect", False):
  1291. p_argnames, _ = ParameterSet._parse_parametrize_args(
  1292. *marker.args, **marker.kwargs
  1293. )
  1294. parametrize_argnames.update(p_argnames)
  1295. return parametrize_argnames
  1296. def deduplicate_names(*seqs: Iterable[str]) -> tuple[str, ...]:
  1297. """De-duplicate the sequence of names while keeping the original order."""
  1298. # Ideally we would use a set, but it does not preserve insertion order.
  1299. return tuple(dict.fromkeys(name for seq in seqs for name in seq))
  1300. class FixtureManager:
  1301. """pytest fixture definitions and information is stored and managed
  1302. from this class.
  1303. During collection fm.parsefactories() is called multiple times to parse
  1304. fixture function definitions into FixtureDef objects and internal
  1305. data structures.
  1306. During collection of test functions, metafunc-mechanics instantiate
  1307. a FuncFixtureInfo object which is cached per node/func-name.
  1308. This FuncFixtureInfo object is later retrieved by Function nodes
  1309. which themselves offer a fixturenames attribute.
  1310. The FuncFixtureInfo object holds information about fixtures and FixtureDefs
  1311. relevant for a particular function. An initial list of fixtures is
  1312. assembled like this:
  1313. - config-defined usefixtures
  1314. - autouse-marked fixtures along the collection chain up from the function
  1315. - usefixtures markers at module/class/function level
  1316. - test function funcargs
  1317. Subsequently the funcfixtureinfo.fixturenames attribute is computed
  1318. as the closure of the fixtures needed to setup the initial fixtures,
  1319. i.e. fixtures needed by fixture functions themselves are appended
  1320. to the fixturenames list.
  1321. Upon the test-setup phases all fixturenames are instantiated, retrieved
  1322. by a lookup of their FuncFixtureInfo.
  1323. """
  1324. def __init__(self, session: Session) -> None:
  1325. self.session = session
  1326. self.config: Config = session.config
  1327. # Maps a fixture name (argname) to all of the FixtureDefs in the test
  1328. # suite/plugins defined with this name. Populated by parsefactories().
  1329. # TODO: The order of the FixtureDefs list of each arg is significant,
  1330. # explain.
  1331. self._arg2fixturedefs: Final[dict[str, list[FixtureDef[Any]]]] = {}
  1332. self._holderobjseen: Final[set[object]] = set()
  1333. # A mapping from a nodeid to a list of autouse fixtures it defines.
  1334. self._nodeid_autousenames: Final[dict[str, list[str]]] = {
  1335. "": self.config.getini("usefixtures"),
  1336. }
  1337. session.config.pluginmanager.register(self, "funcmanage")
  1338. def getfixtureinfo(
  1339. self,
  1340. node: nodes.Item,
  1341. func: Callable[..., object] | None,
  1342. cls: type | None,
  1343. ) -> FuncFixtureInfo:
  1344. """Calculate the :class:`FuncFixtureInfo` for an item.
  1345. If ``func`` is None, or if the item sets an attribute
  1346. ``nofuncargs = True``, then ``func`` is not examined at all.
  1347. :param node:
  1348. The item requesting the fixtures.
  1349. :param func:
  1350. The item's function.
  1351. :param cls:
  1352. If the function is a method, the method's class.
  1353. """
  1354. if func is not None and not getattr(node, "nofuncargs", False):
  1355. argnames = getfuncargnames(func, name=node.name, cls=cls)
  1356. else:
  1357. argnames = ()
  1358. usefixturesnames = self._getusefixturesnames(node)
  1359. autousenames = self._getautousenames(node)
  1360. initialnames = deduplicate_names(autousenames, usefixturesnames, argnames)
  1361. direct_parametrize_args = _get_direct_parametrize_args(node)
  1362. names_closure, arg2fixturedefs = self.getfixtureclosure(
  1363. parentnode=node,
  1364. initialnames=initialnames,
  1365. ignore_args=direct_parametrize_args,
  1366. )
  1367. return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)
  1368. def pytest_plugin_registered(self, plugin: _PluggyPlugin, plugin_name: str) -> None:
  1369. # Fixtures defined in conftest plugins are only visible to within the
  1370. # conftest's directory. This is unlike fixtures in non-conftest plugins
  1371. # which have global visibility. So for conftests, construct the base
  1372. # nodeid from the plugin name (which is the conftest path).
  1373. if plugin_name and plugin_name.endswith("conftest.py"):
  1374. # Note: we explicitly do *not* use `plugin.__file__` here -- The
  1375. # difference is that plugin_name has the correct capitalization on
  1376. # case-insensitive systems (Windows) and other normalization issues
  1377. # (issue #11816).
  1378. conftestpath = absolutepath(plugin_name)
  1379. try:
  1380. nodeid = str(conftestpath.parent.relative_to(self.config.rootpath))
  1381. except ValueError:
  1382. nodeid = ""
  1383. if nodeid == ".":
  1384. nodeid = ""
  1385. if os.sep != nodes.SEP:
  1386. nodeid = nodeid.replace(os.sep, nodes.SEP)
  1387. else:
  1388. nodeid = None
  1389. self.parsefactories(plugin, nodeid)
  1390. def _getautousenames(self, node: nodes.Node) -> Iterator[str]:
  1391. """Return the names of autouse fixtures applicable to node."""
  1392. for parentnode in node.listchain():
  1393. basenames = self._nodeid_autousenames.get(parentnode.nodeid)
  1394. if basenames:
  1395. yield from basenames
  1396. def _getusefixturesnames(self, node: nodes.Item) -> Iterator[str]:
  1397. """Return the names of usefixtures fixtures applicable to node."""
  1398. for marker_node, mark in node.iter_markers_with_node(name="usefixtures"):
  1399. if not mark.args:
  1400. marker_node.warn(
  1401. PytestWarning(
  1402. f"usefixtures() in {node.nodeid} without arguments has no effect"
  1403. )
  1404. )
  1405. yield from mark.args
  1406. def getfixtureclosure(
  1407. self,
  1408. parentnode: nodes.Node,
  1409. initialnames: tuple[str, ...],
  1410. ignore_args: AbstractSet[str],
  1411. ) -> tuple[list[str], dict[str, Sequence[FixtureDef[Any]]]]:
  1412. # Collect the closure of all fixtures, starting with the given
  1413. # fixturenames as the initial set. As we have to visit all
  1414. # factory definitions anyway, we also return an arg2fixturedefs
  1415. # mapping so that the caller can reuse it and does not have
  1416. # to re-discover fixturedefs again for each fixturename
  1417. # (discovering matching fixtures for a given name/node is expensive).
  1418. fixturenames_closure = list(initialnames)
  1419. arg2fixturedefs: dict[str, Sequence[FixtureDef[Any]]] = {}
  1420. # Track the index for each fixture name in the simulated stack.
  1421. # Needed for handling override chains correctly, similar to _get_active_fixturedef.
  1422. # Using negative indices: -1 is the most specific (last), -2 is second to last, etc.
  1423. current_indices: dict[str, int] = {}
  1424. def process_argname(argname: str) -> None:
  1425. # Optimization: already processed this argname.
  1426. if current_indices.get(argname) == -1:
  1427. return
  1428. if argname not in fixturenames_closure:
  1429. fixturenames_closure.append(argname)
  1430. if argname in ignore_args:
  1431. return
  1432. fixturedefs = arg2fixturedefs.get(argname)
  1433. if not fixturedefs:
  1434. fixturedefs = self.getfixturedefs(argname, parentnode)
  1435. if not fixturedefs:
  1436. # Fixture not defined or not visible (will error during runtest).
  1437. return
  1438. arg2fixturedefs[argname] = fixturedefs
  1439. index = current_indices.get(argname, -1)
  1440. if -index > len(fixturedefs):
  1441. # Exhausted the override chain (will error during runtest).
  1442. return
  1443. fixturedef = fixturedefs[index]
  1444. current_indices[argname] = index - 1
  1445. for dep in fixturedef.argnames:
  1446. process_argname(dep)
  1447. current_indices[argname] = index
  1448. for name in initialnames:
  1449. process_argname(name)
  1450. def sort_by_scope(arg_name: str) -> Scope:
  1451. try:
  1452. fixturedefs = arg2fixturedefs[arg_name]
  1453. except KeyError:
  1454. return Scope.Function
  1455. else:
  1456. return fixturedefs[-1]._scope
  1457. fixturenames_closure.sort(key=sort_by_scope, reverse=True)
  1458. return fixturenames_closure, arg2fixturedefs
  1459. def pytest_generate_tests(self, metafunc: Metafunc) -> None:
  1460. """Generate new tests based on parametrized fixtures used by the given metafunc"""
  1461. def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]:
  1462. args, _ = ParameterSet._parse_parametrize_args(*mark.args, **mark.kwargs)
  1463. return args
  1464. for argname in metafunc.fixturenames:
  1465. # Get the FixtureDefs for the argname.
  1466. fixture_defs = metafunc._arg2fixturedefs.get(argname)
  1467. if not fixture_defs:
  1468. # Will raise FixtureLookupError at setup time if not parametrized somewhere
  1469. # else (e.g @pytest.mark.parametrize)
  1470. continue
  1471. # If the test itself parametrizes using this argname, give it
  1472. # precedence.
  1473. if any(
  1474. argname in get_parametrize_mark_argnames(mark)
  1475. for mark in metafunc.definition.iter_markers("parametrize")
  1476. ):
  1477. continue
  1478. # In the common case we only look at the fixture def with the
  1479. # closest scope (last in the list). But if the fixture overrides
  1480. # another fixture, while requesting the super fixture, keep going
  1481. # in case the super fixture is parametrized (#1953).
  1482. for fixturedef in reversed(fixture_defs):
  1483. # Fixture is parametrized, apply it and stop.
  1484. if fixturedef.params is not None:
  1485. metafunc.parametrize(
  1486. argname,
  1487. fixturedef.params,
  1488. indirect=True,
  1489. scope=fixturedef.scope,
  1490. ids=fixturedef.ids,
  1491. )
  1492. break
  1493. # Not requesting the overridden super fixture, stop.
  1494. if argname not in fixturedef.argnames:
  1495. break
  1496. # Try next super fixture, if any.
  1497. def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> None:
  1498. # Separate parametrized setups.
  1499. items[:] = reorder_items(items)
  1500. def _register_fixture(
  1501. self,
  1502. *,
  1503. name: str,
  1504. func: _FixtureFunc[object],
  1505. nodeid: str | None,
  1506. scope: Scope | _ScopeName | Callable[[str, Config], _ScopeName] = "function",
  1507. params: Sequence[object] | None = None,
  1508. ids: tuple[object | None, ...] | Callable[[Any], object | None] | None = None,
  1509. autouse: bool = False,
  1510. ) -> None:
  1511. """Register a fixture
  1512. :param name:
  1513. The fixture's name.
  1514. :param func:
  1515. The fixture's implementation function.
  1516. :param nodeid:
  1517. The visibility of the fixture. The fixture will be available to the
  1518. node with this nodeid and its children in the collection tree.
  1519. None means that the fixture is visible to the entire collection tree,
  1520. e.g. a fixture defined for general use in a plugin.
  1521. :param scope:
  1522. The fixture's scope.
  1523. :param params:
  1524. The fixture's parametrization params.
  1525. :param ids:
  1526. The fixture's IDs.
  1527. :param autouse:
  1528. Whether this is an autouse fixture.
  1529. """
  1530. fixture_def = FixtureDef(
  1531. config=self.config,
  1532. baseid=nodeid,
  1533. argname=name,
  1534. func=func,
  1535. scope=scope,
  1536. params=params,
  1537. ids=ids,
  1538. _ispytest=True,
  1539. _autouse=autouse,
  1540. )
  1541. faclist = self._arg2fixturedefs.setdefault(name, [])
  1542. if fixture_def.has_location:
  1543. faclist.append(fixture_def)
  1544. else:
  1545. # fixturedefs with no location are at the front
  1546. # so this inserts the current fixturedef after the
  1547. # existing fixturedefs from external plugins but
  1548. # before the fixturedefs provided in conftests.
  1549. i = len([f for f in faclist if not f.has_location])
  1550. faclist.insert(i, fixture_def)
  1551. if autouse:
  1552. self._nodeid_autousenames.setdefault(nodeid or "", []).append(name)
  1553. @overload
  1554. def parsefactories(
  1555. self,
  1556. node_or_obj: nodes.Node,
  1557. ) -> None:
  1558. raise NotImplementedError()
  1559. @overload
  1560. def parsefactories(
  1561. self,
  1562. node_or_obj: object,
  1563. nodeid: str | None,
  1564. ) -> None:
  1565. raise NotImplementedError()
  1566. def parsefactories(
  1567. self,
  1568. node_or_obj: nodes.Node | object,
  1569. nodeid: str | NotSetType | None = NOTSET,
  1570. ) -> None:
  1571. """Collect fixtures from a collection node or object.
  1572. Found fixtures are parsed into `FixtureDef`s and saved.
  1573. If `node_or_object` is a collection node (with an underlying Python
  1574. object), the node's object is traversed and the node's nodeid is used to
  1575. determine the fixtures' visibility. `nodeid` must not be specified in
  1576. this case.
  1577. If `node_or_object` is an object (e.g. a plugin), the object is
  1578. traversed and the given `nodeid` is used to determine the fixtures'
  1579. visibility. `nodeid` must be specified in this case; None and "" mean
  1580. total visibility.
  1581. """
  1582. if nodeid is not NOTSET:
  1583. holderobj = node_or_obj
  1584. else:
  1585. assert isinstance(node_or_obj, nodes.Node)
  1586. holderobj = cast(object, node_or_obj.obj) # type: ignore[attr-defined]
  1587. assert isinstance(node_or_obj.nodeid, str)
  1588. nodeid = node_or_obj.nodeid
  1589. if holderobj in self._holderobjseen:
  1590. return
  1591. # Avoid accessing `@property` (and other descriptors) when iterating fixtures.
  1592. if not safe_isclass(holderobj) and not isinstance(holderobj, types.ModuleType):
  1593. holderobj_tp: object = type(holderobj)
  1594. else:
  1595. holderobj_tp = holderobj
  1596. self._holderobjseen.add(holderobj)
  1597. for name in dir(holderobj):
  1598. # The attribute can be an arbitrary descriptor, so the attribute
  1599. # access below can raise. safe_getattr() ignores such exceptions.
  1600. obj_ub = safe_getattr(holderobj_tp, name, None)
  1601. if type(obj_ub) is FixtureFunctionDefinition:
  1602. marker = obj_ub._fixture_function_marker
  1603. if marker.name:
  1604. fixture_name = marker.name
  1605. else:
  1606. fixture_name = name
  1607. # OK we know it is a fixture -- now safe to look up on the _instance_.
  1608. try:
  1609. obj = getattr(holderobj, name)
  1610. # if the fixture is named in the decorator we cannot find it in the module
  1611. except AttributeError:
  1612. obj = obj_ub
  1613. func = obj._get_wrapped_function()
  1614. self._register_fixture(
  1615. name=fixture_name,
  1616. nodeid=nodeid,
  1617. func=func,
  1618. scope=marker.scope,
  1619. params=marker.params,
  1620. ids=marker.ids,
  1621. autouse=marker.autouse,
  1622. )
  1623. def getfixturedefs(
  1624. self, argname: str, node: nodes.Node
  1625. ) -> Sequence[FixtureDef[Any]] | None:
  1626. """Get FixtureDefs for a fixture name which are applicable
  1627. to a given node.
  1628. Returns None if there are no fixtures at all defined with the given
  1629. name. (This is different from the case in which there are fixtures
  1630. with the given name, but none applicable to the node. In this case,
  1631. an empty result is returned).
  1632. :param argname: Name of the fixture to search for.
  1633. :param node: The requesting Node.
  1634. """
  1635. try:
  1636. fixturedefs = self._arg2fixturedefs[argname]
  1637. except KeyError:
  1638. return None
  1639. return tuple(self._matchfactories(fixturedefs, node))
  1640. def _matchfactories(
  1641. self, fixturedefs: Iterable[FixtureDef[Any]], node: nodes.Node
  1642. ) -> Iterator[FixtureDef[Any]]:
  1643. parentnodeids = {n.nodeid for n in node.iter_parents()}
  1644. for fixturedef in fixturedefs:
  1645. if fixturedef.baseid in parentnodeids:
  1646. yield fixturedef
  1647. def show_fixtures_per_test(config: Config) -> int | ExitCode:
  1648. from _pytest.main import wrap_session
  1649. return wrap_session(config, _show_fixtures_per_test)
  1650. _PYTEST_DIR = Path(_pytest.__file__).parent
  1651. def _pretty_fixture_path(invocation_dir: Path, func) -> str:
  1652. loc = Path(getlocation(func, invocation_dir))
  1653. prefix = Path("...", "_pytest")
  1654. try:
  1655. return str(prefix / loc.relative_to(_PYTEST_DIR))
  1656. except ValueError:
  1657. return bestrelpath(invocation_dir, loc)
  1658. def _show_fixtures_per_test(config: Config, session: Session) -> None:
  1659. import _pytest.config
  1660. session.perform_collect()
  1661. invocation_dir = config.invocation_params.dir
  1662. tw = _pytest.config.create_terminal_writer(config)
  1663. verbose = config.get_verbosity()
  1664. def get_best_relpath(func) -> str:
  1665. loc = getlocation(func, invocation_dir)
  1666. return bestrelpath(invocation_dir, Path(loc))
  1667. def write_fixture(fixture_def: FixtureDef[object]) -> None:
  1668. argname = fixture_def.argname
  1669. if verbose <= 0 and argname.startswith("_"):
  1670. return
  1671. prettypath = _pretty_fixture_path(invocation_dir, fixture_def.func)
  1672. tw.write(f"{argname}", green=True)
  1673. tw.write(f" -- {prettypath}", yellow=True)
  1674. tw.write("\n")
  1675. fixture_doc = inspect.getdoc(fixture_def.func)
  1676. if fixture_doc:
  1677. write_docstring(
  1678. tw,
  1679. fixture_doc.split("\n\n", maxsplit=1)[0]
  1680. if verbose <= 0
  1681. else fixture_doc,
  1682. )
  1683. else:
  1684. tw.line(" no docstring available", red=True)
  1685. def write_item(item: nodes.Item) -> None:
  1686. # Not all items have _fixtureinfo attribute.
  1687. info: FuncFixtureInfo | None = getattr(item, "_fixtureinfo", None)
  1688. if info is None or not info.name2fixturedefs:
  1689. # This test item does not use any fixtures.
  1690. return
  1691. tw.line()
  1692. tw.sep("-", f"fixtures used by {item.name}")
  1693. # TODO: Fix this type ignore.
  1694. tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined]
  1695. # dict key not used in loop but needed for sorting.
  1696. for _, fixturedefs in sorted(info.name2fixturedefs.items()):
  1697. assert fixturedefs is not None
  1698. if not fixturedefs:
  1699. continue
  1700. # Last item is expected to be the one used by the test item.
  1701. write_fixture(fixturedefs[-1])
  1702. for session_item in session.items:
  1703. write_item(session_item)
  1704. def showfixtures(config: Config) -> int | ExitCode:
  1705. from _pytest.main import wrap_session
  1706. return wrap_session(config, _showfixtures_main)
  1707. def _showfixtures_main(config: Config, session: Session) -> None:
  1708. import _pytest.config
  1709. session.perform_collect()
  1710. invocation_dir = config.invocation_params.dir
  1711. tw = _pytest.config.create_terminal_writer(config)
  1712. verbose = config.get_verbosity()
  1713. fm = session._fixturemanager
  1714. available = []
  1715. seen: set[tuple[str, str]] = set()
  1716. for argname, fixturedefs in fm._arg2fixturedefs.items():
  1717. assert fixturedefs is not None
  1718. if not fixturedefs:
  1719. continue
  1720. for fixturedef in fixturedefs:
  1721. loc = getlocation(fixturedef.func, invocation_dir)
  1722. if (fixturedef.argname, loc) in seen:
  1723. continue
  1724. seen.add((fixturedef.argname, loc))
  1725. available.append(
  1726. (
  1727. len(fixturedef.baseid),
  1728. fixturedef.func.__module__,
  1729. _pretty_fixture_path(invocation_dir, fixturedef.func),
  1730. fixturedef.argname,
  1731. fixturedef,
  1732. )
  1733. )
  1734. available.sort()
  1735. currentmodule = None
  1736. for baseid, module, prettypath, argname, fixturedef in available:
  1737. if currentmodule != module:
  1738. if not module.startswith("_pytest."):
  1739. tw.line()
  1740. tw.sep("-", f"fixtures defined from {module}")
  1741. currentmodule = module
  1742. if verbose <= 0 and argname.startswith("_"):
  1743. continue
  1744. tw.write(f"{argname}", green=True)
  1745. if fixturedef.scope != "function":
  1746. tw.write(f" [{fixturedef.scope} scope]", cyan=True)
  1747. tw.write(f" -- {prettypath}", yellow=True)
  1748. tw.write("\n")
  1749. doc = inspect.getdoc(fixturedef.func)
  1750. if doc:
  1751. write_docstring(
  1752. tw, doc.split("\n\n", maxsplit=1)[0] if verbose <= 0 else doc
  1753. )
  1754. else:
  1755. tw.line(" no docstring available", red=True)
  1756. tw.line()
  1757. def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None:
  1758. for line in doc.split("\n"):
  1759. tw.line(indent + line)