Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/python.py: 55%

904 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

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

2import enum 

3import fnmatch 

4import inspect 

5import itertools 

6import os 

7import sys 

8import types 

9import warnings 

10from collections import Counter 

11from collections import defaultdict 

12from functools import partial 

13from pathlib import Path 

14from typing import Any 

15from typing import Callable 

16from typing import Dict 

17from typing import Generator 

18from typing import Iterable 

19from typing import Iterator 

20from typing import List 

21from typing import Mapping 

22from typing import Optional 

23from typing import Pattern 

24from typing import Sequence 

25from typing import Set 

26from typing import Tuple 

27from typing import TYPE_CHECKING 

28from typing import Union 

29 

30import attr 

31 

32import _pytest 

33from _pytest import fixtures 

34from _pytest import nodes 

35from _pytest._code import filter_traceback 

36from _pytest._code import getfslineno 

37from _pytest._code.code import ExceptionInfo 

38from _pytest._code.code import TerminalRepr 

39from _pytest._io import TerminalWriter 

40from _pytest._io.saferepr import saferepr 

41from _pytest.compat import ascii_escaped 

42from _pytest.compat import assert_never 

43from _pytest.compat import final 

44from _pytest.compat import get_default_arg_names 

45from _pytest.compat import get_real_func 

46from _pytest.compat import getimfunc 

47from _pytest.compat import getlocation 

48from _pytest.compat import is_async_function 

49from _pytest.compat import is_generator 

50from _pytest.compat import LEGACY_PATH 

51from _pytest.compat import NOTSET 

52from _pytest.compat import safe_getattr 

53from _pytest.compat import safe_isclass 

54from _pytest.compat import STRING_TYPES 

55from _pytest.config import Config 

56from _pytest.config import ExitCode 

57from _pytest.config import hookimpl 

58from _pytest.config.argparsing import Parser 

59from _pytest.deprecated import check_ispytest 

60from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH 

61from _pytest.deprecated import INSTANCE_COLLECTOR 

62from _pytest.deprecated import NOSE_SUPPORT_METHOD 

63from _pytest.fixtures import FuncFixtureInfo 

64from _pytest.main import Session 

65from _pytest.mark import MARK_GEN 

66from _pytest.mark import ParameterSet 

67from _pytest.mark.structures import get_unpacked_marks 

68from _pytest.mark.structures import Mark 

69from _pytest.mark.structures import MarkDecorator 

70from _pytest.mark.structures import normalize_mark_list 

71from _pytest.outcomes import fail 

72from _pytest.outcomes import skip 

73from _pytest.pathlib import bestrelpath 

74from _pytest.pathlib import fnmatch_ex 

75from _pytest.pathlib import import_path 

76from _pytest.pathlib import ImportPathMismatchError 

77from _pytest.pathlib import parts 

78from _pytest.pathlib import visit 

79from _pytest.scope import Scope 

80from _pytest.warning_types import PytestCollectionWarning 

81from _pytest.warning_types import PytestReturnNotNoneWarning 

82from _pytest.warning_types import PytestUnhandledCoroutineWarning 

83 

84if 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 

93def 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 

139def 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 

149def 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 

154def 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 

174def 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) 

189def 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 

208def 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 

223def 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 

228def 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) 

237def 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 

276class 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 

372class _EmptyClass: pass # noqa: E701 

373IGNORED_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) 

384del _EmptyClass 

385# fmt: on 

386 

387 

388class 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 

525class 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 

662class 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 

779def _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 

791def _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 

801class 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 

930class 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 

937def __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 

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

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

946 if init: 

947 return init != object.__init__ 

948 return False 

949 

950 

951def 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) 

960class 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) 

1113class 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 

1184class 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 

1499def _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 

1531def _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 

1544def _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 

1554def show_fixtures_per_test(config): 

1555 from _pytest.main import wrap_session 

1556 

1557 return wrap_session(config, _show_fixtures_per_test) 

1558 

1559 

1560def _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 

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

1611 from _pytest.main import wrap_session 

1612 

1613 return wrap_session(config, _showfixtures_main) 

1614 

1615 

1616def _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 

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

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

1673 tw.line(indent + line) 

1674 

1675 

1676class 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 

1826class 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