1"""Python test discovery, setup and run of test functions."""

2 import enum

3 import fnmatch

4 import inspect

5 import itertools

6 import os

7 import sys

8 import types

9 import warnings

10 from collections import Counter

11 from collections import defaultdict

12 from functools import partial

13 from pathlib import Path

14 from typing import Any

15 from typing import Callable

16 from typing import Dict

17 from typing import Generator

18 from typing import Iterable

19 from typing import Iterator

20 from typing import List

21 from typing import Mapping

22 from typing import Optional

23 from typing import Pattern

24 from typing import Sequence

25 from typing import Set

26 from typing import Tuple

27 from typing import TYPE_CHECKING

28 from typing import Union

29

30 import attr

31

32 import _pytest

33 from _pytest import fixtures

34 from _pytest import nodes

35 from _pytest._code import filter_traceback

36 from _pytest._code import getfslineno

37 from _pytest._code.code import ExceptionInfo

38 from _pytest._code.code import TerminalRepr

39 from _pytest._io import TerminalWriter

40 from _pytest._io.saferepr import saferepr

41 from _pytest.compat import ascii_escaped

42 from _pytest.compat import assert_never

43 from _pytest.compat import final

44 from _pytest.compat import get_default_arg_names

45 from _pytest.compat import get_real_func

46 from _pytest.compat import getimfunc

47 from _pytest.compat import getlocation

48 from _pytest.compat import is_async_function

49 from _pytest.compat import is_generator

50 from _pytest.compat import LEGACY_PATH

51 from _pytest.compat import NOTSET

52 from _pytest.compat import safe_getattr

53 from _pytest.compat import safe_isclass

54 from _pytest.compat import STRING_TYPES

55 from _pytest.config import Config

56 from _pytest.config import ExitCode

57 from _pytest.config import hookimpl

58 from _pytest.config.argparsing import Parser

59 from _pytest.deprecated import check_ispytest

60 from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH

61 from _pytest.deprecated import INSTANCE_COLLECTOR

62 from _pytest.deprecated import NOSE_SUPPORT_METHOD

63 from _pytest.fixtures import FuncFixtureInfo

64 from _pytest.main import Session

65 from _pytest.mark import MARK_GEN

66 from _pytest.mark import ParameterSet

67 from _pytest.mark.structures import get_unpacked_marks

68 from _pytest.mark.structures import Mark

69 from _pytest.mark.structures import MarkDecorator

70 from _pytest.mark.structures import normalize_mark_list

71 from _pytest.outcomes import fail

72 from _pytest.outcomes import skip

73 from _pytest.pathlib import bestrelpath

74 from _pytest.pathlib import fnmatch_ex

75 from _pytest.pathlib import import_path

76 from _pytest.pathlib import ImportPathMismatchError

77 from _pytest.pathlib import parts

78 from _pytest.pathlib import visit

79 from _pytest.scope import Scope

80 from _pytest.warning_types import PytestCollectionWarning

81 from _pytest.warning_types import PytestReturnNotNoneWarning

82 from _pytest.warning_types import PytestUnhandledCoroutineWarning

83

84 if TYPE_CHECKING:

85 from typing_extensions import Literal

86

87 from _pytest.scope import _ScopeName

88

89

90_PYTEST_DIR = Path(_pytest.__file__).parent

91

92

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

94 group = parser.getgroup("general")

95 group.addoption(

96 "--fixtures",

97 "--funcargs",

98 action="store_true",

99 dest="showfixtures",

100 default=False,

101 help="Show available fixtures, sorted by plugin appearance "

102 "(fixtures with leading '_' are only shown with '-v')",

103 )

104 group.addoption(

105 "--fixtures-per-test",

106 action="store_true",

107 dest="show_fixtures_per_test",

108 default=False,

109 help="Show fixtures per test",

110 )

111 parser.addini(

112 "python_files",

113 type="args",

114 # NOTE: default is also used in AssertionRewritingHook.

115 default=["test_*.py", "*_test.py"],

116 help="Glob-style file patterns for Python test module discovery",

117 )

118 parser.addini(

119 "python_classes",

120 type="args",

121 default=["Test"],

122 help="Prefixes or glob names for Python test class discovery",

123 )

124 parser.addini(

125 "python_functions",

126 type="args",

127 default=["test"],

128 help="Prefixes or glob names for Python test function and method discovery",

129 )

130 parser.addini(

131 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support",

132 type="bool",

133 default=False,

134 help="Disable string escape non-ASCII characters, might cause unwanted "

135 "side effects(use at your own risk)",

136 )

137

138

139 def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:

140 if config.option.showfixtures:

141 showfixtures(config)

142 return 0

143 if config.option.show_fixtures_per_test:

144 show_fixtures_per_test(config)

145 return 0

146 return None

147

148

149 def pytest_generate_tests(metafunc: "Metafunc") -> None:

150 for marker in metafunc.definition.iter_markers(name="parametrize"):

151 metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker)

152

153

154 def pytest_configure(config: Config) -> None:

155 config.addinivalue_line(

156 "markers",

157 "parametrize(argnames, argvalues): call a test function multiple "

158 "times passing in different arguments in turn. argvalues generally "

159 "needs to be a list of values if argnames specifies only one name "

160 "or a list of tuples of values if argnames specifies multiple names. "

161 "Example: @parametrize('arg1', [1,2]) would lead to two calls of the "

162 "decorated test function, one with arg1=1 and another with arg1=2."

163 "see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info "

164 "and examples.",

165 )

166 config.addinivalue_line(

167 "markers",

168 "usefixtures(fixturename1, fixturename2, ...): mark tests as needing "

169 "all of the specified fixtures. see "

170 "https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures ",

171 )

172

173

174 def async_warn_and_skip(nodeid: str) -> None:

175 msg = "async def functions are not natively supported and have been skipped.\n"

176 msg += (

177 "You need to install a suitable plugin for your async framework, for example:\n"

178 )

179 msg += " - anyio\n"

180 msg += " - pytest-asyncio\n"

181 msg += " - pytest-tornasync\n"

182 msg += " - pytest-trio\n"

183 msg += " - pytest-twisted"

184 warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))

185 skip(reason="async def function and no async plugin installed (see warnings)")

186

187

188@hookimpl(trylast=True)

189 def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]:

190 testfunction = pyfuncitem.obj

191 if is_async_function(testfunction):

192 async_warn_and_skip(pyfuncitem.nodeid)

193 funcargs = pyfuncitem.funcargs

194 testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}

195 result = testfunction(**testargs)

196 if hasattr(result, "__await__") or hasattr(result, "__aiter__"):

197 async_warn_and_skip(pyfuncitem.nodeid)

198 elif result is not None:

199 warnings.warn(

200 PytestReturnNotNoneWarning(

201 f"Expected None, but {pyfuncitem.nodeid} returned {result!r}, which will be an error in a "

202 "future version of pytest. Did you mean to use `assert` instead of `return`?"

203 )

204 )

205 return True

206

207

208 def pytest_collect_file(file_path: Path, parent: nodes.Collector) -> Optional["Module"]:

209 if file_path.suffix == ".py":

210 if not parent.session.isinitpath(file_path):

211 if not path_matches_patterns(

212 file_path, parent.config.getini("python_files") + ["__init__.py"]

213 ):

214 return None

215 ihook = parent.session.gethookproxy(file_path)

216 module: Module = ihook.pytest_pycollect_makemodule(

217 module_path=file_path, parent=parent

218 )

219 return module

220 return None

221

222

223 def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool:

224 """Return whether path matches any of the patterns in the list of globs given."""

225 return any(fnmatch_ex(pattern, path) for pattern in patterns)

226

227

228 def pytest_pycollect_makemodule(module_path: Path, parent) -> "Module":

229 if module_path.name == "__init__.py":

230 pkg: Package = Package.from_parent(parent, path=module_path)

231 return pkg

232 mod: Module = Module.from_parent(parent, path=module_path)

233 return mod

234

235

236@hookimpl(trylast=True)

237 def pytest_pycollect_makeitem(

238 collector: Union["Module", "Class"], name: str, obj: object

239) -> Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]:

240 assert isinstance(collector, (Class, Module)), type(collector)

241 # Nothing was collected elsewhere, let's do it here.

