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