1 import functools

2 import inspect

3 import os

4 import sys

5 import warnings

6 from collections import defaultdict

7 from collections import deque

8 from contextlib import suppress

9 from pathlib import Path

10 from types import TracebackType

11 from typing import Any

12 from typing import Callable

13 from typing import cast

14 from typing import Dict

15 from typing import Generator

16 from typing import Generic

17 from typing import Iterable

18 from typing import Iterator

19 from typing import List

20 from typing import MutableMapping

21 from typing import NoReturn

22 from typing import Optional

23 from typing import Sequence

24 from typing import Set

25 from typing import Tuple

26 from typing import Type

27 from typing import TYPE_CHECKING

28 from typing import TypeVar

29 from typing import Union

30

31 import attr

32

33 import _pytest

34 from _pytest import nodes

35 from _pytest._code import getfslineno

36 from _pytest._code.code import FormattedExcinfo

37 from _pytest._code.code import TerminalRepr

38 from _pytest._io import TerminalWriter

39 from _pytest.compat import _format_args

40 from _pytest.compat import _PytestWrapper

41 from _pytest.compat import assert_never

42 from _pytest.compat import final

43 from _pytest.compat import get_real_func

44 from _pytest.compat import get_real_method

45 from _pytest.compat import getfuncargnames

46 from _pytest.compat import getimfunc

47 from _pytest.compat import getlocation

48 from _pytest.compat import is_generator

49 from _pytest.compat import NOTSET

50 from _pytest.compat import overload

51 from _pytest.compat import safe_getattr

52 from _pytest.config import _PluggyPlugin

53 from _pytest.config import Config

54 from _pytest.config.argparsing import Parser

55 from _pytest.deprecated import check_ispytest

56 from _pytest.deprecated import YIELD_FIXTURE

57 from _pytest.mark import Mark

58 from _pytest.mark import ParameterSet

59 from _pytest.mark.structures import MarkDecorator

60 from _pytest.outcomes import fail

61 from _pytest.outcomes import skip

62 from _pytest.outcomes import TEST_OUTCOME

63 from _pytest.pathlib import absolutepath

64 from _pytest.pathlib import bestrelpath

65 from _pytest.scope import HIGH_SCOPES

66 from _pytest.scope import Scope

67 from _pytest.stash import StashKey

68

69

70 if TYPE_CHECKING:

71 from typing import Deque

72

73 from _pytest.scope import _ScopeName

74 from _pytest.main import Session

75 from _pytest.python import CallSpec2

76 from _pytest.python import Metafunc

77

78

79# The value of the fixture -- return/yield of the fixture function (type variable).

80 FixtureValue = TypeVar("FixtureValue")

81# The type of the fixture function (type variable).

82 FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object])

83# The type of a fixture function (type alias generic in fixture value).

84_FixtureFunc = Union[

85 Callable[..., FixtureValue], Callable[..., Generator[FixtureValue, None, None]]

86]

87# The type of FixtureDef.cached_result (type alias generic in fixture value).

88_FixtureCachedResult = Union[

89 Tuple[

90 # The result.

91 FixtureValue,

92 # Cache key.

93 object,

94 None,

95 ],

96 Tuple[

97 None,

98 # Cache key.

99 object,

100 # Exc info if raised.

101 Tuple[Type[BaseException], BaseException, TracebackType],

102 ],

103]

104

105

106@attr.s(frozen=True, auto_attribs=True)

107 class PseudoFixtureDef(Generic[FixtureValue]):

108 cached_result: "_FixtureCachedResult[FixtureValue]"

109 _scope: Scope

110

111

112 def pytest_sessionstart(session: "Session") -> None:

113 session._fixturemanager = FixtureManager(session)

114

115

116 def get_scope_package(node, fixturedef: "FixtureDef[object]"):

117 import pytest

118

119 cls = pytest.Package

120 current = node

121 fixture_package_name = "{}/{}".format(fixturedef.baseid, "__init__.py")

122 while current and (

123 type(current) is not cls or fixture_package_name != current.nodeid

124 ):

125 current = current.parent

126 if current is None:

127 return node.session

128 return current

129

130

131 def get_scope_node(

132 node: nodes.Node, scope: Scope

133) -> Optional[Union[nodes.Item, nodes.Collector]]:

134 import _pytest.python

135

136 if scope is Scope.Function:

137 return node.getparent(nodes.Item)

138 elif scope is Scope.Class:

139 return node.getparent(_pytest.python.Class)

140 elif scope is Scope.Module:

141 return node.getparent(_pytest.python.Module)

142 elif scope is Scope.Package:

143 return node.getparent(_pytest.python.Package)

144 elif scope is Scope.Session:

145 return node.getparent(_pytest.main.Session)

146 else:

147 assert_never(scope)

148

149

150# Used for storing artificial fixturedefs for direct parametrization.

151 name2pseudofixturedef_key = StashKey[Dict[str, "FixtureDef[Any]"]]()

152

153

154 def add_funcarg_pseudo_fixture_def(

155 collector: nodes.Collector, metafunc: "Metafunc", fixturemanager: "FixtureManager"

156) -> None:

157 # This function will transform all collected calls to functions

158 # if they use direct funcargs (i.e. direct parametrization)

159 # because we want later test execution to be able to rely on

160 # an existing FixtureDef structure for all arguments.

161 # XXX we can probably avoid this algorithm if we modify CallSpec2

162 # to directly care for creating the fixturedefs within its methods.

163 if not metafunc._calls[0].funcargs:

164 # This function call does not have direct parametrization.

165 return

166 # Collect funcargs of all callspecs into a list of values.

167 arg2params: Dict[str, List[object]] = {}

168 arg2scope: Dict[str, Scope] = {}

169 for callspec in metafunc._calls:

170 for argname, argvalue in callspec.funcargs.items():

171 assert argname not in callspec.params

172 callspec.params[argname] = argvalue

173 arg2params_list = arg2params.setdefault(argname, [])

174 callspec.indices[argname] = len(arg2params_list)

175 arg2params_list.append(argvalue)

176 if argname not in arg2scope:

177 scope = callspec._arg2scope.get(argname, Scope.Function)

178 arg2scope[argname] = scope

179 callspec.funcargs.clear()

180

181 # Register artificial FixtureDef's so that later at test execution

182 # time we can rely on a proper FixtureDef to exist for fixture setup.

183 arg2fixturedefs = metafunc._arg2fixturedefs

184 for argname, valuelist in arg2params.items():

185 # If we have a scope that is higher than function, we need

186 # to make sure we only ever create an according fixturedef on

187 # a per-scope basis. We thus store and cache the fixturedef on the

188 # node related to the scope.

189 scope = arg2scope[argname]

190 node = None

191 if scope is not Scope.Function:

192 node = get_scope_node(collector, scope)

193 if node is None:

194 assert scope is Scope.Class and isinstance(

195 collector, _pytest.python.Module

196 )

197 # Use module-level collector for class-scope (for now).

198 node = collector

199 if node is None:

200 name2pseudofixturedef = None

201 else:

202 default: Dict[str, FixtureDef[Any]] = {}

203 name2pseudofixturedef = node.stash.setdefault(

204 name2pseudofixturedef_key, default

205 )

206 if name2pseudofixturedef is not None and argname in name2pseudofixturedef:

207 arg2fixturedefs[argname] = [name2pseudofixturedef[argname]]

208 else:

209 fixturedef = FixtureDef(

210 fixturemanager=fixturemanager,

211 baseid="",

212 argname=argname,

213 func=get_direct_param_fixture_func,

214 scope=arg2scope[argname],

215 params=valuelist,

216 unittest=False,

217 ids=None,

218 )