242 if safe_isclass(obj):

243 if collector.istestclass(obj, name):

244 klass: Class = Class.from_parent(collector, name=name, obj=obj)

245 return klass

246 elif collector.istestfunction(obj, name):

247 # mock seems to store unbound methods (issue473), normalize it.

248 obj = getattr(obj, "__func__", obj)

249 # We need to try and unwrap the function if it's a functools.partial

250 # or a functools.wrapped.

251 # We mustn't if it's been wrapped with mock.patch (python 2 only).

252 if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))):

253 filename, lineno = getfslineno(obj)

254 warnings.warn_explicit(

255 message=PytestCollectionWarning(

256 "cannot collect %r because it is not a function." % name

257 ),

258 category=None,

259 filename=str(filename),

260 lineno=lineno + 1,

261 )

262 elif getattr(obj, "__test__", True):

263 if is_generator(obj):

264 res: Function = Function.from_parent(collector, name=name)

265 reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(

266 name=name

267 )

268 res.add_marker(MARK_GEN.xfail(run=False, reason=reason))

269 res.warn(PytestCollectionWarning(reason))

270 return res

271 else:

272 return list(collector._genfunctions(name, obj))

273 return None

274

275

276 class PyobjMixin(nodes.Node):

277 """this mix-in inherits from Node to carry over the typing information

278

279 as its intended to always mix in before a node

280 its position in the mro is unaffected"""

281

282 _ALLOW_MARKERS = True

283

284 @property

285 def module(self):

286 """Python module object this node was collected from (can be None)."""

287 node = self.getparent(Module)

288 return node.obj if node is not None else None

289

290 @property

291 def cls(self):

292 """Python class object this node was collected from (can be None)."""

293 node = self.getparent(Class)

294 return node.obj if node is not None else None

295

296 @property

297 def instance(self):

298 """Python instance object the function is bound to.

299

300 Returns None if not a test method, e.g. for a standalone test function,

301 a staticmethod, a class or a module.

302 """

303 node = self.getparent(Function)

304 return getattr(node.obj, "__self__", None) if node is not None else None

305

306 @property

307 def obj(self):

308 """Underlying Python object."""

309 obj = getattr(self, "_obj", None)

310 if obj is None:

311 self._obj = obj = self._getobj()

312 # XXX evil hack

313 # used to avoid Function marker duplication

314 if self._ALLOW_MARKERS:

315 self.own_markers.extend(get_unpacked_marks(self.obj))

316 # This assumes that `obj` is called before there is a chance

317 # to add custom keys to `self.keywords`, so no fear of overriding.

318 self.keywords.update((mark.name, mark) for mark in self.own_markers)

319 return obj

320

321 @obj.setter

322 def obj(self, value):

323 self._obj = value

324

325 def _getobj(self):

326 """Get the underlying Python object. May be overwritten by subclasses."""

327 # TODO: Improve the type of `parent` such that assert/ignore aren't needed.

328 assert self.parent is not None

329 obj = self.parent.obj # type: ignore[attr-defined]

330 return getattr(obj, self.name)

331

332 def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> str:

333 """Return Python path relative to the containing module."""

334 chain = self.listchain()

335 chain.reverse()

336 parts = []

337 for node in chain:

338 name = node.name

339 if isinstance(node, Module):

340 name = os.path.splitext(name)[0]

341 if stopatmodule:

342 if includemodule:

343 parts.append(name)

344 break

345 parts.append(name)

346 parts.reverse()

347 return ".".join(parts)

348

349 def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]:

350 # XXX caching?

351 obj = self.obj

352 compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)

353 if isinstance(compat_co_firstlineno, int):

354 # nose compatibility

355 file_path = sys.modules[obj.__module__].__file__

356 assert file_path is not None

357 if file_path.endswith(".pyc"):

358 file_path = file_path[:-1]

359 path: Union["os.PathLike[str]", str] = file_path

360 lineno = compat_co_firstlineno

361 else:

362 path, lineno = getfslineno(obj)

363 modpath = self.getmodpath()

364 assert isinstance(lineno, int)

365 return path, lineno, modpath

366

367

368# As an optimization, these builtin attribute names are pre-ignored when

369# iterating over an object during collection -- the pytest_pycollect_makeitem

370# hook is not called for them.

371# fmt: off

372 class _EmptyClass: pass # noqa: E701

373 IGNORED_ATTRIBUTES = frozenset.union( # noqa: E305

374 frozenset(),

375 # Module.

376 dir(types.ModuleType("empty_module")),

377 # Some extra module attributes the above doesn't catch.

378 {"__builtins__", "__file__", "__cached__"},

379 # Class.

380 dir(_EmptyClass),

381 # Instance.

382 dir(_EmptyClass()),

383)

384 del _EmptyClass

385# fmt: on

386

387

388 class PyCollector(PyobjMixin, nodes.Collector):

389 def funcnamefilter(self, name: str) -> bool:

390 return self._matches_prefix_or_glob_option("python_functions", name)

391

392 def isnosetest(self, obj: object) -> bool:

393 """Look for the __test__ attribute, which is applied by the

394 @nose.tools.istest decorator.

395 """

396 # We explicitly check for "is True" here to not mistakenly treat

397 # classes with a custom __getattr__ returning something truthy (like a

398 # function) as test classes.

399 return safe_getattr(obj, "__test__", False) is True

400

401 def classnamefilter(self, name: str) -> bool:

402 return self._matches_prefix_or_glob_option("python_classes", name)

403

404 def istestfunction(self, obj: object, name: str) -> bool:

405 if self.funcnamefilter(name) or self.isnosetest(obj):

406 if isinstance(obj, staticmethod):

407 # staticmethods need to be unwrapped.

408 obj = safe_getattr(obj, "__func__", False)

409 return callable(obj) and fixtures.getfixturemarker(obj) is None

410 else:

411 return False

412

413 def istestclass(self, obj: object, name: str) -> bool:

414 return self.classnamefilter(name) or self.isnosetest(obj)

415

416 def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool:

417 """Check if the given name matches the prefix or glob-pattern defined

418 in ini configuration."""

419 for option in self.config.getini(option_name):

420 if name.startswith(option):

421 return True

422 # Check that name looks like a glob-string before calling fnmatch

423 # because this is called for every name in each collected module,

424 # and fnmatch is somewhat expensive to call.

425 elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch(

426 name, option

427 ):

428 return True

429 return False

430

431 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:

432 if not getattr(self.obj, "__test__", True):

433 return []

434

435 # Avoid random getattrs and peek in the __dict__ instead.

436 dicts = [getattr(self.obj, "__dict__", {})]

437 if isinstance(self.obj, type):

438 for basecls in self.obj.__mro__:

439 dicts.append(basecls.__dict__)

440

441 # In each class, nodes should be definition ordered.

442 # __dict__ is definition ordered.

443 seen: Set[str] = set()

444 dict_values: List[List[Union[nodes.Item, nodes.Collector]]] = []

445 ihook = self.ihook

446 for dic in dicts:

447 values: List[Union[nodes.Item, nodes.Collector]] = []

448 # Note: seems like the dict can change during iteration -

449 # be careful not to remove the list() without consideration.

450 for name, obj in list(dic.items()):

451 if name in IGNORED_ATTRIBUTES:

452 continue

453 if name in seen:

454 continue

455 seen.add(name)

456 res = ihook.pytest_pycollect_makeitem(

457 collector=self, name=name, obj=obj

458 )

459 if res is None:

460 continue

461 elif isinstance(res, list):

462 values.extend(res)

463 else:

464 values.append(res)

465 dict_values.append(values)

466

467 # Between classes in the class hierarchy, reverse-MRO order -- nodes

468 # inherited from base classes should come before subclasses.

469 result = []

470 for values in reversed(dict_values):

471 result.extend(values)

472 return result

473

474 def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]:

475 modulecol = self.getparent(Module)

476 assert modulecol is not None

477 module = modulecol.obj

478 clscol = self.getparent(Class)

479 cls = clscol and clscol.obj or None

480

481 definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj)

482 fixtureinfo = definition._fixtureinfo

483

484 # pytest_generate_tests impls call metafunc.parametrize() which fills

485 # metafunc._calls, the outcome of the hook.

