Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/_pytest/fixtures.py : 44%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import functools
2import inspect
3import itertools
4import sys
5import warnings
6from collections import defaultdict
7from collections import deque
8from types import TracebackType
9from typing import Any
10from typing import Callable
11from typing import cast
12from typing import Dict
13from typing import Generator
14from typing import Generic
15from typing import Iterable
16from typing import Iterator
17from typing import List
18from typing import Optional
19from typing import Sequence
20from typing import Set
21from typing import Tuple
22from typing import TypeVar
23from typing import Union
25import attr
26import py
28import _pytest
29from _pytest._code import getfslineno
30from _pytest._code.code import FormattedExcinfo
31from _pytest._code.code import TerminalRepr
32from _pytest._io import TerminalWriter
33from _pytest.compat import _format_args
34from _pytest.compat import _PytestWrapper
35from _pytest.compat import get_real_func
36from _pytest.compat import get_real_method
37from _pytest.compat import getfuncargnames
38from _pytest.compat import getimfunc
39from _pytest.compat import getlocation
40from _pytest.compat import is_generator
41from _pytest.compat import NOTSET
42from _pytest.compat import order_preserving_dict
43from _pytest.compat import overload
44from _pytest.compat import safe_getattr
45from _pytest.compat import TYPE_CHECKING
46from _pytest.config import _PluggyPlugin
47from _pytest.config import Config
48from _pytest.config.argparsing import Parser
49from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
50from _pytest.deprecated import FUNCARGNAMES
51from _pytest.mark import ParameterSet
52from _pytest.outcomes import fail
53from _pytest.outcomes import TEST_OUTCOME
55if TYPE_CHECKING:
56 from typing import Deque
57 from typing import NoReturn
58 from typing import Type
59 from typing_extensions import Literal
61 from _pytest import nodes
62 from _pytest.main import Session
63 from _pytest.python import CallSpec2
64 from _pytest.python import Function
65 from _pytest.python import Metafunc
67 _Scope = Literal["session", "package", "module", "class", "function"]
70# The value of the fixture -- return/yield of the fixture function (type variable).
71_FixtureValue = TypeVar("_FixtureValue")
72# The type of the fixture function (type variable).
73_FixtureFunction = TypeVar("_FixtureFunction", bound=Callable[..., object])
74# The type of a fixture function (type alias generic in fixture value).
75_FixtureFunc = Union[
76 Callable[..., _FixtureValue], Callable[..., Generator[_FixtureValue, None, None]]
77]
78# The type of FixtureDef.cached_result (type alias generic in fixture value).
79_FixtureCachedResult = Union[
80 Tuple[
81 # The result.
82 _FixtureValue,
83 # Cache key.
84 object,
85 None,
86 ],
87 Tuple[
88 None,
89 # Cache key.
90 object,
91 # Exc info if raised.
92 Tuple["Type[BaseException]", BaseException, TracebackType],
93 ],
94]
97@attr.s(frozen=True)
98class PseudoFixtureDef:
99 cached_result = attr.ib(type="_FixtureCachedResult")
100 scope = attr.ib(type="_Scope")
103def pytest_sessionstart(session: "Session") -> None:
104 import _pytest.python
105 import _pytest.nodes
107 scopename2class.update(
108 {
109 "package": _pytest.python.Package,
110 "class": _pytest.python.Class,
111 "module": _pytest.python.Module,
112 "function": _pytest.nodes.Item,
113 "session": _pytest.main.Session,
114 }
115 )
116 session._fixturemanager = FixtureManager(session)
119scopename2class = {} # type: Dict[str, Type[nodes.Node]]
121scope2props = dict(session=()) # type: Dict[str, Tuple[str, ...]]
122scope2props["package"] = ("fspath",)
123scope2props["module"] = ("fspath", "module")
124scope2props["class"] = scope2props["module"] + ("cls",)
125scope2props["instance"] = scope2props["class"] + ("instance",)
126scope2props["function"] = scope2props["instance"] + ("function", "keywords")
129def scopeproperty(name=None, doc=None):
130 def decoratescope(func):
131 scopename = name or func.__name__
133 def provide(self):
134 if func.__name__ in scope2props[self.scope]:
135 return func(self)
136 raise AttributeError(
137 "{} not available in {}-scoped context".format(scopename, self.scope)
138 )
140 return property(provide, None, None, func.__doc__)
142 return decoratescope
145def get_scope_package(node, fixturedef: "FixtureDef"):
146 import pytest
148 cls = pytest.Package
149 current = node
150 fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py")
151 while current and (
152 type(current) is not cls or fixture_package_name != current.nodeid
153 ):
154 current = current.parent
155 if current is None:
156 return node.session
157 return current
160def get_scope_node(node, scope):
161 cls = scopename2class.get(scope)
162 if cls is None:
163 raise ValueError("unknown scope")
164 return node.getparent(cls)
167def add_funcarg_pseudo_fixture_def(
168 collector, metafunc: "Metafunc", fixturemanager: "FixtureManager"
169) -> None:
170 # this function will transform all collected calls to a functions
171 # if they use direct funcargs (i.e. direct parametrization)
172 # because we want later test execution to be able to rely on
173 # an existing FixtureDef structure for all arguments.
174 # XXX we can probably avoid this algorithm if we modify CallSpec2
175 # to directly care for creating the fixturedefs within its methods.
176 if not metafunc._calls[0].funcargs:
177 return # this function call does not have direct parametrization
178 # collect funcargs of all callspecs into a list of values
179 arg2params = {} # type: Dict[str, List[object]]
180 arg2scope = {} # type: Dict[str, _Scope]
181 for callspec in metafunc._calls:
182 for argname, argvalue in callspec.funcargs.items():
183 assert argname not in callspec.params
184 callspec.params[argname] = argvalue
185 arg2params_list = arg2params.setdefault(argname, [])
186 callspec.indices[argname] = len(arg2params_list)
187 arg2params_list.append(argvalue)
188 if argname not in arg2scope:
189 scopenum = callspec._arg2scopenum.get(argname, scopenum_function)
190 arg2scope[argname] = scopes[scopenum]
191 callspec.funcargs.clear()
193 # register artificial FixtureDef's so that later at test execution
194 # time we can rely on a proper FixtureDef to exist for fixture setup.
195 arg2fixturedefs = metafunc._arg2fixturedefs
196 for argname, valuelist in arg2params.items():
197 # if we have a scope that is higher than function we need
198 # to make sure we only ever create an according fixturedef on
199 # a per-scope basis. We thus store and cache the fixturedef on the
200 # node related to the scope.
201 scope = arg2scope[argname]
202 node = None
203 if scope != "function":
204 node = get_scope_node(collector, scope)
205 if node is None:
206 assert scope == "class" and isinstance(collector, _pytest.python.Module)
207 # use module-level collector for class-scope (for now)
208 node = collector
209 if node and argname in node._name2pseudofixturedef:
210 arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]]
211 else:
212 fixturedef = FixtureDef(
213 fixturemanager=fixturemanager,
214 baseid="",
215 argname=argname,
216 func=get_direct_param_fixture_func,
217 scope=arg2scope[argname],
218 params=valuelist,
219 unittest=False,
220 ids=None,
221 )
222 arg2fixturedefs[argname] = [fixturedef]
223 if node is not None:
224 node._name2pseudofixturedef[argname] = fixturedef
227def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]:
228 """ return fixturemarker or None if it doesn't exist or raised
229 exceptions."""
230 try:
231 fixturemarker = getattr(
232 obj, "_pytestfixturefunction", None
233 ) # type: Optional[FixtureFunctionMarker]
234 except TEST_OUTCOME:
235 # some objects raise errors like request (from flask import request)
236 # we don't expect them to be fixture functions
237 return None
238 return fixturemarker
241# Parametrized fixture key, helper alias for code below.
242_Key = Tuple[object, ...]
245def get_parametrized_fixture_keys(item: "nodes.Item", scopenum: int) -> Iterator[_Key]:
246 """ return list of keys for all parametrized arguments which match
247 the specified scope. """
248 assert scopenum < scopenum_function # function
249 try:
250 callspec = item.callspec # type: ignore[attr-defined]
251 except AttributeError:
252 pass
253 else:
254 cs = callspec # type: CallSpec2
255 # cs.indices.items() is random order of argnames. Need to
256 # sort this so that different calls to
257 # get_parametrized_fixture_keys will be deterministic.
258 for argname, param_index in sorted(cs.indices.items()):
259 if cs._arg2scopenum[argname] != scopenum:
260 continue
261 if scopenum == 0: # session
262 key = (argname, param_index) # type: _Key
263 elif scopenum == 1: # package
264 key = (argname, param_index, item.fspath.dirpath())
265 elif scopenum == 2: # module
266 key = (argname, param_index, item.fspath)
267 elif scopenum == 3: # class
268 item_cls = item.cls # type: ignore[attr-defined]
269 key = (argname, param_index, item.fspath, item_cls)
270 yield key
273# algorithm for sorting on a per-parametrized resource setup basis
274# it is called for scopenum==0 (session) first and performs sorting
275# down to the lower scopes such as to minimize number of "high scope"
276# setups and teardowns
279def reorder_items(items: "Sequence[nodes.Item]") -> "List[nodes.Item]":
280 argkeys_cache = {} # type: Dict[int, Dict[nodes.Item, Dict[_Key, None]]]
281 items_by_argkey = {} # type: Dict[int, Dict[_Key, Deque[nodes.Item]]]
282 for scopenum in range(0, scopenum_function):
283 d = {} # type: Dict[nodes.Item, Dict[_Key, None]]
284 argkeys_cache[scopenum] = d
285 item_d = defaultdict(deque) # type: Dict[_Key, Deque[nodes.Item]]
286 items_by_argkey[scopenum] = item_d
287 for item in items:
288 # cast is a workaround for https://github.com/python/typeshed/issues/3800.
289 keys = cast(
290 "Dict[_Key, None]",
291 order_preserving_dict.fromkeys(
292 get_parametrized_fixture_keys(item, scopenum), None
293 ),
294 )
295 if keys:
296 d[item] = keys
297 for key in keys:
298 item_d[key].append(item)
299 # cast is a workaround for https://github.com/python/typeshed/issues/3800.
300 items_dict = cast(
301 "Dict[nodes.Item, None]", order_preserving_dict.fromkeys(items, None)
302 )
303 return list(reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, 0))
306def fix_cache_order(
307 item: "nodes.Item",
308 argkeys_cache: "Dict[int, Dict[nodes.Item, Dict[_Key, None]]]",
309 items_by_argkey: "Dict[int, Dict[_Key, Deque[nodes.Item]]]",
310) -> None:
311 for scopenum in range(0, scopenum_function):
312 for key in argkeys_cache[scopenum].get(item, []):
313 items_by_argkey[scopenum][key].appendleft(item)
316def reorder_items_atscope(
317 items: "Dict[nodes.Item, None]",
318 argkeys_cache: "Dict[int, Dict[nodes.Item, Dict[_Key, None]]]",
319 items_by_argkey: "Dict[int, Dict[_Key, Deque[nodes.Item]]]",
320 scopenum: int,
321) -> "Dict[nodes.Item, None]":
322 if scopenum >= scopenum_function or len(items) < 3:
323 return items
324 ignore = set() # type: Set[Optional[_Key]]
325 items_deque = deque(items)
326 items_done = order_preserving_dict() # type: Dict[nodes.Item, None]
327 scoped_items_by_argkey = items_by_argkey[scopenum]
328 scoped_argkeys_cache = argkeys_cache[scopenum]
329 while items_deque:
330 no_argkey_group = order_preserving_dict() # type: Dict[nodes.Item, None]
331 slicing_argkey = None
332 while items_deque:
333 item = items_deque.popleft()
334 if item in items_done or item in no_argkey_group:
335 continue
336 argkeys = order_preserving_dict.fromkeys(
337 (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None
338 )
339 if not argkeys:
340 no_argkey_group[item] = None
341 else:
342 slicing_argkey, _ = argkeys.popitem()
343 # we don't have to remove relevant items from later in the deque because they'll just be ignored
344 matching_items = [
345 i for i in scoped_items_by_argkey[slicing_argkey] if i in items
346 ]
347 for i in reversed(matching_items):
348 fix_cache_order(i, argkeys_cache, items_by_argkey)
349 items_deque.appendleft(i)
350 break
351 if no_argkey_group:
352 no_argkey_group = reorder_items_atscope(
353 no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1
354 )
355 for item in no_argkey_group:
356 items_done[item] = None
357 ignore.add(slicing_argkey)
358 return items_done
361def fillfixtures(function: "Function") -> None:
362 """ fill missing funcargs for a test function. """
363 # Uncomment this after 6.0 release (#7361)
364 # warnings.warn(FILLFUNCARGS, stacklevel=2)
365 try:
366 request = function._request
367 except AttributeError:
368 # XXX this special code path is only expected to execute
369 # with the oejskit plugin. It uses classes with funcargs
370 # and we thus have to work a bit to allow this.
371 fm = function.session._fixturemanager
372 assert function.parent is not None
373 fi = fm.getfixtureinfo(function.parent, function.obj, None)
374 function._fixtureinfo = fi
375 request = function._request = FixtureRequest(function)
376 request._fillfixtures()
377 # prune out funcargs for jstests
378 newfuncargs = {}
379 for name in fi.argnames:
380 newfuncargs[name] = function.funcargs[name]
381 function.funcargs = newfuncargs
382 else:
383 request._fillfixtures()
386def get_direct_param_fixture_func(request):
387 return request.param
390@attr.s(slots=True)
391class FuncFixtureInfo:
392 # original function argument names
393 argnames = attr.ib(type=Tuple[str, ...])
394 # argnames that function immediately requires. These include argnames +
395 # fixture names specified via usefixtures and via autouse=True in fixture
396 # definitions.
397 initialnames = attr.ib(type=Tuple[str, ...])
398 names_closure = attr.ib(type=List[str])
399 name2fixturedefs = attr.ib(type=Dict[str, Sequence["FixtureDef"]])
401 def prune_dependency_tree(self) -> None:
402 """Recompute names_closure from initialnames and name2fixturedefs
404 Can only reduce names_closure, which means that the new closure will
405 always be a subset of the old one. The order is preserved.
407 This method is needed because direct parametrization may shadow some
408 of the fixtures that were included in the originally built dependency
409 tree. In this way the dependency tree can get pruned, and the closure
410 of argnames may get reduced.
411 """
412 closure = set() # type: Set[str]
413 working_set = set(self.initialnames)
414 while working_set:
415 argname = working_set.pop()
416 # argname may be smth not included in the original names_closure,
417 # in which case we ignore it. This currently happens with pseudo
418 # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'.
419 # So they introduce the new dependency 'request' which might have
420 # been missing in the original tree (closure).
421 if argname not in closure and argname in self.names_closure:
422 closure.add(argname)
423 if argname in self.name2fixturedefs:
424 working_set.update(self.name2fixturedefs[argname][-1].argnames)
426 self.names_closure[:] = sorted(closure, key=self.names_closure.index)
429class FixtureRequest:
430 """ A request for a fixture from a test or fixture function.
432 A request object gives access to the requesting test context
433 and has an optional ``param`` attribute in case
434 the fixture is parametrized indirectly.
435 """
437 def __init__(self, pyfuncitem) -> None:
438 self._pyfuncitem = pyfuncitem
439 #: fixture for which this request is being performed
440 self.fixturename = None # type: Optional[str]
441 #: Scope string, one of "function", "class", "module", "session"
442 self.scope = "function" # type: _Scope
443 self._fixture_defs = {} # type: Dict[str, FixtureDef]
444 fixtureinfo = pyfuncitem._fixtureinfo # type: FuncFixtureInfo
445 self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()
446 self._arg2index = {} # type: Dict[str, int]
447 self._fixturemanager = (
448 pyfuncitem.session._fixturemanager
449 ) # type: FixtureManager
451 @property
452 def fixturenames(self) -> List[str]:
453 """names of all active fixtures in this request"""
454 result = list(self._pyfuncitem._fixtureinfo.names_closure)
455 result.extend(set(self._fixture_defs).difference(result))
456 return result
458 @property
459 def funcargnames(self) -> List[str]:
460 """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
461 warnings.warn(FUNCARGNAMES, stacklevel=2)
462 return self.fixturenames
464 @property
465 def node(self):
466 """ underlying collection node (depends on current request scope)"""
467 return self._getscopeitem(self.scope)
469 def _getnextfixturedef(self, argname: str) -> "FixtureDef":
470 fixturedefs = self._arg2fixturedefs.get(argname, None)
471 if fixturedefs is None:
472 # we arrive here because of a dynamic call to
473 # getfixturevalue(argname) usage which was naturally
474 # not known at parsing/collection time
475 assert self._pyfuncitem.parent is not None
476 parentid = self._pyfuncitem.parent.nodeid
477 fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid)
478 # TODO: Fix this type ignore. Either add assert or adjust types.
479 # Can this be None here?
480 self._arg2fixturedefs[argname] = fixturedefs # type: ignore[assignment]
481 # fixturedefs list is immutable so we maintain a decreasing index
482 index = self._arg2index.get(argname, 0) - 1
483 if fixturedefs is None or (-index > len(fixturedefs)):
484 raise FixtureLookupError(argname, self)
485 self._arg2index[argname] = index
486 return fixturedefs[index]
488 @property
489 def config(self) -> Config:
490 """ the pytest config object associated with this request. """
491 return self._pyfuncitem.config # type: ignore[no-any-return] # noqa: F723
493 @scopeproperty()
494 def function(self):
495 """ test function object if the request has a per-function scope. """
496 return self._pyfuncitem.obj
498 @scopeproperty("class")
499 def cls(self):
500 """ class (can be None) where the test function was collected. """
501 clscol = self._pyfuncitem.getparent(_pytest.python.Class)
502 if clscol:
503 return clscol.obj
505 @property
506 def instance(self):
507 """ instance (can be None) on which test function was collected. """
508 # unittest support hack, see _pytest.unittest.TestCaseFunction
509 try:
510 return self._pyfuncitem._testcase
511 except AttributeError:
512 function = getattr(self, "function", None)
513 return getattr(function, "__self__", None)
515 @scopeproperty()
516 def module(self):
517 """ python module object where the test function was collected. """
518 return self._pyfuncitem.getparent(_pytest.python.Module).obj
520 @scopeproperty()
521 def fspath(self) -> py.path.local:
522 """ the file system path of the test module which collected this test. """
523 # TODO: Remove ignore once _pyfuncitem is properly typed.
524 return self._pyfuncitem.fspath # type: ignore
526 @property
527 def keywords(self):
528 """ keywords/markers dictionary for the underlying node. """
529 return self.node.keywords
531 @property
532 def session(self):
533 """ pytest session object. """
534 return self._pyfuncitem.session
536 def addfinalizer(self, finalizer: Callable[[], object]) -> None:
537 """ add finalizer/teardown function to be called after the
538 last test within the requesting test context finished
539 execution. """
540 # XXX usually this method is shadowed by fixturedef specific ones
541 self._addfinalizer(finalizer, scope=self.scope)
543 def _addfinalizer(self, finalizer: Callable[[], object], scope) -> None:
544 colitem = self._getscopeitem(scope)
545 self._pyfuncitem.session._setupstate.addfinalizer(
546 finalizer=finalizer, colitem=colitem
547 )
549 def applymarker(self, marker) -> None:
550 """ Apply a marker to a single test function invocation.
551 This method is useful if you don't want to have a keyword/marker
552 on all function invocations.
554 :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object
555 created by a call to ``pytest.mark.NAME(...)``.
556 """
557 self.node.add_marker(marker)
559 def raiseerror(self, msg: Optional[str]) -> "NoReturn":
560 """ raise a FixtureLookupError with the given message. """
561 raise self._fixturemanager.FixtureLookupError(None, self, msg)
563 def _fillfixtures(self) -> None:
564 item = self._pyfuncitem
565 fixturenames = getattr(item, "fixturenames", self.fixturenames)
566 for argname in fixturenames:
567 if argname not in item.funcargs:
568 item.funcargs[argname] = self.getfixturevalue(argname)
570 def getfixturevalue(self, argname: str) -> Any:
571 """ Dynamically run a named fixture function.
573 Declaring fixtures via function argument is recommended where possible.
574 But if you can only decide whether to use another fixture at test
575 setup time, you may use this function to retrieve it inside a fixture
576 or test function body.
578 :raise pytest.FixtureLookupError:
579 If the given fixture could not be found.
580 """
581 fixturedef = self._get_active_fixturedef(argname)
582 assert fixturedef.cached_result is not None
583 return fixturedef.cached_result[0]
585 def _get_active_fixturedef(
586 self, argname: str
587 ) -> Union["FixtureDef", PseudoFixtureDef]:
588 try:
589 return self._fixture_defs[argname]
590 except KeyError:
591 try:
592 fixturedef = self._getnextfixturedef(argname)
593 except FixtureLookupError:
594 if argname == "request":
595 cached_result = (self, [0], None)
596 scope = "function" # type: _Scope
597 return PseudoFixtureDef(cached_result, scope)
598 raise
599 # remove indent to prevent the python3 exception
600 # from leaking into the call
601 self._compute_fixture_value(fixturedef)
602 self._fixture_defs[argname] = fixturedef
603 return fixturedef
605 def _get_fixturestack(self) -> List["FixtureDef"]:
606 current = self
607 values = [] # type: List[FixtureDef]
608 while 1:
609 fixturedef = getattr(current, "_fixturedef", None)
610 if fixturedef is None:
611 values.reverse()
612 return values
613 values.append(fixturedef)
614 assert isinstance(current, SubRequest)
615 current = current._parent_request
617 def _compute_fixture_value(self, fixturedef: "FixtureDef") -> None:
618 """
619 Creates a SubRequest based on "self" and calls the execute method of the given fixturedef object. This will
620 force the FixtureDef object to throw away any previous results and compute a new fixture value, which
621 will be stored into the FixtureDef object itself.
622 """
623 # prepare a subrequest object before calling fixture function
624 # (latter managed by fixturedef)
625 argname = fixturedef.argname
626 funcitem = self._pyfuncitem
627 scope = fixturedef.scope
628 try:
629 param = funcitem.callspec.getparam(argname)
630 except (AttributeError, ValueError):
631 param = NOTSET
632 param_index = 0
633 has_params = fixturedef.params is not None
634 fixtures_not_supported = getattr(funcitem, "nofuncargs", False)
635 if has_params and fixtures_not_supported:
636 msg = (
637 "{name} does not support fixtures, maybe unittest.TestCase subclass?\n"
638 "Node id: {nodeid}\n"
639 "Function type: {typename}"
640 ).format(
641 name=funcitem.name,
642 nodeid=funcitem.nodeid,
643 typename=type(funcitem).__name__,
644 )
645 fail(msg, pytrace=False)
646 if has_params:
647 frame = inspect.stack()[3]
648 frameinfo = inspect.getframeinfo(frame[0])
649 source_path = py.path.local(frameinfo.filename)
650 source_lineno = frameinfo.lineno
651 rel_source_path = source_path.relto(funcitem.config.rootdir)
652 if rel_source_path:
653 source_path_str = rel_source_path
654 else:
655 source_path_str = str(source_path)
656 msg = (
657 "The requested fixture has no parameter defined for test:\n"
658 " {}\n\n"
659 "Requested fixture '{}' defined in:\n{}"
660 "\n\nRequested here:\n{}:{}".format(
661 funcitem.nodeid,
662 fixturedef.argname,
663 getlocation(fixturedef.func, funcitem.config.rootdir),
664 source_path_str,
665 source_lineno,
666 )
667 )
668 fail(msg, pytrace=False)
669 else:
670 param_index = funcitem.callspec.indices[argname]
671 # if a parametrize invocation set a scope it will override
672 # the static scope defined with the fixture function
673 paramscopenum = funcitem.callspec._arg2scopenum.get(argname)
674 if paramscopenum is not None:
675 scope = scopes[paramscopenum]
677 subrequest = SubRequest(self, scope, param, param_index, fixturedef)
679 # check if a higher-level scoped fixture accesses a lower level one
680 subrequest._check_scope(argname, self.scope, scope)
681 try:
682 # call the fixture function
683 fixturedef.execute(request=subrequest)
684 finally:
685 self._schedule_finalizers(fixturedef, subrequest)
687 def _schedule_finalizers(
688 self, fixturedef: "FixtureDef", subrequest: "SubRequest"
689 ) -> None:
690 # if fixture function failed it might have registered finalizers
691 self.session._setupstate.addfinalizer(
692 functools.partial(fixturedef.finish, request=subrequest), subrequest.node
693 )
695 def _check_scope(self, argname, invoking_scope: "_Scope", requested_scope) -> None:
696 if argname == "request":
697 return
698 if scopemismatch(invoking_scope, requested_scope):
699 # try to report something helpful
700 lines = self._factorytraceback()
701 fail(
702 "ScopeMismatch: You tried to access the %r scoped "
703 "fixture %r with a %r scoped request object, "
704 "involved factories\n%s"
705 % ((requested_scope, argname, invoking_scope, "\n".join(lines))),
706 pytrace=False,
707 )
709 def _factorytraceback(self) -> List[str]:
710 lines = []
711 for fixturedef in self._get_fixturestack():
712 factory = fixturedef.func
713 fs, lineno = getfslineno(factory)
714 p = self._pyfuncitem.session.fspath.bestrelpath(fs)
715 args = _format_args(factory)
716 lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args))
717 return lines
719 def _getscopeitem(self, scope):
720 if scope == "function":
721 # this might also be a non-function Item despite its attribute name
722 return self._pyfuncitem
723 if scope == "package":
724 # FIXME: _fixturedef is not defined on FixtureRequest (this class),
725 # but on FixtureRequest (a subclass).
726 node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined]
727 else:
728 node = get_scope_node(self._pyfuncitem, scope)
729 if node is None and scope == "class":
730 # fallback to function item itself
731 node = self._pyfuncitem
732 assert node, 'Could not obtain a node for scope "{}" for function {!r}'.format(
733 scope, self._pyfuncitem
734 )
735 return node
737 def __repr__(self) -> str:
738 return "<FixtureRequest for %r>" % (self.node)
741class SubRequest(FixtureRequest):
742 """ a sub request for handling getting a fixture from a
743 test function/fixture. """
745 def __init__(
746 self,
747 request: "FixtureRequest",
748 scope: "_Scope",
749 param,
750 param_index: int,
751 fixturedef: "FixtureDef",
752 ) -> None:
753 self._parent_request = request
754 self.fixturename = fixturedef.argname # type: str
755 if param is not NOTSET:
756 self.param = param
757 self.param_index = param_index
758 self.scope = scope
759 self._fixturedef = fixturedef
760 self._pyfuncitem = request._pyfuncitem
761 self._fixture_defs = request._fixture_defs
762 self._arg2fixturedefs = request._arg2fixturedefs
763 self._arg2index = request._arg2index
764 self._fixturemanager = request._fixturemanager
766 def __repr__(self) -> str:
767 return "<SubRequest {!r} for {!r}>".format(self.fixturename, self._pyfuncitem)
769 def addfinalizer(self, finalizer: Callable[[], object]) -> None:
770 self._fixturedef.addfinalizer(finalizer)
772 def _schedule_finalizers(
773 self, fixturedef: "FixtureDef", subrequest: "SubRequest"
774 ) -> None:
775 # if the executing fixturedef was not explicitly requested in the argument list (via
776 # getfixturevalue inside the fixture call) then ensure this fixture def will be finished
777 # first
778 if fixturedef.argname not in self.fixturenames:
779 fixturedef.addfinalizer(
780 functools.partial(self._fixturedef.finish, request=self)
781 )
782 super()._schedule_finalizers(fixturedef, subrequest)
785scopes = ["session", "package", "module", "class", "function"] # type: List[_Scope]
786scopenum_function = scopes.index("function")
789def scopemismatch(currentscope: "_Scope", newscope: "_Scope") -> bool:
790 return scopes.index(newscope) > scopes.index(currentscope)
793def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int:
794 """Look up the index of ``scope`` and raise a descriptive value error
795 if not defined.
796 """
797 strscopes = scopes # type: Sequence[str]
798 try:
799 return strscopes.index(scope)
800 except ValueError:
801 fail(
802 "{} {}got an unexpected scope value '{}'".format(
803 descr, "from {} ".format(where) if where else "", scope
804 ),
805 pytrace=False,
806 )
809class FixtureLookupError(LookupError):
810 """ could not return a requested Fixture (missing or invalid). """
812 def __init__(
813 self, argname: Optional[str], request: FixtureRequest, msg: Optional[str] = None
814 ) -> None:
815 self.argname = argname
816 self.request = request
817 self.fixturestack = request._get_fixturestack()
818 self.msg = msg
820 def formatrepr(self) -> "FixtureLookupErrorRepr":
821 tblines = [] # type: List[str]
822 addline = tblines.append
823 stack = [self.request._pyfuncitem.obj]
824 stack.extend(map(lambda x: x.func, self.fixturestack))
825 msg = self.msg
826 if msg is not None:
827 # the last fixture raise an error, let's present
828 # it at the requesting side
829 stack = stack[:-1]
830 for function in stack:
831 fspath, lineno = getfslineno(function)
832 try:
833 lines, _ = inspect.getsourcelines(get_real_func(function))
834 except (OSError, IndexError, TypeError):
835 error_msg = "file %s, line %s: source code not available"
836 addline(error_msg % (fspath, lineno + 1))
837 else:
838 addline("file {}, line {}".format(fspath, lineno + 1))
839 for i, line in enumerate(lines):
840 line = line.rstrip()
841 addline(" " + line)
842 if line.lstrip().startswith("def"):
843 break
845 if msg is None:
846 fm = self.request._fixturemanager
847 available = set()
848 parentid = self.request._pyfuncitem.parent.nodeid
849 for name, fixturedefs in fm._arg2fixturedefs.items():
850 faclist = list(fm._matchfactories(fixturedefs, parentid))
851 if faclist:
852 available.add(name)
853 if self.argname in available:
854 msg = " recursive dependency involving fixture '{}' detected".format(
855 self.argname
856 )
857 else:
858 msg = "fixture '{}' not found".format(self.argname)
859 msg += "\n available fixtures: {}".format(", ".join(sorted(available)))
860 msg += "\n use 'pytest --fixtures [testpath]' for help on them."
862 return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
865class FixtureLookupErrorRepr(TerminalRepr):
866 def __init__(
867 self,
868 filename: Union[str, py.path.local],
869 firstlineno: int,
870 tblines: Sequence[str],
871 errorstring: str,
872 argname: Optional[str],
873 ) -> None:
874 self.tblines = tblines
875 self.errorstring = errorstring
876 self.filename = filename
877 self.firstlineno = firstlineno
878 self.argname = argname
880 def toterminal(self, tw: TerminalWriter) -> None:
881 # tw.line("FixtureLookupError: %s" %(self.argname), red=True)
882 for tbline in self.tblines:
883 tw.line(tbline.rstrip())
884 lines = self.errorstring.split("\n")
885 if lines:
886 tw.line(
887 "{} {}".format(FormattedExcinfo.fail_marker, lines[0].strip()),
888 red=True,
889 )
890 for line in lines[1:]:
891 tw.line(
892 "{} {}".format(FormattedExcinfo.flow_marker, line.strip()),
893 red=True,
894 )
895 tw.line()
896 tw.line("%s:%d" % (self.filename, self.firstlineno + 1))
899def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn":
900 fs, lineno = getfslineno(fixturefunc)
901 location = "{}:{}".format(fs, lineno + 1)
902 source = _pytest._code.Source(fixturefunc)
903 fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False)
906def call_fixture_func(
907 fixturefunc: "_FixtureFunc[_FixtureValue]", request: FixtureRequest, kwargs
908) -> _FixtureValue:
909 if is_generator(fixturefunc):
910 fixturefunc = cast(
911 Callable[..., Generator[_FixtureValue, None, None]], fixturefunc
912 )
913 generator = fixturefunc(**kwargs)
914 try:
915 fixture_result = next(generator)
916 except StopIteration:
917 raise ValueError(
918 "{} did not yield a value".format(request.fixturename)
919 ) from None
920 finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)
921 request.addfinalizer(finalizer)
922 else:
923 fixturefunc = cast(Callable[..., _FixtureValue], fixturefunc)
924 fixture_result = fixturefunc(**kwargs)
925 return fixture_result
928def _teardown_yield_fixture(fixturefunc, it) -> None:
929 """Executes the teardown of a fixture function by advancing the iterator after the
930 yield and ensure the iteration ends (if not it means there is more than one yield in the function)"""
931 try:
932 next(it)
933 except StopIteration:
934 pass
935 else:
936 fail_fixturefunc(fixturefunc, "fixture function has more than one 'yield'")
939def _eval_scope_callable(
940 scope_callable: "Callable[[str, Config], _Scope]",
941 fixture_name: str,
942 config: Config,
943) -> "_Scope":
944 try:
945 # Type ignored because there is no typing mechanism to specify
946 # keyword arguments, currently.
947 result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg]
948 except Exception as e:
949 raise TypeError(
950 "Error evaluating {} while defining fixture '{}'.\n"
951 "Expected a function with the signature (*, fixture_name, config)".format(
952 scope_callable, fixture_name
953 )
954 ) from e
955 if not isinstance(result, str):
956 fail(
957 "Expected {} to return a 'str' while defining fixture '{}', but it returned:\n"
958 "{!r}".format(scope_callable, fixture_name, result),
959 pytrace=False,
960 )
961 return result
964class FixtureDef(Generic[_FixtureValue]):
965 """ A container for a factory definition. """
967 def __init__(
968 self,
969 fixturemanager: "FixtureManager",
970 baseid,
971 argname: str,
972 func: "_FixtureFunc[_FixtureValue]",
973 scope: "Union[_Scope, Callable[[str, Config], _Scope]]",
974 params: Optional[Sequence[object]],
975 unittest: bool = False,
976 ids: Optional[
977 Union[
978 Tuple[Union[None, str, float, int, bool], ...],
979 Callable[[Any], Optional[object]],
980 ]
981 ] = None,
982 ) -> None:
983 self._fixturemanager = fixturemanager
984 self.baseid = baseid or ""
985 self.has_location = baseid is not None
986 self.func = func
987 self.argname = argname
988 if callable(scope):
989 scope_ = _eval_scope_callable(scope, argname, fixturemanager.config)
990 else:
991 scope_ = scope
992 self.scopenum = scope2index(
993 scope_ or "function",
994 descr="Fixture '{}'".format(func.__name__),
995 where=baseid,
996 )
997 self.scope = scope_
998 self.params = params # type: Optional[Sequence[object]]
999 self.argnames = getfuncargnames(
1000 func, name=argname, is_method=unittest
1001 ) # type: Tuple[str, ...]
1002 self.unittest = unittest
1003 self.ids = ids
1004 self.cached_result = None # type: Optional[_FixtureCachedResult[_FixtureValue]]
1005 self._finalizers = [] # type: List[Callable[[], object]]
1007 def addfinalizer(self, finalizer: Callable[[], object]) -> None:
1008 self._finalizers.append(finalizer)
1010 def finish(self, request: SubRequest) -> None:
1011 exc = None
1012 try:
1013 while self._finalizers:
1014 try:
1015 func = self._finalizers.pop()
1016 func()
1017 except BaseException as e:
1018 # XXX Only first exception will be seen by user,
1019 # ideally all should be reported.
1020 if exc is None:
1021 exc = e
1022 if exc:
1023 raise exc
1024 finally:
1025 hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
1026 hook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
1027 # even if finalization fails, we invalidate
1028 # the cached fixture value and remove
1029 # all finalizers because they may be bound methods which will
1030 # keep instances alive
1031 self.cached_result = None
1032 self._finalizers = []
1034 def execute(self, request: SubRequest) -> _FixtureValue:
1035 # get required arguments and register our own finish()
1036 # with their finalization
1037 for argname in self.argnames:
1038 fixturedef = request._get_active_fixturedef(argname)
1039 if argname != "request":
1040 # PseudoFixtureDef is only for "request".
1041 assert isinstance(fixturedef, FixtureDef)
1042 fixturedef.addfinalizer(functools.partial(self.finish, request=request))
1044 my_cache_key = self.cache_key(request)
1045 if self.cached_result is not None:
1046 # note: comparison with `==` can fail (or be expensive) for e.g.
1047 # numpy arrays (#6497)
1048 cache_key = self.cached_result[1]
1049 if my_cache_key is cache_key:
1050 if self.cached_result[2] is not None:
1051 _, val, tb = self.cached_result[2]
1052 raise val.with_traceback(tb)
1053 else:
1054 result = self.cached_result[0]
1055 return result
1056 # we have a previous but differently parametrized fixture instance
1057 # so we need to tear it down before creating a new one
1058 self.finish(request)
1059 assert self.cached_result is None
1061 hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
1062 result = hook.pytest_fixture_setup(fixturedef=self, request=request)
1063 return result
1065 def cache_key(self, request: SubRequest) -> object:
1066 return request.param_index if not hasattr(request, "param") else request.param
1068 def __repr__(self) -> str:
1069 return "<FixtureDef argname={!r} scope={!r} baseid={!r}>".format(
1070 self.argname, self.scope, self.baseid
1071 )
1074def resolve_fixture_function(
1075 fixturedef: FixtureDef[_FixtureValue], request: FixtureRequest
1076) -> "_FixtureFunc[_FixtureValue]":
1077 """Gets the actual callable that can be called to obtain the fixture value, dealing with unittest-specific
1078 instances and bound methods.
1079 """
1080 fixturefunc = fixturedef.func
1081 if fixturedef.unittest:
1082 if request.instance is not None:
1083 # bind the unbound method to the TestCase instance
1084 fixturefunc = fixturedef.func.__get__(request.instance) # type: ignore[union-attr]
1085 else:
1086 # the fixture function needs to be bound to the actual
1087 # request.instance so that code working with "fixturedef" behaves
1088 # as expected.
1089 if request.instance is not None:
1090 # handle the case where fixture is defined not in a test class, but some other class
1091 # (for example a plugin class with a fixture), see #2270
1092 if hasattr(fixturefunc, "__self__") and not isinstance(
1093 request.instance, fixturefunc.__self__.__class__ # type: ignore[union-attr]
1094 ):
1095 return fixturefunc
1096 fixturefunc = getimfunc(fixturedef.func)
1097 if fixturefunc != fixturedef.func:
1098 fixturefunc = fixturefunc.__get__(request.instance) # type: ignore[union-attr]
1099 return fixturefunc
1102def pytest_fixture_setup(
1103 fixturedef: FixtureDef[_FixtureValue], request: SubRequest
1104) -> _FixtureValue:
1105 """ Execution of fixture setup. """
1106 kwargs = {}
1107 for argname in fixturedef.argnames:
1108 fixdef = request._get_active_fixturedef(argname)
1109 assert fixdef.cached_result is not None
1110 result, arg_cache_key, exc = fixdef.cached_result
1111 request._check_scope(argname, request.scope, fixdef.scope)
1112 kwargs[argname] = result
1114 fixturefunc = resolve_fixture_function(fixturedef, request)
1115 my_cache_key = fixturedef.cache_key(request)
1116 try:
1117 result = call_fixture_func(fixturefunc, request, kwargs)
1118 except TEST_OUTCOME:
1119 exc_info = sys.exc_info()
1120 assert exc_info[0] is not None
1121 fixturedef.cached_result = (None, my_cache_key, exc_info)
1122 raise
1123 fixturedef.cached_result = (result, my_cache_key, None)
1124 return result
1127def _ensure_immutable_ids(
1128 ids: Optional[
1129 Union[
1130 Iterable[Union[None, str, float, int, bool]],
1131 Callable[[Any], Optional[object]],
1132 ]
1133 ],
1134) -> Optional[
1135 Union[
1136 Tuple[Union[None, str, float, int, bool], ...],
1137 Callable[[Any], Optional[object]],
1138 ]
1139]:
1140 if ids is None:
1141 return None
1142 if callable(ids):
1143 return ids
1144 return tuple(ids)
1147def _params_converter(
1148 params: Optional[Iterable[object]],
1149) -> Optional[Tuple[object, ...]]:
1150 return tuple(params) if params is not None else None
1153def wrap_function_to_error_out_if_called_directly(function, fixture_marker):
1154 """Wrap the given fixture function so we can raise an error about it being called directly,
1155 instead of used as an argument in a test function.
1156 """
1157 message = (
1158 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n'
1159 "but are created automatically when test functions request them as parameters.\n"
1160 "See https://docs.pytest.org/en/stable/fixture.html for more information about fixtures, and\n"
1161 "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code."
1162 ).format(name=fixture_marker.name or function.__name__)
1164 @functools.wraps(function)
1165 def result(*args, **kwargs):
1166 fail(message, pytrace=False)
1168 # keep reference to the original function in our own custom attribute so we don't unwrap
1169 # further than this point and lose useful wrappings like @mock.patch (#3774)
1170 result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined]
1172 return result
1175@attr.s(frozen=True)
1176class FixtureFunctionMarker:
1177 scope = attr.ib(type="Union[_Scope, Callable[[str, Config], _Scope]]")
1178 params = attr.ib(type=Optional[Tuple[object, ...]], converter=_params_converter)
1179 autouse = attr.ib(type=bool, default=False)
1180 ids = attr.ib(
1181 type=Union[
1182 Tuple[Union[None, str, float, int, bool], ...],
1183 Callable[[Any], Optional[object]],
1184 ],
1185 default=None,
1186 converter=_ensure_immutable_ids,
1187 )
1188 name = attr.ib(type=Optional[str], default=None)
1190 def __call__(self, function: _FixtureFunction) -> _FixtureFunction:
1191 if inspect.isclass(function):
1192 raise ValueError("class fixtures not supported (maybe in the future)")
1194 if getattr(function, "_pytestfixturefunction", False):
1195 raise ValueError(
1196 "fixture is being applied more than once to the same function"
1197 )
1199 function = wrap_function_to_error_out_if_called_directly(function, self)
1201 name = self.name or function.__name__
1202 if name == "request":
1203 location = getlocation(function)
1204 fail(
1205 "'request' is a reserved word for fixtures, use another name:\n {}".format(
1206 location
1207 ),
1208 pytrace=False,
1209 )
1211 # Type ignored because https://github.com/python/mypy/issues/2087.
1212 function._pytestfixturefunction = self # type: ignore[attr-defined]
1213 return function
1216@overload
1217def fixture(
1218 fixture_function: _FixtureFunction,
1219 *,
1220 scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ...,
1221 params: Optional[Iterable[object]] = ...,
1222 autouse: bool = ...,
1223 ids: Optional[
1224 Union[
1225 Iterable[Union[None, str, float, int, bool]],
1226 Callable[[Any], Optional[object]],
1227 ]
1228 ] = ...,
1229 name: Optional[str] = ...
1230) -> _FixtureFunction:
1231 raise NotImplementedError()
1234@overload # noqa: F811
1235def fixture( # noqa: F811
1236 fixture_function: None = ...,
1237 *,
1238 scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ...,
1239 params: Optional[Iterable[object]] = ...,
1240 autouse: bool = ...,
1241 ids: Optional[
1242 Union[
1243 Iterable[Union[None, str, float, int, bool]],
1244 Callable[[Any], Optional[object]],
1245 ]
1246 ] = ...,
1247 name: Optional[str] = None
1248) -> FixtureFunctionMarker:
1249 raise NotImplementedError()
1252def fixture( # noqa: F811
1253 fixture_function: Optional[_FixtureFunction] = None,
1254 *args: Any,
1255 scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function",
1256 params: Optional[Iterable[object]] = None,
1257 autouse: bool = False,
1258 ids: Optional[
1259 Union[
1260 Iterable[Union[None, str, float, int, bool]],
1261 Callable[[Any], Optional[object]],
1262 ]
1263 ] = None,
1264 name: Optional[str] = None
1265) -> Union[FixtureFunctionMarker, _FixtureFunction]:
1266 """Decorator to mark a fixture factory function.
1268 This decorator can be used, with or without parameters, to define a
1269 fixture function.
1271 The name of the fixture function can later be referenced to cause its
1272 invocation ahead of running tests: test
1273 modules or classes can use the ``pytest.mark.usefixtures(fixturename)``
1274 marker.
1276 Test functions can directly use fixture names as input
1277 arguments in which case the fixture instance returned from the fixture
1278 function will be injected.
1280 Fixtures can provide their values to test functions using ``return`` or ``yield``
1281 statements. When using ``yield`` the code block after the ``yield`` statement is executed
1282 as teardown code regardless of the test outcome, and must yield exactly once.
1284 :arg scope: the scope for which this fixture is shared, one of
1285 ``"function"`` (default), ``"class"``, ``"module"``,
1286 ``"package"`` or ``"session"``.
1288 This parameter may also be a callable which receives ``(fixture_name, config)``
1289 as parameters, and must return a ``str`` with one of the values mentioned above.
1291 See :ref:`dynamic scope` in the docs for more information.
1293 :arg params: an optional list of parameters which will cause multiple
1294 invocations of the fixture function and all of the tests
1295 using it.
1296 The current parameter is available in ``request.param``.
1298 :arg autouse: if True, the fixture func is activated for all tests that
1299 can see it. If False (the default) then an explicit
1300 reference is needed to activate the fixture.
1302 :arg ids: list of string ids each corresponding to the params
1303 so that they are part of the test id. If no ids are provided
1304 they will be generated automatically from the params.
1306 :arg name: the name of the fixture. This defaults to the name of the
1307 decorated function. If a fixture is used in the same module in
1308 which it is defined, the function name of the fixture will be
1309 shadowed by the function arg that requests the fixture; one way
1310 to resolve this is to name the decorated function
1311 ``fixture_<fixturename>`` and then use
1312 ``@pytest.fixture(name='<fixturename>')``.
1313 """
1314 # Positional arguments backward compatibility.
1315 # If a kwarg is equal to its default, assume it was not explicitly
1316 # passed, i.e. not duplicated. The more correct way is to use a
1317 # **kwargs and check `in`, but that obfuscates the function signature.
1318 if isinstance(fixture_function, str):
1319 # It's actually the first positional argument, scope.
1320 args = (fixture_function, *args)
1321 fixture_function = None
1322 duplicated_args = []
1323 if len(args) > 0:
1324 if scope == "function":
1325 scope = args[0]
1326 else:
1327 duplicated_args.append("scope")
1328 if len(args) > 1:
1329 if params is None:
1330 params = args[1]
1331 else:
1332 duplicated_args.append("params")
1333 if len(args) > 2:
1334 if autouse is False:
1335 autouse = args[2]
1336 else:
1337 duplicated_args.append("autouse")
1338 if len(args) > 3:
1339 if ids is None:
1340 ids = args[3]
1341 else:
1342 duplicated_args.append("ids")
1343 if len(args) > 4:
1344 if name is None:
1345 name = args[4]
1346 else:
1347 duplicated_args.append("name")
1348 if len(args) > 5:
1349 raise TypeError(
1350 "fixture() takes 5 positional arguments but {} were given".format(len(args))
1351 )
1352 if duplicated_args:
1353 raise TypeError(
1354 "The fixture arguments are defined as positional and keyword: {}. "
1355 "Use only keyword arguments.".format(", ".join(duplicated_args))
1356 )
1357 if args:
1358 warnings.warn(FIXTURE_POSITIONAL_ARGUMENTS, stacklevel=2)
1359 # End backward compatiblity.
1361 fixture_marker = FixtureFunctionMarker(
1362 scope=scope, params=params, autouse=autouse, ids=ids, name=name,
1363 )
1365 # Direct decoration.
1366 if fixture_function:
1367 return fixture_marker(fixture_function)
1369 return fixture_marker
1372def yield_fixture(
1373 fixture_function=None,
1374 *args,
1375 scope="function",
1376 params=None,
1377 autouse=False,
1378 ids=None,
1379 name=None
1380):
1381 """ (return a) decorator to mark a yield-fixture factory function.
1383 .. deprecated:: 3.0
1384 Use :py:func:`pytest.fixture` directly instead.
1385 """
1386 return fixture(
1387 fixture_function,
1388 *args,
1389 scope=scope,
1390 params=params,
1391 autouse=autouse,
1392 ids=ids,
1393 name=name,
1394 )
1397@fixture(scope="session")
1398def pytestconfig(request: FixtureRequest) -> Config:
1399 """Session-scoped fixture that returns the :class:`_pytest.config.Config` object.
1401 Example::
1403 def test_foo(pytestconfig):
1404 if pytestconfig.getoption("verbose") > 0:
1405 ...
1407 """
1408 return request.config
1411def pytest_addoption(parser: Parser) -> None:
1412 parser.addini(
1413 "usefixtures",
1414 type="args",
1415 default=[],
1416 help="list of default fixtures to be used with this project",
1417 )
1420class FixtureManager:
1421 """
1422 pytest fixtures definitions and information is stored and managed
1423 from this class.
1425 During collection fm.parsefactories() is called multiple times to parse
1426 fixture function definitions into FixtureDef objects and internal
1427 data structures.
1429 During collection of test functions, metafunc-mechanics instantiate
1430 a FuncFixtureInfo object which is cached per node/func-name.
1431 This FuncFixtureInfo object is later retrieved by Function nodes
1432 which themselves offer a fixturenames attribute.
1434 The FuncFixtureInfo object holds information about fixtures and FixtureDefs
1435 relevant for a particular function. An initial list of fixtures is
1436 assembled like this:
1438 - ini-defined usefixtures
1439 - autouse-marked fixtures along the collection chain up from the function
1440 - usefixtures markers at module/class/function level
1441 - test function funcargs
1443 Subsequently the funcfixtureinfo.fixturenames attribute is computed
1444 as the closure of the fixtures needed to setup the initial fixtures,
1445 i. e. fixtures needed by fixture functions themselves are appended
1446 to the fixturenames list.
1448 Upon the test-setup phases all fixturenames are instantiated, retrieved
1449 by a lookup of their FuncFixtureInfo.
1450 """
1452 FixtureLookupError = FixtureLookupError
1453 FixtureLookupErrorRepr = FixtureLookupErrorRepr
1455 def __init__(self, session: "Session") -> None:
1456 self.session = session
1457 self.config = session.config # type: Config
1458 self._arg2fixturedefs = {} # type: Dict[str, List[FixtureDef]]
1459 self._holderobjseen = set() # type: Set
1460 self._nodeid_and_autousenames = [
1461 ("", self.config.getini("usefixtures"))
1462 ] # type: List[Tuple[str, List[str]]]
1463 session.config.pluginmanager.register(self, "funcmanage")
1465 def _get_direct_parametrize_args(self, node: "nodes.Node") -> List[str]:
1466 """This function returns all the direct parametrization
1467 arguments of a node, so we don't mistake them for fixtures
1469 Check https://github.com/pytest-dev/pytest/issues/5036
1471 This things are done later as well when dealing with parametrization
1472 so this could be improved
1473 """
1474 parametrize_argnames = [] # type: List[str]
1475 for marker in node.iter_markers(name="parametrize"):
1476 if not marker.kwargs.get("indirect", False):
1477 p_argnames, _ = ParameterSet._parse_parametrize_args(
1478 *marker.args, **marker.kwargs
1479 )
1480 parametrize_argnames.extend(p_argnames)
1482 return parametrize_argnames
1484 def getfixtureinfo(
1485 self, node: "nodes.Node", func, cls, funcargs: bool = True
1486 ) -> FuncFixtureInfo:
1487 if funcargs and not getattr(node, "nofuncargs", False):
1488 argnames = getfuncargnames(func, name=node.name, cls=cls)
1489 else:
1490 argnames = ()
1492 usefixtures = itertools.chain.from_iterable(
1493 mark.args for mark in node.iter_markers(name="usefixtures")
1494 )
1495 initialnames = tuple(usefixtures) + argnames
1496 fm = node.session._fixturemanager
1497 initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(
1498 initialnames, node, ignore_args=self._get_direct_parametrize_args(node)
1499 )
1500 return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)
1502 def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None:
1503 nodeid = None
1504 try:
1505 p = py.path.local(plugin.__file__) # type: ignore[attr-defined]
1506 except AttributeError:
1507 pass
1508 else:
1509 from _pytest import nodes
1511 # construct the base nodeid which is later used to check
1512 # what fixtures are visible for particular tests (as denoted
1513 # by their test id)
1514 if p.basename.startswith("conftest.py"):
1515 nodeid = p.dirpath().relto(self.config.rootdir)
1516 if p.sep != nodes.SEP:
1517 nodeid = nodeid.replace(p.sep, nodes.SEP)
1519 self.parsefactories(plugin, nodeid)
1521 def _getautousenames(self, nodeid: str) -> List[str]:
1522 """ return a tuple of fixture names to be used. """
1523 autousenames = [] # type: List[str]
1524 for baseid, basenames in self._nodeid_and_autousenames:
1525 if nodeid.startswith(baseid):
1526 if baseid:
1527 i = len(baseid)
1528 nextchar = nodeid[i : i + 1]
1529 if nextchar and nextchar not in ":/":
1530 continue
1531 autousenames.extend(basenames)
1532 return autousenames
1534 def getfixtureclosure(
1535 self, fixturenames: Tuple[str, ...], parentnode, ignore_args: Sequence[str] = ()
1536 ) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef]]]:
1537 # collect the closure of all fixtures , starting with the given
1538 # fixturenames as the initial set. As we have to visit all
1539 # factory definitions anyway, we also return an arg2fixturedefs
1540 # mapping so that the caller can reuse it and does not have
1541 # to re-discover fixturedefs again for each fixturename
1542 # (discovering matching fixtures for a given name/node is expensive)
1544 parentid = parentnode.nodeid
1545 fixturenames_closure = self._getautousenames(parentid)
1547 def merge(otherlist: Iterable[str]) -> None:
1548 for arg in otherlist:
1549 if arg not in fixturenames_closure:
1550 fixturenames_closure.append(arg)
1552 merge(fixturenames)
1554 # at this point, fixturenames_closure contains what we call "initialnames",
1555 # which is a set of fixturenames the function immediately requests. We
1556 # need to return it as well, so save this.
1557 initialnames = tuple(fixturenames_closure)
1559 arg2fixturedefs = {} # type: Dict[str, Sequence[FixtureDef]]
1560 lastlen = -1
1561 while lastlen != len(fixturenames_closure):
1562 lastlen = len(fixturenames_closure)
1563 for argname in fixturenames_closure:
1564 if argname in ignore_args:
1565 continue
1566 if argname in arg2fixturedefs:
1567 continue
1568 fixturedefs = self.getfixturedefs(argname, parentid)
1569 if fixturedefs:
1570 arg2fixturedefs[argname] = fixturedefs
1571 merge(fixturedefs[-1].argnames)
1573 def sort_by_scope(arg_name: str) -> int:
1574 try:
1575 fixturedefs = arg2fixturedefs[arg_name]
1576 except KeyError:
1577 return scopes.index("function")
1578 else:
1579 return fixturedefs[-1].scopenum
1581 fixturenames_closure.sort(key=sort_by_scope)
1582 return initialnames, fixturenames_closure, arg2fixturedefs
1584 def pytest_generate_tests(self, metafunc: "Metafunc") -> None:
1585 for argname in metafunc.fixturenames:
1586 faclist = metafunc._arg2fixturedefs.get(argname)
1587 if faclist:
1588 fixturedef = faclist[-1]
1589 if fixturedef.params is not None:
1590 markers = list(metafunc.definition.iter_markers("parametrize"))
1591 for parametrize_mark in markers:
1592 if "argnames" in parametrize_mark.kwargs:
1593 argnames = parametrize_mark.kwargs["argnames"]
1594 else:
1595 argnames = parametrize_mark.args[0]
1597 if not isinstance(argnames, (tuple, list)):
1598 argnames = [
1599 x.strip() for x in argnames.split(",") if x.strip()
1600 ]
1601 if argname in argnames:
1602 break
1603 else:
1604 metafunc.parametrize(
1605 argname,
1606 fixturedef.params,
1607 indirect=True,
1608 scope=fixturedef.scope,
1609 ids=fixturedef.ids,
1610 )
1611 else:
1612 continue # will raise FixtureLookupError at setup time
1614 def pytest_collection_modifyitems(self, items: "List[nodes.Item]") -> None:
1615 # separate parametrized setups
1616 items[:] = reorder_items(items)
1618 def parsefactories(
1619 self, node_or_obj, nodeid=NOTSET, unittest: bool = False
1620 ) -> None:
1621 if nodeid is not NOTSET:
1622 holderobj = node_or_obj
1623 else:
1624 holderobj = node_or_obj.obj
1625 nodeid = node_or_obj.nodeid
1626 if holderobj in self._holderobjseen:
1627 return
1629 self._holderobjseen.add(holderobj)
1630 autousenames = []
1631 for name in dir(holderobj):
1632 # The attribute can be an arbitrary descriptor, so the attribute
1633 # access below can raise. safe_getatt() ignores such exceptions.
1634 obj = safe_getattr(holderobj, name, None)
1635 marker = getfixturemarker(obj)
1636 if not isinstance(marker, FixtureFunctionMarker):
1637 # magic globals with __getattr__ might have got us a wrong
1638 # fixture attribute
1639 continue
1641 if marker.name:
1642 name = marker.name
1644 # during fixture definition we wrap the original fixture function
1645 # to issue a warning if called directly, so here we unwrap it in order to not emit the warning
1646 # when pytest itself calls the fixture function
1647 obj = get_real_method(obj, holderobj)
1649 fixture_def = FixtureDef(
1650 fixturemanager=self,
1651 baseid=nodeid,
1652 argname=name,
1653 func=obj,
1654 scope=marker.scope,
1655 params=marker.params,
1656 unittest=unittest,
1657 ids=marker.ids,
1658 )
1660 faclist = self._arg2fixturedefs.setdefault(name, [])
1661 if fixture_def.has_location:
1662 faclist.append(fixture_def)
1663 else:
1664 # fixturedefs with no location are at the front
1665 # so this inserts the current fixturedef after the
1666 # existing fixturedefs from external plugins but
1667 # before the fixturedefs provided in conftests.
1668 i = len([f for f in faclist if not f.has_location])
1669 faclist.insert(i, fixture_def)
1670 if marker.autouse:
1671 autousenames.append(name)
1673 if autousenames:
1674 self._nodeid_and_autousenames.append((nodeid or "", autousenames))
1676 def getfixturedefs(
1677 self, argname: str, nodeid: str
1678 ) -> Optional[Sequence[FixtureDef]]:
1679 """
1680 Gets a list of fixtures which are applicable to the given node id.
1682 :param str argname: name of the fixture to search for
1683 :param str nodeid: full node id of the requesting test.
1684 :return: list[FixtureDef]
1685 """
1686 try:
1687 fixturedefs = self._arg2fixturedefs[argname]
1688 except KeyError:
1689 return None
1690 return tuple(self._matchfactories(fixturedefs, nodeid))
1692 def _matchfactories(
1693 self, fixturedefs: Iterable[FixtureDef], nodeid: str
1694 ) -> Iterator[FixtureDef]:
1695 from _pytest import nodes
1697 for fixturedef in fixturedefs:
1698 if nodes.ischildnode(fixturedef.baseid, nodeid):
1699 yield fixturedef