219 arg2fixturedefs[argname] = [fixturedef]

220 if name2pseudofixturedef is not None:

221 name2pseudofixturedef[argname] = fixturedef

222

223

224 def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]:

225 """Return fixturemarker or None if it doesn't exist or raised

226 exceptions."""

227 return cast(

228 Optional[FixtureFunctionMarker],

229 safe_getattr(obj, "_pytestfixturefunction", None),

230 )

231

232

233# Parametrized fixture key, helper alias for code below.

234_Key = Tuple[object, ...]

235

236

237 def get_parametrized_fixture_keys(item: nodes.Item, scope: Scope) -> Iterator[_Key]:

238 """Return list of keys for all parametrized arguments which match

239 the specified scope."""

240 assert scope is not Scope.Function

241 try:

242 callspec = item.callspec # type: ignore[attr-defined]

243 except AttributeError:

244 pass

245 else:

246 cs: CallSpec2 = callspec

247 # cs.indices.items() is random order of argnames. Need to

248 # sort this so that different calls to

249 # get_parametrized_fixture_keys will be deterministic.

250 for argname, param_index in sorted(cs.indices.items()):

251 if cs._arg2scope[argname] != scope:

252 continue

253 if scope is Scope.Session:

254 key: _Key = (argname, param_index)

255 elif scope is Scope.Package:

256 key = (argname, param_index, item.path.parent)

257 elif scope is Scope.Module:

258 key = (argname, param_index, item.path)

259 elif scope is Scope.Class:

260 item_cls = item.cls # type: ignore[attr-defined]

261 key = (argname, param_index, item.path, item_cls)

262 else:

263 assert_never(scope)

264 yield key

265

266

267# Algorithm for sorting on a per-parametrized resource setup basis.

268# It is called for Session scope first and performs sorting

269# down to the lower scopes such as to minimize number of "high scope"

270# setups and teardowns.

271

272

273 def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]:

274 argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[_Key, None]]] = {}

275 items_by_argkey: Dict[Scope, Dict[_Key, Deque[nodes.Item]]] = {}

276 for scope in HIGH_SCOPES:

277 d: Dict[nodes.Item, Dict[_Key, None]] = {}

278 argkeys_cache[scope] = d

279 item_d: Dict[_Key, Deque[nodes.Item]] = defaultdict(deque)

280 items_by_argkey[scope] = item_d

281 for item in items:

282 keys = dict.fromkeys(get_parametrized_fixture_keys(item, scope), None)

283 if keys:

284 d[item] = keys

285 for key in keys:

286 item_d[key].append(item)

287 items_dict = dict.fromkeys(items, None)

288 return list(

289 reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, Scope.Session)

290 )

291

292

293 def fix_cache_order(

294 item: nodes.Item,

295 argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[_Key, None]]],

296 items_by_argkey: Dict[Scope, Dict[_Key, "Deque[nodes.Item]"]],

297) -> None:

298 for scope in HIGH_SCOPES:

299 for key in argkeys_cache[scope].get(item, []):

300 items_by_argkey[scope][key].appendleft(item)

301

302

303 def reorder_items_atscope(

304 items: Dict[nodes.Item, None],

305 argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[_Key, None]]],

306 items_by_argkey: Dict[Scope, Dict[_Key, "Deque[nodes.Item]"]],

307 scope: Scope,

308) -> Dict[nodes.Item, None]:

309 if scope is Scope.Function or len(items) < 3:

310 return items

311 ignore: Set[Optional[_Key]] = set()

312 items_deque = deque(items)

313 items_done: Dict[nodes.Item, None] = {}

314 scoped_items_by_argkey = items_by_argkey[scope]

315 scoped_argkeys_cache = argkeys_cache[scope]

316 while items_deque:

317 no_argkey_group: Dict[nodes.Item, None] = {}

318 slicing_argkey = None

319 while items_deque:

320 item = items_deque.popleft()

321 if item in items_done or item in no_argkey_group:

322 continue

323 argkeys = dict.fromkeys(

324 (k for k in scoped_argkeys_cache.get(item, []) if k not in ignore), None

325 )

326 if not argkeys:

327 no_argkey_group[item] = None

328 else:

329 slicing_argkey, _ = argkeys.popitem()

330 # We don't have to remove relevant items from later in the

331 # deque because they'll just be ignored.

332 matching_items = [

333 i for i in scoped_items_by_argkey[slicing_argkey] if i in items

334 ]

335 for i in reversed(matching_items):

336 fix_cache_order(i, argkeys_cache, items_by_argkey)

337 items_deque.appendleft(i)

338 break

339 if no_argkey_group:

340 no_argkey_group = reorder_items_atscope(

341 no_argkey_group, argkeys_cache, items_by_argkey, scope.next_lower()

342 )

343 for item in no_argkey_group:

344 items_done[item] = None

345 ignore.add(slicing_argkey)

346 return items_done

347

348

349 def get_direct_param_fixture_func(request: "FixtureRequest") -> Any:

350 return request.param

351

352

353@attr.s(slots=True, auto_attribs=True)

354 class FuncFixtureInfo:

355 # Original function argument names.

356 argnames: Tuple[str, ...]

357 # Argnames that function immediately requires. These include argnames +

358 # fixture names specified via usefixtures and via autouse=True in fixture

359 # definitions.

360 initialnames: Tuple[str, ...]

361 names_closure: List[str]

362 name2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]]

363

364 def prune_dependency_tree(self) -> None:

365 """Recompute names_closure from initialnames and name2fixturedefs.

366

367 Can only reduce names_closure, which means that the new closure will

368 always be a subset of the old one. The order is preserved.

369

370 This method is needed because direct parametrization may shadow some

371 of the fixtures that were included in the originally built dependency

372 tree. In this way the dependency tree can get pruned, and the closure

373 of argnames may get reduced.

374 """

375 closure: Set[str] = set()

376 working_set = set(self.initialnames)

377 while working_set:

378 argname = working_set.pop()

379 # Argname may be smth not included in the original names_closure,

380 # in which case we ignore it. This currently happens with pseudo

381 # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'.

382 # So they introduce the new dependency 'request' which might have

383 # been missing in the original tree (closure).

384 if argname not in closure and argname in self.names_closure:

385 closure.add(argname)

386 if argname in self.name2fixturedefs:

387 working_set.update(self.name2fixturedefs[argname][-1].argnames)

388

389 self.names_closure[:] = sorted(closure, key=self.names_closure.index)

390

391

392 class FixtureRequest:

393 """A request for a fixture from a test or fixture function.

394

395 A request object gives access to the requesting test context and has

396 an optional ``param`` attribute in case the fixture is parametrized

397 indirectly.

398 """

399

400 def __init__(self, pyfuncitem, *, _ispytest: bool = False) -> None:

401 check_ispytest(_ispytest)

402 self._pyfuncitem = pyfuncitem

403 #: Fixture for which this request is being performed.

404 self.fixturename: Optional[str] = None

405 self._scope = Scope.Function

406 self._fixture_defs: Dict[str, FixtureDef[Any]] = {}

407 fixtureinfo: FuncFixtureInfo = pyfuncitem._fixtureinfo

408 self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy()

409 self._arg2index: Dict[str, int] = {}

410 self._fixturemanager: FixtureManager = pyfuncitem.session._fixturemanager

411 # Notes on the type of `param`:

412 # -`request.param` is only defined in parametrized fixtures, and will raise

413 # AttributeError otherwise. Python typing has no notion of "undefined", so

414 # this cannot be reflected in the type.

415 # - Technically `param` is only (possibly) defined on SubRequest, not

416 # FixtureRequest, but the typing of that is still in flux so this cheats.