486 metafunc = Metafunc(

487 definition=definition,

488 fixtureinfo=fixtureinfo,

489 config=self.config,

490 cls=cls,

491 module=module,

492 _ispytest=True,

493 )

494 methods = []

495 if hasattr(module, "pytest_generate_tests"):

496 methods.append(module.pytest_generate_tests)

497 if cls is not None and hasattr(cls, "pytest_generate_tests"):

498 methods.append(cls().pytest_generate_tests)

499 self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))

500

501 if not metafunc._calls:

502 yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo)

503 else:

504 # Add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs.

505 fm = self.session._fixturemanager

506 fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm)

507

508 # Add_funcarg_pseudo_fixture_def may have shadowed some fixtures

509 # with direct parametrization, so make sure we update what the

510 # function really needs.

511 fixtureinfo.prune_dependency_tree()

512

513 for callspec in metafunc._calls:

514 subname = f"{name}[{callspec.id}]"

515 yield Function.from_parent(

516 self,

517 name=subname,

518 callspec=callspec,

519 fixtureinfo=fixtureinfo,

520 keywords={callspec.id: True},

521 originalname=name,

522 )

523

524

525 class Module(nodes.File, PyCollector):

526 """Collector for test classes and functions."""

527

528 def _getobj(self):

529 return self._importtestmodule()

530

531 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:

532 self._inject_setup_module_fixture()

533 self._inject_setup_function_fixture()

534 self.session._fixturemanager.parsefactories(self)

535 return super().collect()

536

537 def _inject_setup_module_fixture(self) -> None:

538 """Inject a hidden autouse, module scoped fixture into the collected module object

539 that invokes setUpModule/tearDownModule if either or both are available.

540

541 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with

542 other fixtures (#517).

543 """

544 has_nose = self.config.pluginmanager.has_plugin("nose")

545 setup_module = _get_first_non_fixture_func(

546 self.obj, ("setUpModule", "setup_module")

547 )

548 if setup_module is None and has_nose:

549 # The name "setup" is too common - only treat as fixture if callable.

550 setup_module = _get_first_non_fixture_func(self.obj, ("setup",))

551 if not callable(setup_module):

552 setup_module = None

553 teardown_module = _get_first_non_fixture_func(

554 self.obj, ("tearDownModule", "teardown_module")

555 )

556 if teardown_module is None and has_nose:

557 teardown_module = _get_first_non_fixture_func(self.obj, ("teardown",))

558 # Same as "setup" above - only treat as fixture if callable.

559 if not callable(teardown_module):

560 teardown_module = None

561

562 if setup_module is None and teardown_module is None:

563 return

564

565 @fixtures.fixture(

566 autouse=True,

567 scope="module",

568 # Use a unique name to speed up lookup.

569 name=f"_xunit_setup_module_fixture_{self.obj.__name__}",

570 )

571 def xunit_setup_module_fixture(request) -> Generator[None, None, None]:

572 if setup_module is not None:

573 _call_with_optional_argument(setup_module, request.module)

574 yield

575 if teardown_module is not None:

576 _call_with_optional_argument(teardown_module, request.module)

577

578 self.obj.__pytest_setup_module = xunit_setup_module_fixture

579

580 def _inject_setup_function_fixture(self) -> None:

581 """Inject a hidden autouse, function scoped fixture into the collected module object

582 that invokes setup_function/teardown_function if either or both are available.

583

584 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with

585 other fixtures (#517).

586 """

587 setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",))

588 teardown_function = _get_first_non_fixture_func(

589 self.obj, ("teardown_function",)

590 )

591 if setup_function is None and teardown_function is None:

592 return

593

594 @fixtures.fixture(

595 autouse=True,

596 scope="function",

597 # Use a unique name to speed up lookup.

598 name=f"_xunit_setup_function_fixture_{self.obj.__name__}",

599 )

600 def xunit_setup_function_fixture(request) -> Generator[None, None, None]:

601 if request.instance is not None:

602 # in this case we are bound to an instance, so we need to let

603 # setup_method handle this

604 yield

605 return

606 if setup_function is not None:

607 _call_with_optional_argument(setup_function, request.function)

608 yield

609 if teardown_function is not None:

610 _call_with_optional_argument(teardown_function, request.function)

611

612 self.obj.__pytest_setup_function = xunit_setup_function_fixture

613

614 def _importtestmodule(self):

615 # We assume we are only called once per module.

616 importmode = self.config.getoption("--import-mode")

617 try:

618 mod = import_path(self.path, mode=importmode, root=self.config.rootpath)

619 except SyntaxError as e:

620 raise self.CollectError(

621 ExceptionInfo.from_current().getrepr(style="short")

622 ) from e

623 except ImportPathMismatchError as e:

624 raise self.CollectError(

625 "import file mismatch:\n"

626 "imported module %r has this __file__ attribute:\n"

627 " %s\n"

628 "which is not the same as the test file we want to collect:\n"

629 " %s\n"

630 "HINT: remove __pycache__ / .pyc files and/or use a "

631 "unique basename for your test file modules" % e.args

632 ) from e

633 except ImportError as e:

634 exc_info = ExceptionInfo.from_current()

635 if self.config.getoption("verbose") < 2:

636 exc_info.traceback = exc_info.traceback.filter(filter_traceback)

637 exc_repr = (

638 exc_info.getrepr(style="short")

639 if exc_info.traceback

640 else exc_info.exconly()

641 )

642 formatted_tb = str(exc_repr)

643 raise self.CollectError(

644 "ImportError while importing test module '{path}'.\n"

645 "Hint: make sure your test modules/packages have valid Python names.\n"

646 "Traceback:\n"

647 "{traceback}".format(path=self.path, traceback=formatted_tb)

648 ) from e

649 except skip.Exception as e:

650 if e.allow_module_level:

651 raise

652 raise self.CollectError(

653 "Using pytest.skip outside of a test will skip the entire module. "

654 "If that's your intention, pass `allow_module_level=True`. "

655 "If you want to skip a specific test or an entire class, "

656 "use the @pytest.mark.skip or @pytest.mark.skipif decorators."

657 ) from e

658 self.config.pluginmanager.consider_module(mod)

659 return mod

660

661

662 class Package(Module):

663 def __init__(

664 self,

665 fspath: Optional[LEGACY_PATH],

666 parent: nodes.Collector,

667 # NOTE: following args are unused:

668 config=None,

669 session=None,

670 nodeid=None,

671 path=Optional[Path],

672 ) -> None:

673 # NOTE: Could be just the following, but kept as-is for compat.

674 # nodes.FSCollector.__init__(self, fspath, parent=parent)

675 session = parent.session

676 nodes.FSCollector.__init__(

677 self,

678 fspath=fspath,

679 path=path,

680 parent=parent,

681 config=config,

682 session=session,

683 nodeid=nodeid,

684 )

685 self.name = self.path.parent.name

686

687 def setup(self) -> None:

688 # Not using fixtures to call setup_module here because autouse fixtures

689 # from packages are not called automatically (#4085).

690 setup_module = _get_first_non_fixture_func(

691 self.obj, ("setUpModule", "setup_module")

692 )

693 if setup_module is not None:

694 _call_with_optional_argument(setup_module, self.obj)

695

696 teardown_module = _get_first_non_fixture_func(

697 self.obj, ("tearDownModule", "teardown_module")

698 )

699 if teardown_module is not None:

700 func = partial(_call_with_optional_argument, teardown_module, self.obj)

701 self.addfinalizer(func)

702

703 def gethookproxy(self, fspath: "os.PathLike[str]"):

704 warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)

705 return self.session.gethookproxy(fspath)

706

707 def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool:

708 warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)

709 return self.session.isinitpath(path)

710

711 def _recurse(self, direntry: "os.DirEntry[str]") -> bool:

712 if direntry.name == "__pycache__":

713 return False

714 fspath = Path(direntry.path)

715 ihook = self.session.gethookproxy(fspath.parent)

716 if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config):

717 return False

718 norecursepatterns = self.config.getini("norecursedirs")

719 if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns):

720 return False

721 return True

722

723 def _collectfile(

724 self, fspath: Path, handle_dupes: bool = True

725 ) -> Sequence[nodes.Collector]:

726 assert (

727 fspath.is_file()

728 ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(

729 fspath, fspath.is_dir(), fspath.exists(), fspath.is_symlink()

730 )

731 ihook = self.session.gethookproxy(fspath)

732 if not self.session.isinitpath(fspath):

733 if ihook.pytest_ignore_collect(collection_path=fspath, config=self.config):

734 return ()

735

736 if handle_dupes:

737 keepduplicates = self.config.getoption("keepduplicates")

738 if not keepduplicates:

739 duplicate_paths = self.config.pluginmanager._duplicatepaths

740 if fspath in duplicate_paths:

741 return ()

742 else:

743 duplicate_paths.add(fspath)

744

745 return ihook.pytest_collect_file(file_path=fspath, parent=self) # type: ignore[no-any-return]

746

747 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:

748 this_path = self.path.parent

749 init_module = this_path / "__init__.py"

750 if init_module.is_file() and path_matches_patterns(

751 init_module, self.config.getini("python_files")

752 ):

753 yield Module.from_parent(self, path=init_module)

754 pkg_prefixes: Set[Path] = set()

755 for direntry in visit(str(this_path), recurse=self._recurse):

756 path = Path(direntry.path)

757

758 # We will visit our own __init__.py file, in which case we skip it.

759 if direntry.is_file():

760 if direntry.name == "__init__.py" and path.parent == this_path:

761 continue

762

763 parts_ = parts(direntry.path)

764 if any(

765 str(pkg_prefix) in parts_ and pkg_prefix / "__init__.py" != path

766 for pkg_prefix in pkg_prefixes

767 ):

768 continue

769

770 if direntry.is_file():

771 yield from self._collectfile(path)

772 elif not direntry.is_dir():

773 # Broken symlink or invalid/missing file.

774 continue

775 elif path.joinpath("__init__.py").is_file():

776 pkg_prefixes.add(path)

777

778

779 def _call_with_optional_argument(func, arg) -> None:

780 """Call the given function with the given argument if func accepts one argument, otherwise

781 calls func without arguments."""

782 arg_count = func.__code__.co_argcount

783 if inspect.ismethod(func):

784 arg_count -= 1

785 if arg_count:

786 func(arg)

787 else:

788 func()

789

790

791 def _get_first_non_fixture_func(obj: object, names: Iterable[str]) -> Optional[object]:

792 """Return the attribute from the given object to be used as a setup/teardown

793 xunit-style function, but only if not marked as a fixture to avoid calling it twice."""

794 for name in names:

795 meth: Optional[object] = getattr(obj, name, None)

796 if meth is not None and fixtures.getfixturemarker(meth) is None:

797 return meth

798 return None

799

800

801 class Class(PyCollector):

802 """Collector for test methods."""

803

804 @classmethod

805 def from_parent(cls, parent, *, name, obj=None, **kw):

806 """The public constructor."""

807 return super().from_parent(name=name, parent=parent, **kw)

808

809 def newinstance(self):

810 return self.obj()

811

812 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:

813 if not safe_getattr(self.obj, "__test__", True):

814 return []

815 if hasinit(self.obj):

816 assert self.parent is not None

817 self.warn(

818 PytestCollectionWarning(

819 "cannot collect test class %r because it has a "

820 "__init__ constructor (from: %s)"

821 % (self.obj.__name__, self.parent.nodeid)

822 )

823 )

824 return []

825 elif hasnew(self.obj):

826 assert self.parent is not None

827 self.warn(

828 PytestCollectionWarning(

829 "cannot collect test class %r because it has a "

830 "__new__ constructor (from: %s)"

831 % (self.obj.__name__, self.parent.nodeid)

832 )

833 )

834 return []

835

836 self._inject_setup_class_fixture()

837 self._inject_setup_method_fixture()

838

839 self.session._fixturemanager.parsefactories(self.newinstance(), self.nodeid)

840

841 return super().collect()

842

843 def _inject_setup_class_fixture(self) -> None:

844 """Inject a hidden autouse, class scoped fixture into the collected class object

845 that invokes setup_class/teardown_class if either or both are available.

846

847 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with

848 other fixtures (#517).

849 """

850 setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",))

851 teardown_class = _get_first_non_fixture_func(self.obj, ("teardown_class",))

852 if setup_class is None and teardown_class is None:

853 return

854

855 @fixtures.fixture(

856 autouse=True,

857 scope="class",

858 # Use a unique name to speed up lookup.

859 name=f"_xunit_setup_class_fixture_{self.obj.__qualname__}",

860 )

861 def xunit_setup_class_fixture(cls) -> Generator[None, None, None]:

862 if setup_class is not None:

863 func = getimfunc(setup_class)

864 _call_with_optional_argument(func, self.obj)

865 yield

866 if teardown_class is not None:

867 func = getimfunc(teardown_class)

868 _call_with_optional_argument(func, self.obj)

869

870 self.obj.__pytest_setup_class = xunit_setup_class_fixture

871

872 def _inject_setup_method_fixture(self) -> None:

873 """Inject a hidden autouse, function scoped fixture into the collected class object

874 that invokes setup_method/teardown_method if either or both are available.

875

876 Using a fixture to invoke these methods ensures we play nicely and unsurprisingly with

877 other fixtures (#517).

878 """

879 has_nose = self.config.pluginmanager.has_plugin("nose")

880 setup_name = "setup_method"

881 setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))

882 emit_nose_setup_warning = False

883 if setup_method is None and has_nose:

884 setup_name = "setup"

885 emit_nose_setup_warning = True

886 setup_method = _get_first_non_fixture_func(self.obj, (setup_name,))

887 teardown_name = "teardown_method"

888 teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))

889 emit_nose_teardown_warning = False

890 if teardown_method is None and has_nose:

891 teardown_name = "teardown"

892 emit_nose_teardown_warning = True

893 teardown_method = _get_first_non_fixture_func(self.obj, (teardown_name,))

894 if setup_method is None and teardown_method is None:

895 return

896

897 @fixtures.fixture(

898 autouse=True,

899 scope="function",

900 # Use a unique name to speed up lookup.

901 name=f"_xunit_setup_method_fixture_{self.obj.__qualname__}",

902 )

903 def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]:

904 method = request.function

905 if setup_method is not None:

906 func = getattr(self, setup_name)

907 _call_with_optional_argument(func, method)

908 if emit_nose_setup_warning:

909 warnings.warn(

910 NOSE_SUPPORT_METHOD.format(

911 nodeid=request.node.nodeid, method="setup"

912 ),

913 stacklevel=2,

914 )

915 yield

916 if teardown_method is not None:

917 func = getattr(self, teardown_name)

918 _call_with_optional_argument(func, method)

919 if emit_nose_teardown_warning:

920 warnings.warn(

921 NOSE_SUPPORT_METHOD.format(

922 nodeid=request.node.nodeid, method="teardown"

923 ),

924 stacklevel=2,

925 )

926

927 self.obj.__pytest_setup_method = xunit_setup_method_fixture

928

929

930 class InstanceDummy:

931 """Instance used to be a node type between Class and Function. It has been

932 removed in pytest 7.0. Some plugins exist which reference `pytest.Instance`

933 only to ignore it; this dummy class keeps them working. This will be removed

934 in pytest 8."""

935

936

937 def __getattr__(name: str) -> object:

938 if name == "Instance":

939 warnings.warn(INSTANCE_COLLECTOR, 2)

940 return InstanceDummy

941 raise AttributeError(f"module {__name__} has no attribute {name}")

942

943

944 def hasinit(obj: object) -> bool:

945 init: object = getattr(obj, "__init__", None)

946 if init:

947 return init != object.__init__

948 return False

949

950

951 def hasnew(obj: object) -> bool:

952 new: object = getattr(obj, "__new__", None)

953 if new:

954 return new != object.__new__

955 return False

956

957

958@final

959@attr.s(frozen=True, auto_attribs=True, slots=True)

960 class IdMaker:

961 """Make IDs for a parametrization."""

962

963 # The argnames of the parametrization.

964 argnames: Sequence[str]

965 # The ParameterSets of the parametrization.

966 parametersets: Sequence[ParameterSet]

967 # Optionally, a user-provided callable to make IDs for parameters in a