417 # - In the future we might consider using a generic for the param type, but

418 # for now just using Any.

419 self.param: Any

420

421 @property

422 def scope(self) -> "_ScopeName":

423 """Scope string, one of "function", "class", "module", "package", "session"."""

424 return self._scope.value

425

426 @property

427 def fixturenames(self) -> List[str]:

428 """Names of all active fixtures in this request."""

429 result = list(self._pyfuncitem._fixtureinfo.names_closure)

430 result.extend(set(self._fixture_defs).difference(result))

431 return result

432

433 @property

434 def node(self):

435 """Underlying collection node (depends on current request scope)."""

436 return self._getscopeitem(self._scope)

437

438 def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]":

439 fixturedefs = self._arg2fixturedefs.get(argname, None)

440 if fixturedefs is None:

441 # We arrive here because of a dynamic call to

442 # getfixturevalue(argname) usage which was naturally

443 # not known at parsing/collection time.

444 assert self._pyfuncitem.parent is not None

445 parentid = self._pyfuncitem.parent.nodeid

446 fixturedefs = self._fixturemanager.getfixturedefs(argname, parentid)

447 # TODO: Fix this type ignore. Either add assert or adjust types.

448 # Can this be None here?

449 self._arg2fixturedefs[argname] = fixturedefs # type: ignore[assignment]

450 # fixturedefs list is immutable so we maintain a decreasing index.

451 index = self._arg2index.get(argname, 0) - 1

452 if fixturedefs is None or (-index > len(fixturedefs)):

453 raise FixtureLookupError(argname, self)

454 self._arg2index[argname] = index

455 return fixturedefs[index]

456

457 @property

458 def config(self) -> Config:

459 """The pytest config object associated with this request."""

460 return self._pyfuncitem.config # type: ignore[no-any-return]

461

462 @property

463 def function(self):

464 """Test function object if the request has a per-function scope."""

465 if self.scope != "function":

466 raise AttributeError(

467 f"function not available in {self.scope}-scoped context"

468 )

469 return self._pyfuncitem.obj

470

471 @property

472 def cls(self):

473 """Class (can be None) where the test function was collected."""

474 if self.scope not in ("class", "function"):

475 raise AttributeError(f"cls not available in {self.scope}-scoped context")

476 clscol = self._pyfuncitem.getparent(_pytest.python.Class)

477 if clscol:

478 return clscol.obj

479

480 @property

481 def instance(self):

482 """Instance (can be None) on which test function was collected."""

483 # unittest support hack, see _pytest.unittest.TestCaseFunction.

484 try:

485 return self._pyfuncitem._testcase

486 except AttributeError:

487 function = getattr(self, "function", None)

488 return getattr(function, "__self__", None)

489

490 @property

491 def module(self):

492 """Python module object where the test function was collected."""

493 if self.scope not in ("function", "class", "module"):

494 raise AttributeError(f"module not available in {self.scope}-scoped context")

495 return self._pyfuncitem.getparent(_pytest.python.Module).obj

496

497 @property

498 def path(self) -> Path:

499 """Path where the test function was collected."""

500 if self.scope not in ("function", "class", "module", "package"):

501 raise AttributeError(f"path not available in {self.scope}-scoped context")

502 # TODO: Remove ignore once _pyfuncitem is properly typed.

503 return self._pyfuncitem.path # type: ignore

504

505 @property

506 def keywords(self) -> MutableMapping[str, Any]:

507 """Keywords/markers dictionary for the underlying node."""

508 node: nodes.Node = self.node

509 return node.keywords

510

511 @property

512 def session(self) -> "Session":

513 """Pytest session object."""

514 return self._pyfuncitem.session # type: ignore[no-any-return]

515

516 def addfinalizer(self, finalizer: Callable[[], object]) -> None:

517 """Add finalizer/teardown function to be called without arguments after

518 the last test within the requesting test context finished execution."""

519 # XXX usually this method is shadowed by fixturedef specific ones.

520 self._addfinalizer(finalizer, scope=self.scope)

521

522 def _addfinalizer(self, finalizer: Callable[[], object], scope) -> None:

523 node = self._getscopeitem(scope)

524 node.addfinalizer(finalizer)

525

526 def applymarker(self, marker: Union[str, MarkDecorator]) -> None:

527 """Apply a marker to a single test function invocation.

528

529 This method is useful if you don't want to have a keyword/marker

530 on all function invocations.

531

532 :param marker:

533 An object created by a call to ``pytest.mark.NAME(...)``.

534 """

535 self.node.add_marker(marker)

536

537 def raiseerror(self, msg: Optional[str]) -> NoReturn:

538 """Raise a FixtureLookupError exception.

539

540 :param msg:

541 An optional custom error message.

542 """

543 raise self._fixturemanager.FixtureLookupError(None, self, msg)

544

545 def _fillfixtures(self) -> None:

546 item = self._pyfuncitem

547 fixturenames = getattr(item, "fixturenames", self.fixturenames)

548 for argname in fixturenames:

549 if argname not in item.funcargs:

550 item.funcargs[argname] = self.getfixturevalue(argname)

551

552 def getfixturevalue(self, argname: str) -> Any:

553 """Dynamically run a named fixture function.

554

555 Declaring fixtures via function argument is recommended where possible.

556 But if you can only decide whether to use another fixture at test

557 setup time, you may use this function to retrieve it inside a fixture

558 or test function body.

559

560 This method can be used during the test setup phase or the test run

561 phase, but during the test teardown phase a fixture's value may not

562 be available.

563

564 :param argname:

565 The fixture name.

566 :raises pytest.FixtureLookupError:

567 If the given fixture could not be found.

568 """

569 fixturedef = self._get_active_fixturedef(argname)

570 assert fixturedef.cached_result is not None, (

571 f'The fixture value for "{argname}" is not available. '

572 "This can happen when the fixture has already been torn down."

573 )

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 return PseudoFixtureDef(cached_result, Scope.Function)

588 raise

589 # Remove indent to prevent the python3 exception

590 # from leaking into the call.

591 self._compute_fixture_value(fixturedef)

592 self._fixture_defs[argname] = fixturedef

593 return fixturedef

594

595 def _get_fixturestack(self) -> List["FixtureDef[Any]"]:

596 current = self

597 values: List[FixtureDef[Any]] = []

598 while isinstance(current, SubRequest):

599 values.append(current._fixturedef) # type: ignore[has-type]

600 current = current._parent_request

601 values.reverse()

602 return values

603

604 def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None:

605 """Create a SubRequest based on "self" and call the execute method

606 of the given FixtureDef object.

607

608 This will force the FixtureDef object to throw away any previous

609 results and compute a new fixture value, which will be stored into

610 the FixtureDef object itself.

611 """

612 # prepare a subrequest object before calling fixture function

613 # (latter managed by fixturedef)

614 argname = fixturedef.argname

615 funcitem = self._pyfuncitem

616 scope = fixturedef._scope

617 try:

618 callspec = funcitem.callspec

619 except AttributeError:

620 callspec = None

621 if callspec is not None and argname in callspec.params:

622 param = callspec.params[argname]

623 param_index = callspec.indices[argname]

624 # If a parametrize invocation set a scope it will override

625 # the static scope defined with the fixture function.

626 with suppress(KeyError):

627 scope = callspec._arg2scope[argname]

628 else:

629 param = NOTSET

630 param_index = 0

631 has_params = fixturedef.params is not None

632 fixtures_not_supported = getattr(funcitem, "nofuncargs", False)

633 if has_params and fixtures_not_supported:

634 msg = (

635 "{name} does not support fixtures, maybe unittest.TestCase subclass?\n"

636 "Node id: {nodeid}\n"

637 "Function type: {typename}"

638 ).format(

639 name=funcitem.name,

640 nodeid=funcitem.nodeid,

641 typename=type(funcitem).__name__,

642 )

643 fail(msg, pytrace=False)

644 if has_params:

645 frame = inspect.stack()[3]

646 frameinfo = inspect.getframeinfo(frame[0])

647 source_path = absolutepath(frameinfo.filename)

648 source_lineno = frameinfo.lineno

649 try:

650 source_path_str = str(

651 source_path.relative_to(funcitem.config.rootpath)

652 )

653 except ValueError:

654 source_path_str = str(source_path)

655 msg = (

656 "The requested fixture has no parameter defined for test:\n"

657 " {}\n\n"

658 "Requested fixture '{}' defined in:\n{}"

659 "\n\nRequested here:\n{}:{}".format(

660 funcitem.nodeid,

661 fixturedef.argname,

662 getlocation(fixturedef.func, funcitem.config.rootpath),

663 source_path_str,

664 source_lineno,

665 )

666 )

667 fail(msg, pytrace=False)

668

669 subrequest = SubRequest(

670 self, scope, param, param_index, fixturedef, _ispytest=True

671 )

672

673 # Check if a higher-level scoped fixture accesses a lower level one.

674 subrequest._check_scope(argname, self._scope, scope)

675 try:

676 # Call the fixture function.

677 fixturedef.execute(request=subrequest)

678 finally:

679 self._schedule_finalizers(fixturedef, subrequest)

680

681 def _schedule_finalizers(

682 self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest"

683 ) -> None:

684 # If fixture function failed it might have registered finalizers.

685 subrequest.node.addfinalizer(lambda: fixturedef.finish(request=subrequest))

686

687 def _check_scope(

688 self,

689 argname: str,

690 invoking_scope: Scope,

691 requested_scope: Scope,

692 ) -> None:

693 if argname == "request":

694 return

695 if invoking_scope > requested_scope:

696 # Try to report something helpful.

697 text = "\n".join(self._factorytraceback())

698 fail(

699 f"ScopeMismatch: You tried to access the {requested_scope.value} scoped "

700 f"fixture {argname} with a {invoking_scope.value} scoped request object, "

701 f"involved factories:\n{text}",

702 pytrace=False,

703 )

704

705 def _factorytraceback(self) -> List[str]:

706 lines = []

707 for fixturedef in self._get_fixturestack():

708 factory = fixturedef.func

709 fs, lineno = getfslineno(factory)

710 if isinstance(fs, Path):

711 session: Session = self._pyfuncitem.session

712 p = bestrelpath(session.path, fs)

713 else:

714 p = fs

715 args = _format_args(factory)

716 lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args))

717 return lines

718

719 def _getscopeitem(

720 self, scope: Union[Scope, "_ScopeName"]

721 ) -> Union[nodes.Item, nodes.Collector]:

722 if isinstance(scope, str):

723 scope = Scope(scope)

724 if scope is Scope.Function:

725 # This might also be a non-function Item despite its attribute name.

726 node: Optional[Union[nodes.Item, nodes.Collector]] = self._pyfuncitem

727 elif scope is Scope.Package:

728 # FIXME: _fixturedef is not defined on FixtureRequest (this class),

729 # but on FixtureRequest (a subclass).

730 node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined]

731 else:

732 node = get_scope_node(self._pyfuncitem, scope)

733 if node is None and scope is Scope.Class:

734 # Fallback to function item itself.

735 node = self._pyfuncitem

736 assert node, 'Could not obtain a node for scope "{}" for function {!r}'.format(

737 scope, self._pyfuncitem

738 )

739 return node

740

741 def __repr__(self) -> str:

742 return "<FixtureRequest for %r>" % (self.node)

743

744

745@final

746 class SubRequest(FixtureRequest):

747 """A sub request for handling getting a fixture from a test function/fixture."""

748

749 def __init__(

750 self,

751 request: "FixtureRequest",

752 scope: Scope,

753 param: Any,

754 param_index: int,

755 fixturedef: "FixtureDef[object]",

756 *,

757 _ispytest: bool = False,

758 ) -> None:

759 check_ispytest(_ispytest)

760 self._parent_request = request

761 self.fixturename = fixturedef.argname

762 if param is not NOTSET:

763 self.param = param

764 self.param_index = param_index

765 self._scope = scope

766 self._fixturedef = fixturedef

767 self._pyfuncitem = request._pyfuncitem

768 self._fixture_defs = request._fixture_defs

769 self._arg2fixturedefs = request._arg2fixturedefs

770 self._arg2index = request._arg2index

771 self._fixturemanager = request._fixturemanager

772

773 def __repr__(self) -> str:

774 return f"<SubRequest {self.fixturename!r} for {self._pyfuncitem!r}>"

775

776 def addfinalizer(self, finalizer: Callable[[], object]) -> None:

777 """Add finalizer/teardown function to be called without arguments after

778 the last test within the requesting test context finished execution."""

779 self._fixturedef.addfinalizer(finalizer)

780

781 def _schedule_finalizers(

782 self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest"

783 ) -> None:

784 # If the executing fixturedef was not explicitly requested in the argument list (via

785 # getfixturevalue inside the fixture call) then ensure this fixture def will be finished

786 # first.

787 if fixturedef.argname not in self.fixturenames:

788 fixturedef.addfinalizer(

789 functools.partial(self._fixturedef.finish, request=self)

790 )

791 super()._schedule_finalizers(fixturedef, subrequest)

792

793

794@final

795 class FixtureLookupError(LookupError):

796 """Could not return a requested fixture (missing or invalid)."""

797

798 def __init__(

799 self, argname: Optional[str], request: FixtureRequest, msg: Optional[str] = None

800 ) -> None:

801 self.argname = argname

802 self.request = request

803 self.fixturestack = request._get_fixturestack()

804 self.msg = msg

805

806 def formatrepr(self) -> "FixtureLookupErrorRepr":

807 tblines: List[str] = []

808 addline = tblines.append

809 stack = [self.request._pyfuncitem.obj]

810 stack.extend(map(lambda x: x.func, self.fixturestack))

811 msg = self.msg

812 if msg is not None:

813 # The last fixture raise an error, let's present

814 # it at the requesting side.

815 stack = stack[:-1]

816 for function in stack:

817 fspath, lineno = getfslineno(function)

818 try:

819 lines, _ = inspect.getsourcelines(get_real_func(function))

820 except (OSError, IndexError, TypeError):

821 error_msg = "file %s, line %s: source code not available"

822 addline(error_msg % (fspath, lineno + 1))

823 else:

824 addline(f"file {fspath}, line {lineno + 1}")

825 for i, line in enumerate(lines):

826 line = line.rstrip()

827 addline(" " + line)

828 if line.lstrip().startswith("def"):

829 break

830

831 if msg is None:

832 fm = self.request._fixturemanager

833 available = set()

834 parentid = self.request._pyfuncitem.parent.nodeid

835 for name, fixturedefs in fm._arg2fixturedefs.items():

836 faclist = list(fm._matchfactories(fixturedefs, parentid))

837 if faclist:

838 available.add(name)

839 if self.argname in available:

840 msg = " recursive dependency involving fixture '{}' detected".format(

841 self.argname

842 )

843 else:

844 msg = f"fixture '{self.argname}' not found"

845 msg += "\n available fixtures: {}".format(", ".join(sorted(available)))

846 msg += "\n use 'pytest --fixtures [testpath]' for help on them."

847

848 return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)

849

850

851 class FixtureLookupErrorRepr(TerminalRepr):