968 # ParameterSet.

969 idfn: Optional[Callable[[Any], Optional[object]]]

970 # Optionally, explicit IDs for ParameterSets by index.

971 ids: Optional[Sequence[Optional[object]]]

972 # Optionally, the pytest config.

973 # Used for controlling ASCII escaping, and for calling the

974 # :hook:`pytest_make_parametrize_id` hook.

975 config: Optional[Config]

976 # Optionally, the ID of the node being parametrized.

977 # Used only for clearer error messages.

978 nodeid: Optional[str]

979 # Optionally, the ID of the function being parametrized.

980 # Used only for clearer error messages.

981 func_name: Optional[str]

982

983 def make_unique_parameterset_ids(self) -> List[str]:

984 """Make a unique identifier for each ParameterSet, that may be used to

985 identify the parametrization in a node ID.

986

987 Format is <prm_1_token>-...-<prm_n_token>[counter], where prm_x_token is

988 - user-provided id, if given

989 - else an id derived from the value, applicable for certain types

990 - else <argname><parameterset index>

991 The counter suffix is appended only in case a string wouldn't be unique

992 otherwise.

993 """

994 resolved_ids = list(self._resolve_ids())

995 # All IDs must be unique!

996 if len(resolved_ids) != len(set(resolved_ids)):

997 # Record the number of occurrences of each ID.

998 id_counts = Counter(resolved_ids)

999 # Map the ID to its next suffix.

1000 id_suffixes: Dict[str, int] = defaultdict(int)

1001 # Suffix non-unique IDs to make them unique.

1002 for index, id in enumerate(resolved_ids):

1003 if id_counts[id] > 1:

1004 resolved_ids[index] = f"{id}{id_suffixes[id]}"

1005 id_suffixes[id] += 1

1006 return resolved_ids

1007

1008 def _resolve_ids(self) -> Iterable[str]:

1009 """Resolve IDs for all ParameterSets (may contain duplicates)."""

1010 for idx, parameterset in enumerate(self.parametersets):

1011 if parameterset.id is not None:

1012 # ID provided directly - pytest.param(..., id="...")

1013 yield parameterset.id

1014 elif self.ids and idx < len(self.ids) and self.ids[idx] is not None:

1015 # ID provided in the IDs list - parametrize(..., ids=[...]).

1016 yield self._idval_from_value_required(self.ids[idx], idx)

1017 else:

1018 # ID not provided - generate it.

1019 yield "-".join(

1020 self._idval(val, argname, idx)

1021 for val, argname in zip(parameterset.values, self.argnames)

1022 )

1023

1024 def _idval(self, val: object, argname: str, idx: int) -> str:

1025 """Make an ID for a parameter in a ParameterSet."""

1026 idval = self._idval_from_function(val, argname, idx)

1027 if idval is not None:

1028 return idval

1029 idval = self._idval_from_hook(val, argname)

1030 if idval is not None:

1031 return idval

1032 idval = self._idval_from_value(val)

1033 if idval is not None:

1034 return idval

1035 return self._idval_from_argname(argname, idx)

1036

1037 def _idval_from_function(

1038 self, val: object, argname: str, idx: int

1039 ) -> Optional[str]:

1040 """Try to make an ID for a parameter in a ParameterSet using the

1041 user-provided id callable, if given."""

1042 if self.idfn is None:

1043 return None

1044 try:

1045 id = self.idfn(val)

1046 except Exception as e:

1047 prefix = f"{self.nodeid}: " if self.nodeid is not None else ""

1048 msg = "error raised while trying to determine id of parameter '{}' at position {}"

1049 msg = prefix + msg.format(argname, idx)

1050 raise ValueError(msg) from e

1051 if id is None:

1052 return None

1053 return self._idval_from_value(id)

1054

1055 def _idval_from_hook(self, val: object, argname: str) -> Optional[str]:

1056 """Try to make an ID for a parameter in a ParameterSet by calling the

1057 :hook:`pytest_make_parametrize_id` hook."""

1058 if self.config:

1059 id: Optional[str] = self.config.hook.pytest_make_parametrize_id(

1060 config=self.config, val=val, argname=argname

1061 )

1062 return id

1063 return None

1064

1065 def _idval_from_value(self, val: object) -> Optional[str]:

1066 """Try to make an ID for a parameter in a ParameterSet from its value,

1067 if the value type is supported."""

1068 if isinstance(val, STRING_TYPES):

1069 return _ascii_escaped_by_config(val, self.config)

1070 elif val is None or isinstance(val, (float, int, bool, complex)):

1071 return str(val)

1072 elif isinstance(val, Pattern):

1073 return ascii_escaped(val.pattern)

1074 elif val is NOTSET:

1075 # Fallback to default. Note that NOTSET is an enum.Enum.

1076 pass

1077 elif isinstance(val, enum.Enum):

1078 return str(val)

1079 elif isinstance(getattr(val, "__name__", None), str):

1080 # Name of a class, function, module, etc.

1081 name: str = getattr(val, "__name__")

1082 return name

1083 return None

1084

1085 def _idval_from_value_required(self, val: object, idx: int) -> str:

1086 """Like _idval_from_value(), but fails if the type is not supported."""

1087 id = self._idval_from_value(val)

1088 if id is not None:

1089 return id

1090

1091 # Fail.

1092 if self.func_name is not None:

1093 prefix = f"In {self.func_name}: "

1094 elif self.nodeid is not None:

1095 prefix = f"In {self.nodeid}: "

1096 else:

1097 prefix = ""

1098 msg = (

1099 f"{prefix}ids contains unsupported value {saferepr(val)} (type: {type(val)!r}) at index {idx}. "

1100 "Supported types are: str, bytes, int, float, complex, bool, enum, regex or anything with a __name__."

1101 )

1102 fail(msg, pytrace=False)

1103

1104 @staticmethod

1105 def _idval_from_argname(argname: str, idx: int) -> str:

1106 """Make an ID for a parameter in a ParameterSet from the argument name

1107 and the index of the ParameterSet."""

1108 return str(argname) + str(idx)

1109

1110

1111@final

1112@attr.s(frozen=True, slots=True, auto_attribs=True)

1113 class CallSpec2:

1114 """A planned parameterized invocation of a test function.

1115

1116 Calculated during collection for a given test function's Metafunc.

1117 Once collection is over, each callspec is turned into a single Item

1118 and stored in item.callspec.

1119 """

1120

1121 # arg name -> arg value which will be passed to the parametrized test

1122 # function (direct parameterization).

1123 funcargs: Dict[str, object] = attr.Factory(dict)

1124 # arg name -> arg value which will be passed to a fixture of the same name

1125 # (indirect parametrization).

1126 params: Dict[str, object] = attr.Factory(dict)

1127 # arg name -> arg index.

1128 indices: Dict[str, int] = attr.Factory(dict)

1129 # Used for sorting parametrized resources.

1130 _arg2scope: Dict[str, Scope] = attr.Factory(dict)

1131 # Parts which will be added to the item's name in `[..]` separated by "-".

1132 _idlist: List[str] = attr.Factory(list)

1133 # Marks which will be applied to the item.

1134 marks: List[Mark] = attr.Factory(list)

1135

1136 def setmulti(

1137 self,

1138 *,

1139 valtypes: Mapping[str, "Literal['params', 'funcargs']"],

1140 argnames: Iterable[str],

1141 valset: Iterable[object],

1142 id: str,

1143 marks: Iterable[Union[Mark, MarkDecorator]],

1144 scope: Scope,

1145 param_index: int,

1146 ) -> "CallSpec2":

1147 funcargs = self.funcargs.copy()

1148 params = self.params.copy()

1149 indices = self.indices.copy()

1150 arg2scope = self._arg2scope.copy()

1151 for arg, val in zip(argnames, valset):

1152 if arg in params or arg in funcargs:

1153 raise ValueError(f"duplicate {arg!r}")

1154 valtype_for_arg = valtypes[arg]

1155 if valtype_for_arg == "params":

1156 params[arg] = val

1157 elif valtype_for_arg == "funcargs":

1158 funcargs[arg] = val

1159 else:

1160 assert_never(valtype_for_arg)

1161 indices[arg] = param_index

1162 arg2scope[arg] = scope

1163 return CallSpec2(

1164 funcargs=funcargs,

1165 params=params,

1166 arg2scope=arg2scope,

1167 indices=indices,

1168 idlist=[*self._idlist, id],

1169 marks=[*self.marks, *normalize_mark_list(marks)],

1170 )

1171

1172 def getparam(self, name: str) -> object:

1173 try:

1174 return self.params[name]

1175 except KeyError as e:

1176 raise ValueError(name) from e

1177

1178 @property

1179 def id(self) -> str:

1180 return "-".join(self._idlist)

1181

1182

1183@final

1184 class Metafunc:

1185 """Objects passed to the :hook:`pytest_generate_tests` hook.

1186

1187 They help to inspect a test function and to generate tests according to

1188 test configuration or values specified in the class or module where a

1189 test function is defined.

1190 """

1191

1192 def __init__(

1193 self,

1194 definition: "FunctionDefinition",

1195 fixtureinfo: fixtures.FuncFixtureInfo,

1196 config: Config,

1197 cls=None,

1198 module=None,

1199 *,

1200 _ispytest: bool = False,

1201 ) -> None:

1202 check_ispytest(_ispytest)

1203

1204 #: Access to the underlying :class:`_pytest.python.FunctionDefinition`.

1205 self.definition = definition

1206

1207 #: Access to the :class:`pytest.Config` object for the test session.

1208 self.config = config

1209

1210 #: The module object where the test function is defined in.

1211 self.module = module

1212

1213 #: Underlying Python test function.

1214 self.function = definition.obj

1215

1216 #: Set of fixture names required by the test function.

1217 self.fixturenames = fixtureinfo.names_closure

1218

1219 #: Class object where the test function is defined in or ``None``.

1220 self.cls = cls

1221

1222 self._arg2fixturedefs = fixtureinfo.name2fixturedefs

1223

1224 # Result of parametrize().

1225 self._calls: List[CallSpec2] = []

1226

1227 def parametrize(

1228 self,

1229 argnames: Union[str, Sequence[str]],

1230 argvalues: Iterable[Union[ParameterSet, Sequence[object], object]],

1231 indirect: Union[bool, Sequence[str]] = False,

1232 ids: Optional[

1233 Union[Iterable[Optional[object]], Callable[[Any], Optional[object]]]

1234 ] = None,

1235 scope: "Optional[_ScopeName]" = None,

1236 *,

1237 _param_mark: Optional[Mark] = None,

1238 ) -> None:

1239 """Add new invocations to the underlying test function using the list

1240 of argvalues for the given argnames. Parametrization is performed

1241 during the collection phase. If you need to setup expensive resources

1242 see about setting indirect to do it rather than at test setup time.

1243

1244 Can be called multiple times, in which case each call parametrizes all

1245 previous parametrizations, e.g.

1246

1247 ::

1248

1249 unparametrized: t

1250 parametrize ["x", "y"]: t[x], t[y]

1251 parametrize [1, 2]: t[x-1], t[x-2], t[y-1], t[y-2]

1252

1253 :param argnames:

1254 A comma-separated string denoting one or more argument names, or

1255 a list/tuple of argument strings.

1256

1257 :param argvalues:

1258 The list of argvalues determines how often a test is invoked with

1259 different argument values.

1260

1261 If only one argname was specified argvalues is a list of values.

1262 If N argnames were specified, argvalues must be a list of

1263 N-tuples, where each tuple-element specifies a value for its

1264 respective argname.

1265

1266 :param indirect:

1267 A list of arguments' names (subset of argnames) or a boolean.

1268 If True the list contains all names from the argnames. Each

1269 argvalue corresponding to an argname in this list will

1270 be passed as request.param to its respective argname fixture

1271 function so that it can perform more expensive setups during the

1272 setup phase of a test rather than at collection time.

1273

1274 :param ids:

1275 Sequence of (or generator for) ids for ``argvalues``,

1276 or a callable to return part of the id for each argvalue.

1277

1278 With sequences (and generators like ``itertools.count()``) the

1279 returned ids should be of type ``string``, ``int``, ``float``,

1280 ``bool``, or ``None``.

1281 They are mapped to the corresponding index in ``argvalues``.

1282 ``None`` means to use the auto-generated id.

1283

1284 If it is a callable it will be called for each entry in

1285 ``argvalues``, and the return value is used as part of the

1286 auto-generated id for the whole set (where parts are joined with

1287 dashes ("-")).

1288 This is useful to provide more specific ids for certain items, e.g.

1289 dates. Returning ``None`` will use an auto-generated id.

1290

1291 If no ids are provided they will be generated automatically from

1292 the argvalues.

1293

1294 :param scope:

1295 If specified it denotes the scope of the parameters.

1296 The scope is used for grouping tests by parameter instances.

1297 It will also override any fixture-function defined scope, allowing

1298 to set a dynamic scope using test context or configuration.

1299 """

1300 argnames, parametersets = ParameterSet._for_parametrize(

1301 argnames,

1302 argvalues,

1303 self.function,

1304 self.config,

1305 nodeid=self.definition.nodeid,

1306 )

1307 del argvalues

1308

1309 if "request" in argnames:

1310 fail(

1311 "'request' is a reserved name and cannot be used in @pytest.mark.parametrize",

1312 pytrace=False,

1313 )

1314

1315 if scope is not None:

1316 scope_ = Scope.from_user(

1317 scope, descr=f"parametrize() call in {self.function.__name__}"

1318 )

1319 else:

1320 scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)

1321

1322 self._validate_if_using_arg_names(argnames, indirect)

1323

1324 arg_values_types = self._resolve_arg_value_types(argnames, indirect)

1325

1326 # Use any already (possibly) generated ids with parametrize Marks.

1327 if _param_mark and _param_mark._param_ids_from:

1328 generated_ids = _param_mark._param_ids_from._param_ids_generated

1329 if generated_ids is not None:

1330 ids = generated_ids

1331

1332 ids = self._resolve_parameter_set_ids(

1333 argnames, ids, parametersets, nodeid=self.definition.nodeid

1334 )

1335

1336 # Store used (possibly generated) ids with parametrize Marks.

1337 if _param_mark and _param_mark._param_ids_from and generated_ids is None:

1338 object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids)

1339

1340 # Create the new calls: if we are parametrize() multiple times (by applying the decorator

1341 # more than once) then we accumulate those calls generating the cartesian product

1342 # of all calls.

1343 newcalls = []

1344 for callspec in self._calls or [CallSpec2()]:

1345 for param_index, (param_id, param_set) in enumerate(

1346 zip(ids, parametersets)

1347 ):

1348 newcallspec = callspec.setmulti(

1349 valtypes=arg_values_types,

1350 argnames=argnames,

1351 valset=param_set.values,

1352 id=param_id,

1353 marks=param_set.marks,

1354 scope=scope_,

1355 param_index=param_index,

1356 )

1357 newcalls.append(newcallspec)

1358 self._calls = newcalls

1359

1360 def _resolve_parameter_set_ids(

1361 self,

1362 argnames: Sequence[str],

1363 ids: Optional[

1364 Union[Iterable[Optional[object]], Callable[[Any], Optional[object]]]

1365 ],

1366 parametersets: Sequence[ParameterSet],

1367 nodeid: str,

1368 ) -> List[str]:

1369 """Resolve the actual ids for the given parameter sets.

1370

1371 :param argnames:

1372 Argument names passed to ``parametrize()``.

1373 :param ids:

1374 The `ids` parameter of the ``parametrize()`` call (see docs).

1375 :param parametersets:

1376 The parameter sets, each containing a set of values corresponding

1377 to ``argnames``.

1378 :param nodeid str:

1379 The nodeid of the definition item that generated this

1380 parametrization.

1381 :returns:

1382 List with ids for each parameter set given.

1383 """

1384 if ids is None:

1385 idfn = None

1386 ids_ = None

1387 elif callable(ids):

1388 idfn = ids

1389 ids_ = None

1390 else:

1391 idfn = None

1392 ids_ = self._validate_ids(ids, parametersets, self.function.__name__)

1393 id_maker = IdMaker(

1394 argnames,

1395 parametersets,

1396 idfn,

1397 ids_,

1398 self.config,

1399 nodeid=nodeid,

1400 func_name=self.function.__name__,

1401 )