852 def __init__(

853 self,

854 filename: Union[str, "os.PathLike[str]"],

855 firstlineno: int,

856 tblines: Sequence[str],

857 errorstring: str,

858 argname: Optional[str],

859 ) -> None:

860 self.tblines = tblines

861 self.errorstring = errorstring

862 self.filename = filename

863 self.firstlineno = firstlineno

864 self.argname = argname

865

866 def toterminal(self, tw: TerminalWriter) -> None:

867 # tw.line("FixtureLookupError: %s" %(self.argname), red=True)

868 for tbline in self.tblines:

869 tw.line(tbline.rstrip())

870 lines = self.errorstring.split("\n")

871 if lines:

872 tw.line(

873 f"{FormattedExcinfo.fail_marker} {lines[0].strip()}",

874 red=True,

875 )

876 for line in lines[1:]:

877 tw.line(

878 f"{FormattedExcinfo.flow_marker} {line.strip()}",

879 red=True,

880 )

881 tw.line()

882 tw.line("%s:%d" % (os.fspath(self.filename), self.firstlineno + 1))

883

884

885 def fail_fixturefunc(fixturefunc, msg: str) -> NoReturn:

886 fs, lineno = getfslineno(fixturefunc)

887 location = f"{fs}:{lineno + 1}"

888 source = _pytest._code.Source(fixturefunc)

889 fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False)

890

891

892 def call_fixture_func(

893 fixturefunc: "_FixtureFunc[FixtureValue]", request: FixtureRequest, kwargs

894) -> FixtureValue:

895 if is_generator(fixturefunc):

896 fixturefunc = cast(

897 Callable[..., Generator[FixtureValue, None, None]], fixturefunc

898 )

899 generator = fixturefunc(**kwargs)

900 try:

901 fixture_result = next(generator)

902 except StopIteration:

903 raise ValueError(f"{request.fixturename} did not yield a value") from None

904 finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator)

905 request.addfinalizer(finalizer)

906 else:

907 fixturefunc = cast(Callable[..., FixtureValue], fixturefunc)

908 fixture_result = fixturefunc(**kwargs)

909 return fixture_result

910

911

912 def _teardown_yield_fixture(fixturefunc, it) -> None:

913 """Execute the teardown of a fixture function by advancing the iterator

914 after the yield and ensure the iteration ends (if not it means there is

915 more than one yield in the function)."""

916 try:

917 next(it)

918 except StopIteration:

919 pass

920 else:

921 fail_fixturefunc(fixturefunc, "fixture function has more than one 'yield'")

922

923

924 def _eval_scope_callable(

925 scope_callable: "Callable[[str, Config], _ScopeName]",

926 fixture_name: str,

927 config: Config,

928) -> "_ScopeName":

929 try:

930 # Type ignored because there is no typing mechanism to specify

931 # keyword arguments, currently.

932 result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg]

933 except Exception as e:

934 raise TypeError(

935 "Error evaluating {} while defining fixture '{}'.\n"

936 "Expected a function with the signature (*, fixture_name, config)".format(

937 scope_callable, fixture_name

938 )

939 ) from e

940 if not isinstance(result, str):

941 fail(

942 "Expected {} to return a 'str' while defining fixture '{}', but it returned:\n"

943 "{!r}".format(scope_callable, fixture_name, result),

944 pytrace=False,

945 )

946 return result

947

948

949@final

950 class FixtureDef(Generic[FixtureValue]):

951 """A container for a fixture definition."""

952

953 def __init__(

954 self,

955 fixturemanager: "FixtureManager",

956 baseid: Optional[str],

957 argname: str,

958 func: "_FixtureFunc[FixtureValue]",

959 scope: Union[Scope, "_ScopeName", Callable[[str, Config], "_ScopeName"], None],

960 params: Optional[Sequence[object]],

961 unittest: bool = False,

962 ids: Optional[

963 Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]]

964 ] = None,

965 ) -> None:

966 self._fixturemanager = fixturemanager

967 # The "base" node ID for the fixture.

968 #

969 # This is a node ID prefix. A fixture is only available to a node (e.g.

970 # a `Function` item) if the fixture's baseid is a parent of the node's

971 # nodeid (see the `iterparentnodeids` function for what constitutes a

972 # "parent" and a "prefix" in this context).

973 #

974 # For a fixture found in a Collector's object (e.g. a `Module`s module,

975 # a `Class`'s class), the baseid is the Collector's nodeid.

976 #

977 # For a fixture found in a conftest plugin, the baseid is the conftest's

978 # directory path relative to the rootdir.

979 #

980 # For other plugins, the baseid is the empty string (always matches).

981 self.baseid = baseid or ""

982 # Whether the fixture was found from a node or a conftest in the

983 # collection tree. Will be false for fixtures defined in non-conftest

984 # plugins.

985 self.has_location = baseid is not None

986 # The fixture factory function.

987 self.func = func

988 # The name by which the fixture may be requested.

989 self.argname = argname

990 if scope is None:

991 scope = Scope.Function

992 elif callable(scope):

993 scope = _eval_scope_callable(scope, argname, fixturemanager.config)

994 if isinstance(scope, str):

995 scope = Scope.from_user(

996 scope, descr=f"Fixture '{func.__name__}'", where=baseid

997 )

998 self._scope = scope

999 # If the fixture is directly parametrized, the parameter values.

1000 self.params: Optional[Sequence[object]] = params

1001 # If the fixture is directly parametrized, a tuple of explicit IDs to

1002 # assign to the parameter values, or a callable to generate an ID given

1003 # a parameter value.

1004 self.ids = ids

1005 # The names requested by the fixtures.

1006 self.argnames = getfuncargnames(func, name=argname, is_method=unittest)

1007 # Whether the fixture was collected from a unittest TestCase class.

1008 # Note that it really only makes sense to define autouse fixtures in

1009 # unittest TestCases.

1010 self.unittest = unittest

1011 # If the fixture was executed, the current value of the fixture.

1012 # Can change if the fixture is executed with different parameters.

1013 self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None

1014 self._finalizers: List[Callable[[], object]] = []

1015

1016 @property

1017 def scope(self) -> "_ScopeName":

1018 """Scope string, one of "function", "class", "module", "package", "session"."""

1019 return self._scope.value

1020

1021 def addfinalizer(self, finalizer: Callable[[], object]) -> None:

1022 self._finalizers.append(finalizer)

1023

1024 def finish(self, request: SubRequest) -> None:

1025 exc = None

1026 try:

1027 while self._finalizers:

1028 try:

1029 func = self._finalizers.pop()

1030 func()

1031 except BaseException as e:

1032 # XXX Only first exception will be seen by user,

1033 # ideally all should be reported.

1034 if exc is None:

1035 exc = e

1036 if exc:

1037 raise exc

1038 finally:

1039 ihook = request.node.ihook

1040 ihook.pytest_fixture_post_finalizer(fixturedef=self, request=request)

1041 # Even if finalization fails, we invalidate the cached fixture

1042 # value and remove all finalizers because they may be bound methods

1043 # which will keep instances alive.

1044 self.cached_result = None

1045 self._finalizers = []

1046

1047 def execute(self, request: SubRequest) -> FixtureValue:

1048 # Get required arguments and register our own finish()

1049 # with their finalization.

1050 for argname in self.argnames:

1051 fixturedef = request._get_active_fixturedef(argname)

1052 if argname != "request":

1053 # PseudoFixtureDef is only for "request".

1054 assert isinstance(fixturedef, FixtureDef)

1055 fixturedef.addfinalizer(functools.partial(self.finish, request=request))

1056

1057 my_cache_key = self.cache_key(request)

1058 if self.cached_result is not None:

1059 # note: comparison with `==` can fail (or be expensive) for e.g.

1060 # numpy arrays (#6497).