1402 return id_maker.make_unique_parameterset_ids()

1403

1404 def _validate_ids(

1405 self,

1406 ids: Iterable[Optional[object]],

1407 parametersets: Sequence[ParameterSet],

1408 func_name: str,

1409 ) -> List[Optional[object]]:

1410 try:

1411 num_ids = len(ids) # type: ignore[arg-type]

1412 except TypeError:

1413 try:

1414 iter(ids)

1415 except TypeError as e:

1416 raise TypeError("ids must be a callable or an iterable") from e

1417 num_ids = len(parametersets)

1418

1419 # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849

1420 if num_ids != len(parametersets) and num_ids != 0:

1421 msg = "In {}: {} parameter sets specified, with different number of ids: {}"

1422 fail(msg.format(func_name, len(parametersets), num_ids), pytrace=False)

1423

1424 return list(itertools.islice(ids, num_ids))

1425

1426 def _resolve_arg_value_types(

1427 self,

1428 argnames: Sequence[str],

1429 indirect: Union[bool, Sequence[str]],

1430 ) -> Dict[str, "Literal['params', 'funcargs']"]:

1431 """Resolve if each parametrized argument must be considered a

1432 parameter to a fixture or a "funcarg" to the function, based on the

1433 ``indirect`` parameter of the parametrized() call.

1434

1435 :param List[str] argnames: List of argument names passed to ``parametrize()``.

1436 :param indirect: Same as the ``indirect`` parameter of ``parametrize()``.

1437 :rtype: Dict[str, str]

1438 A dict mapping each arg name to either:

1439 * "params" if the argname should be the parameter of a fixture of the same name.

1440 * "funcargs" if the argname should be a parameter to the parametrized test function.

1441 """

1442 if isinstance(indirect, bool):

1443 valtypes: Dict[str, Literal["params", "funcargs"]] = dict.fromkeys(

1444 argnames, "params" if indirect else "funcargs"

1445 )

1446 elif isinstance(indirect, Sequence):

1447 valtypes = dict.fromkeys(argnames, "funcargs")

1448 for arg in indirect:

1449 if arg not in argnames:

1450 fail(

1451 "In {}: indirect fixture '{}' doesn't exist".format(

1452 self.function.__name__, arg

1453 ),

1454 pytrace=False,

1455 )

1456 valtypes[arg] = "params"

1457 else:

1458 fail(

1459 "In {func}: expected Sequence or boolean for indirect, got {type}".format(

1460 type=type(indirect).__name__, func=self.function.__name__

1461 ),

1462 pytrace=False,

1463 )

1464 return valtypes

1465

1466 def _validate_if_using_arg_names(

1467 self,

1468 argnames: Sequence[str],

1469 indirect: Union[bool, Sequence[str]],

1470 ) -> None:

1471 """Check if all argnames are being used, by default values, or directly/indirectly.

1472

1473 :param List[str] argnames: List of argument names passed to ``parametrize()``.

1474 :param indirect: Same as the ``indirect`` parameter of ``parametrize()``.

1475 :raises ValueError: If validation fails.

1476 """

1477 default_arg_names = set(get_default_arg_names(self.function))

1478 func_name = self.function.__name__

1479 for arg in argnames:

1480 if arg not in self.fixturenames:

1481 if arg in default_arg_names:

1482 fail(

1483 "In {}: function already takes an argument '{}' with a default value".format(

1484 func_name, arg

1485 ),

1486 pytrace=False,

1487 )

1488 else:

1489 if isinstance(indirect, Sequence):

1490 name = "fixture" if arg in indirect else "argument"

1491 else:

1492 name = "fixture" if indirect else "argument"

1493 fail(

1494 f"In {func_name}: function uses no {name} '{arg}'",

1495 pytrace=False,

1496 )

1497

1498

1499 def _find_parametrized_scope(

1500 argnames: Sequence[str],

1501 arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]],

1502 indirect: Union[bool, Sequence[str]],

1503) -> Scope:

1504 """Find the most appropriate scope for a parametrized call based on its arguments.

1505

1506 When there's at least one direct argument, always use "function" scope.

1507

1508 When a test function is parametrized and all its arguments are indirect

1509 (e.g. fixtures), return the most narrow scope based on the fixtures used.

1510

1511 Related to issue #1832, based on code posted by @Kingdread.

1512 """

1513 if isinstance(indirect, Sequence):

1514 all_arguments_are_fixtures = len(indirect) == len(argnames)

1515 else:

1516 all_arguments_are_fixtures = bool(indirect)

1517

1518 if all_arguments_are_fixtures:

1519 fixturedefs = arg2fixturedefs or {}

1520 used_scopes = [

1521 fixturedef[0]._scope

1522 for name, fixturedef in fixturedefs.items()

1523 if name in argnames

1524 ]

1525 # Takes the most narrow scope from used fixtures.

1526 return min(used_scopes, default=Scope.Function)

1527

1528 return Scope.Function

1529

1530

1531 def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) -> str:

1532 if config is None:

1533 escape_option = False

1534 else:

1535 escape_option = config.getini(

1536 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support"

1537 )

1538 # TODO: If escaping is turned off and the user passes bytes,

1539 # will return a bytes. For now we ignore this but the

1540 # code *probably* doesn't handle this case.

1541 return val if escape_option else ascii_escaped(val) # type: ignore

1542

1543

1544 def _pretty_fixture_path(func) -> str:

1545 cwd = Path.cwd()

1546 loc = Path(getlocation(func, str(cwd)))

1547 prefix = Path("...", "_pytest")

1548 try:

1549 return str(prefix / loc.relative_to(_PYTEST_DIR))

1550 except ValueError:

1551 return bestrelpath(cwd, loc)

1552

1553

1554 def show_fixtures_per_test(config):

1555 from _pytest.main import wrap_session

1556

1557 return wrap_session(config, _show_fixtures_per_test)

1558

1559

1560 def _show_fixtures_per_test(config: Config, session: Session) -> None:

1561 import _pytest.config

1562

1563 session.perform_collect()

1564 curdir = Path.cwd()

1565 tw = _pytest.config.create_terminal_writer(config)

1566 verbose = config.getvalue("verbose")

1567

1568 def get_best_relpath(func) -> str:

1569 loc = getlocation(func, str(curdir))

1570 return bestrelpath(curdir, Path(loc))

1571

1572 def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None:

1573 argname = fixture_def.argname

1574 if verbose <= 0 and argname.startswith("_"):

1575 return

1576 prettypath = _pretty_fixture_path(fixture_def.func)

1577 tw.write(f"{argname}", green=True)

1578 tw.write(f" -- {prettypath}", yellow=True)

1579 tw.write("\n")

1580 fixture_doc = inspect.getdoc(fixture_def.func)

1581 if fixture_doc:

1582 write_docstring(

1583 tw, fixture_doc.split("\n\n")[0] if verbose <= 0 else fixture_doc

1584 )

1585 else:

1586 tw.line(" no docstring available", red=True)

1587

1588 def write_item(item: nodes.Item) -> None:

1589 # Not all items have _fixtureinfo attribute.

1590 info: Optional[FuncFixtureInfo] = getattr(item, "_fixtureinfo", None)

1591 if info is None or not info.name2fixturedefs:

1592 # This test item does not use any fixtures.

1593 return

1594 tw.line()

1595 tw.sep("-", f"fixtures used by {item.name}")

1596 # TODO: Fix this type ignore.

1597 tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined]

1598 # dict key not used in loop but needed for sorting.

1599 for _, fixturedefs in sorted(info.name2fixturedefs.items()):

1600 assert fixturedefs is not None

1601 if not fixturedefs:

1602 continue

1603 # Last item is expected to be the one used by the test item.

1604 write_fixture(fixturedefs[-1])

1605

1606 for session_item in session.items:

1607 write_item(session_item)

1608

1609

1610 def showfixtures(config: Config) -> Union[int, ExitCode]:

1611 from _pytest.main import wrap_session

1612

1613 return wrap_session(config, _showfixtures_main)

1614

1615

1616 def _showfixtures_main(config: Config, session: Session) -> None:

1617 import _pytest.config

1618

1619 session.perform_collect()

1620 curdir = Path.cwd()

1621 tw = _pytest.config.create_terminal_writer(config)

1622 verbose = config.getvalue("verbose")

1623

1624 fm = session._fixturemanager

1625

1626 available = []

1627 seen: Set[Tuple[str, str]] = set()

1628

1629 for argname, fixturedefs in fm._arg2fixturedefs.items():

1630 assert fixturedefs is not None

1631 if not fixturedefs:

1632 continue

1633 for fixturedef in fixturedefs:

1634 loc = getlocation(fixturedef.func, str(curdir))

1635 if (fixturedef.argname, loc) in seen:

1636 continue

1637 seen.add((fixturedef.argname, loc))

1638 available.append(

1639 (

1640 len(fixturedef.baseid),

1641 fixturedef.func.__module__,

1642 _pretty_fixture_path(fixturedef.func),

1643 fixturedef.argname,

1644 fixturedef,

1645 )

1646 )

1647

1648 available.sort()

1649 currentmodule = None

1650 for baseid, module, prettypath, argname, fixturedef in available:

1651 if currentmodule != module:

1652 if not module.startswith("_pytest."):

1653 tw.line()

1654 tw.sep("-", f"fixtures defined from {module}")

1655 currentmodule = module

1656 if verbose <= 0 and argname.startswith("_"):

1657 continue

1658 tw.write(f"{argname}", green=True)

1659 if fixturedef.scope != "function":

1660 tw.write(" [%s scope]" % fixturedef.scope, cyan=True)

1661 tw.write(f" -- {prettypath}", yellow=True)

1662 tw.write("\n")

1663 doc = inspect.getdoc(fixturedef.func)

1664 if doc:

1665 write_docstring(tw, doc.split("\n\n")[0] if verbose <= 0 else doc)

1666 else:

1667 tw.line(" no docstring available", red=True)

1668 tw.line()

1669

1670

1671 def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None:

1672 for line in doc.split("\n"):

1673 tw.line(indent + line)

1674

1675

1676 class Function(PyobjMixin, nodes.Item):

1677 """An Item responsible for setting up and executing a Python test function.

1678

1679 :param name:

1680 The full function name, including any decorations like those

1681 added by parametrization (``my_func[my_param]``).

1682 :param parent:

1683 The parent Node.

1684 :param config:

1685 The pytest Config object.

1686 :param callspec:

1687 If given, this is function has been parametrized and the callspec contains

1688 meta information about the parametrization.

1689 :param callobj:

1690 If given, the object which will be called when the Function is invoked,

1691 otherwise the callobj will be obtained from ``parent`` using ``originalname``.

1692 :param keywords:

1693 Keywords bound to the function object for "-k" matching.

1694 :param session:

1695 The pytest Session object.

1696 :param fixtureinfo:

1697 Fixture information already resolved at this fixture node..

1698 :param originalname:

1699 The attribute name to use for accessing the underlying function object.

1700 Defaults to ``name``. Set this if name is different from the original name,

1701 for example when it contains decorations like those added by parametrization

1702 (``my_func[my_param]``).

1703 """

1704

1705 # Disable since functions handle it themselves.

1706 _ALLOW_MARKERS = False

1707

1708 def __init__(

1709 self,

1710 name: str,

1711 parent,

1712 config: Optional[Config] = None,

1713 callspec: Optional[CallSpec2] = None,

1714 callobj=NOTSET,

1715 keywords: Optional[Mapping[str, Any]] = None,

1716 session: Optional[Session] = None,

1717 fixtureinfo: Optional[FuncFixtureInfo] = None,

1718 originalname: Optional[str] = None,

1719 ) -> None:

1720 super().__init__(name, parent, config=config, session=session)

1721

1722 if callobj is not NOTSET:

1723 self.obj = callobj

1724

1725 #: Original function name, without any decorations (for example

1726 #: parametrization adds a ``"[...]"`` suffix to function names), used to access

1727 #: the underlying function object from ``parent`` (in case ``callobj`` is not given

1728 #: explicitly).

1729 #:

1730 #: .. versionadded:: 3.0

1731 self.originalname = originalname or name

1732

1733 # Note: when FunctionDefinition is introduced, we should change ``originalname``

1734 # to a readonly property that returns FunctionDefinition.name.

1735

1736 self.own_markers.extend(get_unpacked_marks(self.obj))

1737 if callspec:

1738 self.callspec = callspec

1739 self.own_markers.extend(callspec.marks)

1740

1741 # todo: this is a hell of a hack

1742 # https://github.com/pytest-dev/pytest/issues/4569

1743 # Note: the order of the updates is important here; indicates what

1744 # takes priority (ctor argument over function attributes over markers).

1745 # Take own_markers only; NodeKeywords handles parent traversal on its own.

1746 self.keywords.update((mark.name, mark) for mark in self.own_markers)

1747 self.keywords.update(self.obj.__dict__)

1748 if keywords:

1749 self.keywords.update(keywords)

1750

1751 if fixtureinfo is None:

1752 fixtureinfo = self.session._fixturemanager.getfixtureinfo(

1753 self, self.obj, self.cls, funcargs=True

1754 )

1755 self._fixtureinfo: FuncFixtureInfo = fixtureinfo

1756 self.fixturenames = fixtureinfo.names_closure

1757 self._initrequest()

1758

1759 @classmethod

1760 def from_parent(cls, parent, **kw): # todo: determine sound type limitations

1761 """The public constructor."""

1762 return super().from_parent(parent=parent, **kw)

1763

1764 def _initrequest(self) -> None:

1765 self.funcargs: Dict[str, object] = {}

1766 self._request = fixtures.FixtureRequest(self, _ispytest=True)

1767

1768 @property

1769 def function(self):

1770 """Underlying python 'function' object."""

1771 return getimfunc(self.obj)

1772

1773 def _getobj(self):

1774 assert self.parent is not None

1775 if isinstance(self.parent, Class):

1776 # Each Function gets a fresh class instance.

1777 parent_obj = self.parent.newinstance()

1778 else:

1779 parent_obj = self.parent.obj # type: ignore[attr-defined]

1780 return getattr(parent_obj, self.originalname)

1781

1782 @property

1783 def _pyfuncitem(self):

1784 """(compatonly) for code expecting pytest-2.2 style request objects."""

1785 return self

1786

1787 def runtest(self) -> None:

1788 """Execute the underlying test function."""

1789 self.ihook.pytest_pyfunc_call(pyfuncitem=self)

1790

1791 def setup(self) -> None:

1792 self._request._fillfixtures()

1793

1794 def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:

1795 if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False):

1796 code = _pytest._code.Code.from_function(get_real_func(self.obj))

1797 path, firstlineno = code.path, code.firstlineno

1798 traceback = excinfo.traceback

1799 ntraceback = traceback.cut(path=path, firstlineno=firstlineno)

1800 if ntraceback == traceback:

1801 ntraceback = ntraceback.cut(path=path)

1802 if ntraceback == traceback:

1803 ntraceback = ntraceback.filter(filter_traceback)

1804 if not ntraceback:

1805 ntraceback = traceback

1806

1807 excinfo.traceback = ntraceback.filter()

1808 # issue364: mark all but first and last frames to

1809 # only show a single-line message for each frame.

1810 if self.config.getoption("tbstyle", "auto") == "auto":

1811 if len(excinfo.traceback) > 2:

1812 for entry in excinfo.traceback[1:-1]:

1813 entry.set_repr_style("short")

1814

1815 # TODO: Type ignored -- breaks Liskov Substitution.

1816 def repr_failure( # type: ignore[override]

1817 self,

1818 excinfo: ExceptionInfo[BaseException],

1819 ) -> Union[str, TerminalRepr]:

1820 style = self.config.getoption("tbstyle", "auto")

1821 if style == "auto":

1822 style = "long"

1823 return self._repr_failure_py(excinfo, style=style)

1824

1825

1826 class FunctionDefinition(Function):

1827 """

1828 This class is a step gap solution until we evolve to have actual function definition nodes

1829 and manage to get rid of ``metafunc``.

1830 """

1831

1832 def runtest(self) -> None:

1833 raise RuntimeError("function definitions are not supposed to be run as tests")

1834

1835 setup = runtest