1061 cache_key = self.cached_result[1]

1062 if my_cache_key is cache_key:

1063 if self.cached_result[2] is not None:

1064 _, val, tb = self.cached_result[2]

1065 raise val.with_traceback(tb)

1066 else:

1067 result = self.cached_result[0]

1068 return result

1069 # We have a previous but differently parametrized fixture instance

1070 # so we need to tear it down before creating a new one.

1071 self.finish(request)

1072 assert self.cached_result is None

1073

1074 ihook = request.node.ihook

1075 result = ihook.pytest_fixture_setup(fixturedef=self, request=request)

1076 return result

1077

1078 def cache_key(self, request: SubRequest) -> object:

1079 return request.param_index if not hasattr(request, "param") else request.param

1080

1081 def __repr__(self) -> str:

1082 return "<FixtureDef argname={!r} scope={!r} baseid={!r}>".format(

1083 self.argname, self.scope, self.baseid

1084 )

1085

1086

1087 def resolve_fixture_function(

1088 fixturedef: FixtureDef[FixtureValue], request: FixtureRequest

1089) -> "_FixtureFunc[FixtureValue]":

1090 """Get the actual callable that can be called to obtain the fixture

1091 value, dealing with unittest-specific instances and bound methods."""

1092 fixturefunc = fixturedef.func

1093 if fixturedef.unittest:

1094 if request.instance is not None:

1095 # Bind the unbound method to the TestCase instance.

1096 fixturefunc = fixturedef.func.__get__(request.instance) # type: ignore[union-attr]

1097 else:

1098 # The fixture function needs to be bound to the actual

1099 # request.instance so that code working with "fixturedef" behaves

1100 # as expected.

1101 if request.instance is not None:

1102 # Handle the case where fixture is defined not in a test class, but some other class

1103 # (for example a plugin class with a fixture), see #2270.

1104 if hasattr(fixturefunc, "__self__") and not isinstance(

1105 request.instance, fixturefunc.__self__.__class__ # type: ignore[union-attr]

1106 ):

1107 return fixturefunc

1108 fixturefunc = getimfunc(fixturedef.func)

1109 if fixturefunc != fixturedef.func:

1110 fixturefunc = fixturefunc.__get__(request.instance) # type: ignore[union-attr]

1111 return fixturefunc

1112

1113

1114 def pytest_fixture_setup(

1115 fixturedef: FixtureDef[FixtureValue], request: SubRequest

1116) -> FixtureValue:

1117 """Execution of fixture setup."""

1118 kwargs = {}

1119 for argname in fixturedef.argnames:

1120 fixdef = request._get_active_fixturedef(argname)

1121 assert fixdef.cached_result is not None

1122 result, arg_cache_key, exc = fixdef.cached_result

1123 request._check_scope(argname, request._scope, fixdef._scope)

1124 kwargs[argname] = result

1125

1126 fixturefunc = resolve_fixture_function(fixturedef, request)

1127 my_cache_key = fixturedef.cache_key(request)

1128 try:

1129 result = call_fixture_func(fixturefunc, request, kwargs)

1130 except TEST_OUTCOME:

1131 exc_info = sys.exc_info()

1132 assert exc_info[0] is not None

1133 if isinstance(

1134 exc_info[1], skip.Exception

1135 ) and not fixturefunc.__name__.startswith("xunit_setup"):

1136 exc_info[1]._use_item_location = True # type: ignore[attr-defined]

1137 fixturedef.cached_result = (None, my_cache_key, exc_info)

1138 raise

1139 fixturedef.cached_result = (result, my_cache_key, None)

1140 return result

1141

1142

1143 def _ensure_immutable_ids(

1144 ids: Optional[Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]]]

1145) -> Optional[Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]]]:

1146 if ids is None:

1147 return None

1148 if callable(ids):

1149 return ids

1150 return tuple(ids)

1151

1152

1153 def _params_converter(

1154 params: Optional[Iterable[object]],

1155) -> Optional[Tuple[object, ...]]:

1156 return tuple(params) if params is not None else None

1157

1158

1159 def wrap_function_to_error_out_if_called_directly(

1160 function: FixtureFunction,

1161 fixture_marker: "FixtureFunctionMarker",

1162) -> FixtureFunction:

1163 """Wrap the given fixture function so we can raise an error about it being called directly,

1164 instead of used as an argument in a test function."""

1165 message = (

1166 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n'

1167 "but are created automatically when test functions request them as parameters.\n"

1168 "See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n"

1169 "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code."

1170 ).format(name=fixture_marker.name or function.__name__)

1171

1172 @functools.wraps(function)

1173 def result(*args, **kwargs):

1174 fail(message, pytrace=False)

1175

1176 # Keep reference to the original function in our own custom attribute so we don't unwrap

1177 # further than this point and lose useful wrappings like @mock.patch (#3774).

1178 result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined]

1179

1180 return cast(FixtureFunction, result)

1181

1182

1183@final

1184@attr.s(frozen=True, auto_attribs=True)

1185 class FixtureFunctionMarker:

1186 scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]"

1187 params: Optional[Tuple[object, ...]] = attr.ib(converter=_params_converter)

1188 autouse: bool = False

1189 ids: Optional[

1190 Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]]

1191 ] = attr.ib(

1192 default=None,

1193 converter=_ensure_immutable_ids,

1194 )

1195 name: Optional[str] = None

1196

1197 def __call__(self, function: FixtureFunction) -> FixtureFunction:

1198 if inspect.isclass(function):

1199 raise ValueError("class fixtures not supported (maybe in the future)")

1200

1201 if getattr(function, "_pytestfixturefunction", False):

1202 raise ValueError(

1203 "fixture is being applied more than once to the same function"

1204 )

1205

1206 function = wrap_function_to_error_out_if_called_directly(function, self)

1207

1208 name = self.name or function.__name__

1209 if name == "request":

1210 location = getlocation(function)

1211 fail(

1212 "'request' is a reserved word for fixtures, use another name:\n {}".format(

1213 location

1214 ),

1215 pytrace=False,

1216 )

1217

1218 # Type ignored because https://github.com/python/mypy/issues/2087.

1219 function._pytestfixturefunction = self # type: ignore[attr-defined]

1220 return function

1221

1222

1223@overload

1224 def fixture(

1225 fixture_function: FixtureFunction,

1226 *,

1227 scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ...,

1228 params: Optional[Iterable[object]] = ...,

1229 autouse: bool = ...,

1230 ids: Optional[

1231 Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]]

1232 ] = ...,

1233 name: Optional[str] = ...,

1234) -> FixtureFunction:

1235 ...

1236

1237

1238@overload

1239 def fixture( # noqa: F811

1240 fixture_function: None = ...,

1241 *,

1242 scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ...,

1243 params: Optional[Iterable[object]] = ...,

1244 autouse: bool = ...,

1245 ids: Optional[

1246 Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]]

1247 ] = ...,

1248 name: Optional[str] = None,

1249) -> FixtureFunctionMarker:

1250 ...

1251

1252

1253 def fixture( # noqa: F811

1254 fixture_function: Optional[FixtureFunction] = None,

1255 *,

1256 scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = "function",

1257 params: Optional[Iterable[object]] = None,

1258 autouse: bool = False,

1259 ids: Optional[

1260 Union[Sequence[Optional[object]], Callable[[Any], Optional[object]]]

1261 ] = None,

1262 name: Optional[str] = None,

1263) -> Union[FixtureFunctionMarker, FixtureFunction]:

1264 """Decorator to mark a fixture factory function.

1265

1266 This decorator can be used, with or without parameters, to define a

1267 fixture function.

1268

1269 The name of the fixture function can later be referenced to cause its

1270 invocation ahead of running tests: test modules or classes can use the

1271 ``pytest.mark.usefixtures(fixturename)`` marker.

1272

1273 Test functions can directly use fixture names as input arguments in which

1274 case the fixture instance returned from the fixture function will be

1275 injected.

1276

1277 Fixtures can provide their values to test functions using ``return`` or

1278 ``yield`` statements. When using ``yield`` the code block after the

1279 ``yield`` statement is executed as teardown code regardless of the test

1280 outcome, and must yield exactly once.

1281

1282 :param scope:

1283 The scope for which this fixture is shared; one of ``"function"``

1284 (default), ``"class"``, ``"module"``, ``"package"`` or ``"session"``.

1285

1286 This parameter may also be a callable which receives ``(fixture_name, config)``

1287 as parameters, and must return a ``str`` with one of the values mentioned above.

1288

1289 See :ref:`dynamic scope` in the docs for more information.

1290

1291 :param params:

1292 An optional list of parameters which will cause multiple invocations

1293 of the fixture function and all of the tests using it. The current

1294 parameter is available in ``request.param``.

1295

1296 :param autouse:

1297 If True, the fixture func is activated for all tests that can see it.

1298 If False (the default), an explicit reference is needed to activate

1299 the fixture.

1300

1301 :param ids:

1302 Sequence of ids each corresponding to the params so that they are

1303 part of the test id. If no ids are provided they will be generated

1304 automatically from the params.

1305

1306 :param name:

1307 The name of the fixture. This defaults to the name of the decorated

1308 function. If a fixture is used in the same module in which it is

1309 defined, the function name of the fixture will be shadowed by the

1310 function arg that requests the fixture; one way to resolve this is to

1311 name the decorated function ``fixture_<fixturename>`` and then use

1312 ``@pytest.fixture(name='<fixturename>')``.

1313 """

1314 fixture_marker = FixtureFunctionMarker(

1315 scope=scope,

1316 params=params,

1317 autouse=autouse,

1318 ids=ids,

1319 name=name,

1320 )

1321

1322 # Direct decoration.

1323 if fixture_function:

1324 return fixture_marker(fixture_function)

1325

1326 return fixture_marker

1327

1328

1329 def yield_fixture(

1330 fixture_function=None,

1331 *args,

1332 scope="function",

1333 params=None,

1334 autouse=False,

1335 ids=None,

1336 name=None,

1337):

1338 """(Return a) decorator to mark a yield-fixture factory function.

1339

1340 .. deprecated:: 3.0

1341 Use :py:func:`pytest.fixture` directly instead.

1342 """

1343 warnings.warn(YIELD_FIXTURE, stacklevel=2)

1344 return fixture(

1345 fixture_function,

1346 *args,

1347 scope=scope,

1348 params=params,

1349 autouse=autouse,

1350 ids=ids,

1351 name=name,

1352 )

1353

1354

1355@fixture(scope="session")

1356 def pytestconfig(request: FixtureRequest) -> Config:

1357 """Session-scoped fixture that returns the session's :class:`pytest.Config`

1358 object.

1359

1360 Example::

1361

1362 def test_foo(pytestconfig):

1363 if pytestconfig.getoption("verbose") > 0:

1364 ...

1365

1366 """

1367 return request.config

1368

1369

1370 def pytest_addoption(parser: Parser) -> None:

1371 parser.addini(

1372 "usefixtures",

1373 type="args",

1374 default=[],

1375 help="List of default fixtures to be used with this project",

1376 )

1377

1378

1379 class FixtureManager:

1380 """pytest fixture definitions and information is stored and managed

1381 from this class.

1382

1383 During collection fm.parsefactories() is called multiple times to parse

1384 fixture function definitions into FixtureDef objects and internal

1385 data structures.

1386

1387 During collection of test functions, metafunc-mechanics instantiate

1388 a FuncFixtureInfo object which is cached per node/func-name.

1389 This FuncFixtureInfo object is later retrieved by Function nodes

1390 which themselves offer a fixturenames attribute.

1391

1392 The FuncFixtureInfo object holds information about fixtures and FixtureDefs

1393 relevant for a particular function. An initial list of fixtures is

1394 assembled like this:

1395

1396 - ini-defined usefixtures

1397 - autouse-marked fixtures along the collection chain up from the function

1398 - usefixtures markers at module/class/function level

1399 - test function funcargs

1400

1401 Subsequently the funcfixtureinfo.fixturenames attribute is computed

1402 as the closure of the fixtures needed to setup the initial fixtures,

1403 i.e. fixtures needed by fixture functions themselves are appended

1404 to the fixturenames list.

1405

1406 Upon the test-setup phases all fixturenames are instantiated, retrieved

1407 by a lookup of their FuncFixtureInfo.

1408 """

1409

1410 FixtureLookupError = FixtureLookupError

1411 FixtureLookupErrorRepr = FixtureLookupErrorRepr

1412

1413 def __init__(self, session: "Session") -> None:

1414 self.session = session

1415 self.config: Config = session.config

1416 self._arg2fixturedefs: Dict[str, List[FixtureDef[Any]]] = {}

1417 self._holderobjseen: Set[object] = set()

1418 # A mapping from a nodeid to a list of autouse fixtures it defines.

1419 self._nodeid_autousenames: Dict[str, List[str]] = {

1420 "": self.config.getini("usefixtures"),

1421 }

1422 session.config.pluginmanager.register(self, "funcmanage")

1423

1424 def _get_direct_parametrize_args(self, node: nodes.Node) -> List[str]:

1425 """Return all direct parametrization arguments of a node, so we don't

1426 mistake them for fixtures.

1427

1428 Check https://github.com/pytest-dev/pytest/issues/5036.

1429

1430 These things are done later as well when dealing with parametrization

1431 so this could be improved.

1432 """

1433 parametrize_argnames: List[str] = []

1434 for marker in node.iter_markers(name="parametrize"):

1435 if not marker.kwargs.get("indirect", False):

1436 p_argnames, _ = ParameterSet._parse_parametrize_args(

1437 *marker.args, **marker.kwargs

1438 )

1439 parametrize_argnames.extend(p_argnames)

1440

1441 return parametrize_argnames

1442

1443 def getfixtureinfo(

1444 self, node: nodes.Node, func, cls, funcargs: bool = True

1445 ) -> FuncFixtureInfo:

1446 if funcargs and not getattr(node, "nofuncargs", False):

1447 argnames = getfuncargnames(func, name=node.name, cls=cls)

1448 else:

1449 argnames = ()

1450

1451 usefixtures = tuple(

1452 arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args

1453 )

1454 initialnames = usefixtures + argnames

1455 fm = node.session._fixturemanager

1456 initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure(

1457 initialnames, node, ignore_args=self._get_direct_parametrize_args(node)

1458 )

1459 return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)

1460

1461 def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None:

1462 nodeid = None

1463 try:

1464 p = absolutepath(plugin.__file__) # type: ignore[attr-defined]

1465 except AttributeError:

1466 pass

1467 else:

1468 # Construct the base nodeid which is later used to check

1469 # what fixtures are visible for particular tests (as denoted

1470 # by their test id).

1471 if p.name.startswith("conftest.py"):

1472 try:

1473 nodeid = str(p.parent.relative_to(self.config.rootpath))

1474 except ValueError:

1475 nodeid = ""

1476 if nodeid == ".":

1477 nodeid = ""

1478 if os.sep != nodes.SEP:

1479 nodeid = nodeid.replace(os.sep, nodes.SEP)

1480

1481 self.parsefactories(plugin, nodeid)

1482

1483 def _getautousenames(self, nodeid: str) -> Iterator[str]:

1484 """Return the names of autouse fixtures applicable to nodeid."""

1485 for parentnodeid in nodes.iterparentnodeids(nodeid):

1486 basenames = self._nodeid_autousenames.get(parentnodeid)

1487 if basenames:

1488 yield from basenames

1489

1490 def getfixtureclosure(

1491 self,

1492 fixturenames: Tuple[str, ...],

1493 parentnode: nodes.Node,

1494 ignore_args: Sequence[str] = (),

1495 ) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef[Any]]]]:

1496 # Collect the closure of all fixtures, starting with the given

1497 # fixturenames as the initial set. As we have to visit all

1498 # factory definitions anyway, we also return an arg2fixturedefs

1499 # mapping so that the caller can reuse it and does not have

1500 # to re-discover fixturedefs again for each fixturename

1501 # (discovering matching fixtures for a given name/node is expensive).

1502

1503 parentid = parentnode.nodeid

1504 fixturenames_closure = list(self._getautousenames(parentid))

1505

1506 def merge(otherlist: Iterable[str]) -> None:

1507 for arg in otherlist:

1508 if arg not in fixturenames_closure:

1509 fixturenames_closure.append(arg)

1510

1511 merge(fixturenames)

1512

1513 # At this point, fixturenames_closure contains what we call "initialnames",

1514 # which is a set of fixturenames the function immediately requests. We

1515 # need to return it as well, so save this.

1516 initialnames = tuple(fixturenames_closure)

1517

1518 arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {}

1519 lastlen = -1

1520 while lastlen != len(fixturenames_closure):

1521 lastlen = len(fixturenames_closure)

1522 for argname in fixturenames_closure:

1523 if argname in ignore_args:

1524 continue

1525 if argname in arg2fixturedefs:

1526 continue

1527 fixturedefs = self.getfixturedefs(argname, parentid)

1528 if fixturedefs:

1529 arg2fixturedefs[argname] = fixturedefs

1530 merge(fixturedefs[-1].argnames)

1531

1532 def sort_by_scope(arg_name: str) -> Scope:

1533 try:

1534 fixturedefs = arg2fixturedefs[arg_name]

1535 except KeyError:

1536 return Scope.Function

1537 else:

1538 return fixturedefs[-1]._scope

1539

1540 fixturenames_closure.sort(key=sort_by_scope, reverse=True)

1541 return initialnames, fixturenames_closure, arg2fixturedefs

1542

1543 def pytest_generate_tests(self, metafunc: "Metafunc") -> None:

1544 """Generate new tests based on parametrized fixtures used by the given metafunc"""

1545

1546 def get_parametrize_mark_argnames(mark: Mark) -> Sequence[str]:

1547 args, _ = ParameterSet._parse_parametrize_args(*mark.args, **mark.kwargs)

1548 return args

1549

1550 for argname in metafunc.fixturenames:

1551 # Get the FixtureDefs for the argname.

1552 fixture_defs = metafunc._arg2fixturedefs.get(argname)

1553 if not fixture_defs:

1554 # Will raise FixtureLookupError at setup time if not parametrized somewhere

1555 # else (e.g @pytest.mark.parametrize)

1556 continue

1557

1558 # If the test itself parametrizes using this argname, give it

1559 # precedence.

1560 if any(

1561 argname in get_parametrize_mark_argnames(mark)

1562 for mark in metafunc.definition.iter_markers("parametrize")

1563 ):

1564 continue

1565

1566 # In the common case we only look at the fixture def with the

1567 # closest scope (last in the list). But if the fixture overrides

1568 # another fixture, while requesting the super fixture, keep going

1569 # in case the super fixture is parametrized (#1953).

1570 for fixturedef in reversed(fixture_defs):

1571 # Fixture is parametrized, apply it and stop.

1572 if fixturedef.params is not None:

1573 metafunc.parametrize(

1574 argname,

1575 fixturedef.params,

1576 indirect=True,

1577 scope=fixturedef.scope,

1578 ids=fixturedef.ids,

1579 )

1580 break

1581

1582 # Not requesting the overridden super fixture, stop.

1583 if argname not in fixturedef.argnames:

1584 break

1585

1586 # Try next super fixture, if any.

1587

1588 def pytest_collection_modifyitems(self, items: List[nodes.Item]) -> None:

1589 # Separate parametrized setups.

1590 items[:] = reorder_items(items)

1591

1592 def parsefactories(

1593 self, node_or_obj, nodeid=NOTSET, unittest: bool = False

1594 ) -> None:

1595 if nodeid is not NOTSET:

1596 holderobj = node_or_obj

1597 else:

1598 holderobj = node_or_obj.obj

1599 nodeid = node_or_obj.nodeid

1600 if holderobj in self._holderobjseen:

1601 return

1602

1603 self._holderobjseen.add(holderobj)

1604 autousenames = []

1605 for name in dir(holderobj):

1606 # ugly workaround for one of the fspath deprecated property of node

1607 # todo: safely generalize

1608 if isinstance(holderobj, nodes.Node) and name == "fspath":

1609 continue

1610

1611 # The attribute can be an arbitrary descriptor, so the attribute

1612 # access below can raise. safe_getatt() ignores such exceptions.

1613 obj = safe_getattr(holderobj, name, None)

1614 marker = getfixturemarker(obj)

1615 if not isinstance(marker, FixtureFunctionMarker):

1616 # Magic globals with __getattr__ might have got us a wrong

1617 # fixture attribute.

1618 continue

1619

1620 if marker.name:

1621 name = marker.name

1622

1623 # During fixture definition we wrap the original fixture function

1624 # to issue a warning if called directly, so here we unwrap it in

1625 # order to not emit the warning when pytest itself calls the

1626 # fixture function.

1627 obj = get_real_method(obj, holderobj)

1628

1629 fixture_def = FixtureDef(

1630 fixturemanager=self,

1631 baseid=nodeid,

1632 argname=name,

1633 func=obj,

1634 scope=marker.scope,

1635 params=marker.params,

1636 unittest=unittest,

1637 ids=marker.ids,

1638 )

1639

1640 faclist = self._arg2fixturedefs.setdefault(name, [])

1641 if fixture_def.has_location:

1642 faclist.append(fixture_def)

1643 else:

1644 # fixturedefs with no location are at the front

1645 # so this inserts the current fixturedef after the

1646 # existing fixturedefs from external plugins but

1647 # before the fixturedefs provided in conftests.

1648 i = len([f for f in faclist if not f.has_location])

1649 faclist.insert(i, fixture_def)

1650 if marker.autouse:

1651 autousenames.append(name)

1652

1653 if autousenames:

1654 self._nodeid_autousenames.setdefault(nodeid or "", []).extend(autousenames)

1655

1656 def getfixturedefs(

1657 self, argname: str, nodeid: str

1658 ) -> Optional[Sequence[FixtureDef[Any]]]:

1659 """Get a list of fixtures which are applicable to the given node id.

1660

1661 :param str argname: Name of the fixture to search for.

1662 :param str nodeid: Full node id of the requesting test.

1663 :rtype: Sequence[FixtureDef]

1664 """

1665 try:

1666 fixturedefs = self._arg2fixturedefs[argname]

1667 except KeyError:

1668 return None

1669 return tuple(self._matchfactories(fixturedefs, nodeid))

1670

1671 def _matchfactories(

1672 self, fixturedefs: Iterable[FixtureDef[Any]], nodeid: str

1673 ) -> Iterator[FixtureDef[Any]]:

1674 parentnodeids = set(nodes.iterparentnodeids(nodeid))

1675 for fixturedef in fixturedefs:

1676 if fixturedef.baseid in parentnodeids:

1677 yield fixturedef