Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

2import enum 

3import fnmatch 

4import inspect 

5import itertools 

6import os 

7import sys 

8import typing 

9import warnings 

10from collections import Counter 

11from collections import defaultdict 

12from collections.abc import Sequence 

13from functools import partial 

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 Set 

24from typing import Tuple 

25from typing import Union 

26 

27import py 

28 

29import _pytest 

30from _pytest import fixtures 

31from _pytest import nodes 

32from _pytest._code import filter_traceback 

33from _pytest._code import getfslineno 

34from _pytest._code.code import ExceptionInfo 

35from _pytest._io import TerminalWriter 

36from _pytest._io.saferepr import saferepr 

37from _pytest.compat import ascii_escaped 

38from _pytest.compat import get_default_arg_names 

39from _pytest.compat import get_real_func 

40from _pytest.compat import getimfunc 

41from _pytest.compat import getlocation 

42from _pytest.compat import is_async_function 

43from _pytest.compat import is_generator 

44from _pytest.compat import NOTSET 

45from _pytest.compat import REGEX_TYPE 

46from _pytest.compat import safe_getattr 

47from _pytest.compat import safe_isclass 

48from _pytest.compat import STRING_TYPES 

49from _pytest.compat import TYPE_CHECKING 

50from _pytest.config import Config 

51from _pytest.config import ExitCode 

52from _pytest.config import hookimpl 

53from _pytest.config.argparsing import Parser 

54from _pytest.deprecated import FUNCARGNAMES 

55from _pytest.fixtures import FuncFixtureInfo 

56from _pytest.main import Session 

57from _pytest.mark import MARK_GEN 

58from _pytest.mark import ParameterSet 

59from _pytest.mark.structures import get_unpacked_marks 

60from _pytest.mark.structures import Mark 

61from _pytest.mark.structures import MarkDecorator 

62from _pytest.mark.structures import normalize_mark_list 

63from _pytest.outcomes import fail 

64from _pytest.outcomes import skip 

65from _pytest.pathlib import import_path 

66from _pytest.pathlib import ImportPathMismatchError 

67from _pytest.pathlib import parts 

68from _pytest.reports import TerminalRepr 

69from _pytest.warning_types import PytestCollectionWarning 

70from _pytest.warning_types import PytestUnhandledCoroutineWarning 

71 

72if TYPE_CHECKING: 

73 from typing import Type 

74 from typing_extensions import Literal 

75 from _pytest.fixtures import _Scope 

76 

77 

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

79 group = parser.getgroup("general") 

80 group.addoption( 

81 "--fixtures", 

82 "--funcargs", 

83 action="store_true", 

84 dest="showfixtures", 

85 default=False, 

86 help="show available fixtures, sorted by plugin appearance " 

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

88 ) 

89 group.addoption( 

90 "--fixtures-per-test", 

91 action="store_true", 

92 dest="show_fixtures_per_test", 

93 default=False, 

94 help="show fixtures per test", 

95 ) 

96 parser.addini( 

97 "python_files", 

98 type="args", 

99 # NOTE: default is also used in AssertionRewritingHook. 

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

101 help="glob-style file patterns for Python test module discovery", 

102 ) 

103 parser.addini( 

104 "python_classes", 

105 type="args", 

106 default=["Test"], 

107 help="prefixes or glob names for Python test class discovery", 

108 ) 

109 parser.addini( 

110 "python_functions", 

111 type="args", 

112 default=["test"], 

113 help="prefixes or glob names for Python test function and method discovery", 

114 ) 

115 parser.addini( 

116 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", 

117 type="bool", 

118 default=False, 

119 help="disable string escape non-ascii characters, might cause unwanted " 

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

121 ) 

122 

123 

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

125 if config.option.showfixtures: 

126 showfixtures(config) 

127 return 0 

128 if config.option.show_fixtures_per_test: 

129 show_fixtures_per_test(config) 

130 return 0 

131 return None 

132 

133 

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

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

136 # TODO: Fix this type-ignore (overlapping kwargs). 

137 metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) # type: ignore[misc] 

138 

139 

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

141 config.addinivalue_line( 

142 "markers", 

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

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

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

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

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

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

149 "see https://docs.pytest.org/en/stable/parametrize.html for more info " 

150 "and examples.", 

151 ) 

152 config.addinivalue_line( 

153 "markers", 

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

155 "all of the specified fixtures. see " 

156 "https://docs.pytest.org/en/stable/fixture.html#usefixtures ", 

157 ) 

158 

159 

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

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

162 msg += ( 

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

164 ) 

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

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

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

168 msg += " - pytest-twisted" 

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

170 skip(msg="async def function and no async plugin installed (see warnings)") 

171 

172 

173@hookimpl(trylast=True) 

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

175 testfunction = pyfuncitem.obj 

176 if is_async_function(testfunction): 

177 async_warn_and_skip(pyfuncitem.nodeid) 

178 funcargs = pyfuncitem.funcargs 

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

180 result = testfunction(**testargs) 

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

182 async_warn_and_skip(pyfuncitem.nodeid) 

183 return True 

184 

185 

186def pytest_collect_file(path: py.path.local, parent) -> Optional["Module"]: 

187 ext = path.ext 

188 if ext == ".py": 

189 if not parent.session.isinitpath(path): 

190 if not path_matches_patterns( 

191 path, parent.config.getini("python_files") + ["__init__.py"] 

192 ): 

193 return None 

194 ihook = parent.session.gethookproxy(path) 

195 module = ihook.pytest_pycollect_makemodule( 

196 path=path, parent=parent 

197 ) # type: Module 

198 return module 

199 return None 

200 

201 

202def path_matches_patterns(path: py.path.local, patterns: Iterable[str]) -> bool: 

203 """Returns True if path matches any of the patterns in the list of globs given.""" 

204 return any(path.fnmatch(pattern) for pattern in patterns) 

205 

206 

207def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Module": 

208 if path.basename == "__init__.py": 

209 pkg = Package.from_parent(parent, fspath=path) # type: Package 

210 return pkg 

211 mod = Module.from_parent(parent, fspath=path) # type: Module 

212 return mod 

213 

214 

215@hookimpl(trylast=True) 

216def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object): 

217 # nothing was collected elsewhere, let's do it here 

218 if safe_isclass(obj): 

219 if collector.istestclass(obj, name): 

220 return Class.from_parent(collector, name=name, obj=obj) 

221 elif collector.istestfunction(obj, name): 

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

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

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

225 # or a functools.wrapped. 

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

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

228 filename, lineno = getfslineno(obj) 

229 warnings.warn_explicit( 

230 message=PytestCollectionWarning( 

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

232 ), 

233 category=None, 

234 filename=str(filename), 

235 lineno=lineno + 1, 

236 ) 

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

238 if is_generator(obj): 

239 res = Function.from_parent(collector, name=name) 

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

241 name=name 

242 ) 

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

244 res.warn(PytestCollectionWarning(reason)) 

245 else: 

246 res = list(collector._genfunctions(name, obj)) 

247 return res 

248 

249 

250class PyobjMixin: 

251 _ALLOW_MARKERS = True 

252 

253 # Function and attributes that the mixin needs (for type-checking only). 

254 if TYPE_CHECKING: 

255 name = "" # type: str 

256 parent = None # type: Optional[nodes.Node] 

257 own_markers = [] # type: List[Mark] 

258 

259 def getparent(self, cls: Type[nodes._NodeType]) -> Optional[nodes._NodeType]: 

260 ... 

261 

262 def listchain(self) -> List[nodes.Node]: 

263 ... 

264 

265 @property 

266 def module(self): 

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

268 node = self.getparent(Module) 

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

270 

271 @property 

272 def cls(self): 

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

274 node = self.getparent(Class) 

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

276 

277 @property 

278 def instance(self): 

279 """Python instance object this node was collected from (can be None).""" 

280 node = self.getparent(Instance) 

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

282 

283 @property 

284 def obj(self): 

285 """Underlying Python object.""" 

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

287 if obj is None: 

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

289 # XXX evil hack 

290 # used to avoid Instance collector marker duplication 

291 if self._ALLOW_MARKERS: 

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

293 return obj 

294 

295 @obj.setter 

296 def obj(self, value): 

297 self._obj = value 

298 

299 def _getobj(self): 

300 """Gets the underlying Python object. May be overwritten by subclasses.""" 

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

302 assert self.parent is not None 

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

304 return getattr(obj, self.name) 

305 

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

307 """ return python path relative to the containing module. """ 

308 chain = self.listchain() 

309 chain.reverse() 

310 parts = [] 

311 for node in chain: 

312 if isinstance(node, Instance): 

313 continue 

314 name = node.name 

315 if isinstance(node, Module): 

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

317 if stopatmodule: 

318 if includemodule: 

319 parts.append(name) 

320 break 

321 parts.append(name) 

322 parts.reverse() 

323 return ".".join(parts) 

324 

325 def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]: 

326 # XXX caching? 

327 obj = self.obj 

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

329 if isinstance(compat_co_firstlineno, int): 

330 # nose compatibility 

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

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

333 file_path = file_path[:-1] 

334 fspath = file_path # type: Union[py.path.local, str] 

335 lineno = compat_co_firstlineno 

336 else: 

337 fspath, lineno = getfslineno(obj) 

338 modpath = self.getmodpath() 

339 assert isinstance(lineno, int) 

340 return fspath, lineno, modpath 

341 

342 

343class PyCollector(PyobjMixin, nodes.Collector): 

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

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

346 

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

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

349 @nose.tools.istest decorator 

350 """ 

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

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

353 # function) as test classes. 

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

355 

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

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

358 

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

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

361 if isinstance(obj, staticmethod): 

362 # static methods need to be unwrapped 

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

364 return ( 

365 safe_getattr(obj, "__call__", False) 

366 and fixtures.getfixturemarker(obj) is None 

367 ) 

368 else: 

369 return False 

370 

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

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

373 

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

375 """ 

376 checks if the given name matches the prefix or glob-pattern defined 

377 in ini configuration. 

378 """ 

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

380 if name.startswith(option): 

381 return True 

382 # check that name looks like a glob-string before calling fnmatch 

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

384 # and fnmatch is somewhat expensive to call 

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

386 name, option 

387 ): 

388 return True 

389 return False 

390 

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

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

393 return [] 

394 

395 # NB. we avoid random getattrs and peek in the __dict__ instead 

396 # (XXX originally introduced from a PyPy need, still true?) 

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

398 for basecls in self.obj.__class__.__mro__: 

399 dicts.append(basecls.__dict__) 

400 seen = set() # type: Set[str] 

401 values = [] # type: List[Union[nodes.Item, nodes.Collector]] 

402 for dic in dicts: 

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

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

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

406 if name in seen: 

407 continue 

408 seen.add(name) 

409 res = self._makeitem(name, obj) 

410 if res is None: 

411 continue 

412 if not isinstance(res, list): 

413 res = [res] 

414 values.extend(res) 

415 

416 def sort_key(item): 

417 fspath, lineno, _ = item.reportinfo() 

418 return (str(fspath), lineno) 

419 

420 values.sort(key=sort_key) 

421 return values 

422 

423 def _makeitem( 

424 self, name: str, obj: object 

425 ) -> Union[ 

426 None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]] 

427 ]: 

428 # assert self.ihook.fspath == self.fspath, self 

429 item = self.ihook.pytest_pycollect_makeitem( 

430 collector=self, name=name, obj=obj 

431 ) # type: Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]] 

432 return item 

433 

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

435 modulecol = self.getparent(Module) 

436 assert modulecol is not None 

437 module = modulecol.obj 

438 clscol = self.getparent(Class) 

439 cls = clscol and clscol.obj or None 

440 fm = self.session._fixturemanager 

441 

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

443 fixtureinfo = definition._fixtureinfo 

444 

445 metafunc = Metafunc( 

446 definition, fixtureinfo, self.config, cls=cls, module=module 

447 ) 

448 methods = [] 

449 if hasattr(module, "pytest_generate_tests"): 

450 methods.append(module.pytest_generate_tests) 

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

452 methods.append(cls().pytest_generate_tests) 

453 

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

455 

456 if not metafunc._calls: 

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

458 else: 

459 # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs 

460 fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm) 

461 

462 # add_funcarg_pseudo_fixture_def may have shadowed some fixtures 

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

464 # function really needs. 

465 fixtureinfo.prune_dependency_tree() 

466 

467 for callspec in metafunc._calls: 

468 subname = "{}[{}]".format(name, callspec.id) 

469 yield Function.from_parent( 

470 self, 

471 name=subname, 

472 callspec=callspec, 

473 callobj=funcobj, 

474 fixtureinfo=fixtureinfo, 

475 keywords={callspec.id: True}, 

476 originalname=name, 

477 ) 

478 

479 

480class Module(nodes.File, PyCollector): 

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

482 

483 def _getobj(self): 

484 return self._importtestmodule() 

485 

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

487 self._inject_setup_module_fixture() 

488 self._inject_setup_function_fixture() 

489 self.session._fixturemanager.parsefactories(self) 

490 return super().collect() 

491 

492 def _inject_setup_module_fixture(self) -> None: 

493 """Injects a hidden autouse, module scoped fixture into the collected module object 

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

495 

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

497 other fixtures (#517). 

498 """ 

499 setup_module = _get_first_non_fixture_func( 

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

501 ) 

502 teardown_module = _get_first_non_fixture_func( 

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

504 ) 

505 

506 if setup_module is None and teardown_module is None: 

507 return 

508 

509 @fixtures.fixture(autouse=True, scope="module") 

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

511 if setup_module is not None: 

512 _call_with_optional_argument(setup_module, request.module) 

513 yield 

514 if teardown_module is not None: 

515 _call_with_optional_argument(teardown_module, request.module) 

516 

517 self.obj.__pytest_setup_module = xunit_setup_module_fixture 

518 

519 def _inject_setup_function_fixture(self) -> None: 

520 """Injects a hidden autouse, function scoped fixture into the collected module object 

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

522 

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

524 other fixtures (#517). 

525 """ 

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

527 teardown_function = _get_first_non_fixture_func( 

528 self.obj, ("teardown_function",) 

529 ) 

530 if setup_function is None and teardown_function is None: 

531 return 

532 

533 @fixtures.fixture(autouse=True, scope="function") 

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

535 if request.instance is not None: 

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

537 # setup_method handle this 

538 yield 

539 return 

540 if setup_function is not None: 

541 _call_with_optional_argument(setup_function, request.function) 

542 yield 

543 if teardown_function is not None: 

544 _call_with_optional_argument(teardown_function, request.function) 

545 

546 self.obj.__pytest_setup_function = xunit_setup_function_fixture 

547 

548 def _importtestmodule(self): 

549 # we assume we are only called once per module 

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

551 try: 

552 mod = import_path(self.fspath, mode=importmode) 

553 except SyntaxError as e: 

554 raise self.CollectError( 

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

556 ) from e 

557 except ImportPathMismatchError as e: 

558 raise self.CollectError( 

559 "import file mismatch:\n" 

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

561 " %s\n" 

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

563 " %s\n" 

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

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

566 ) from e 

567 except ImportError as e: 

568 exc_info = ExceptionInfo.from_current() 

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

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

571 exc_repr = ( 

572 exc_info.getrepr(style="short") 

573 if exc_info.traceback 

574 else exc_info.exconly() 

575 ) 

576 formatted_tb = str(exc_repr) 

577 raise self.CollectError( 

578 "ImportError while importing test module '{fspath}'.\n" 

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

580 "Traceback:\n" 

581 "{traceback}".format(fspath=self.fspath, traceback=formatted_tb) 

582 ) from e 

583 except _pytest.runner.Skipped as e: 

584 if e.allow_module_level: 

585 raise 

586 raise self.CollectError( 

587 "Using pytest.skip outside of a test is not allowed. " 

588 "To decorate a test function, use the @pytest.mark.skip " 

589 "or @pytest.mark.skipif decorators instead, and to skip a " 

590 "module use `pytestmark = pytest.mark.{skip,skipif}." 

591 ) from e 

592 self.config.pluginmanager.consider_module(mod) 

593 return mod 

594 

595 

596class Package(Module): 

597 def __init__( 

598 self, 

599 fspath: py.path.local, 

600 parent: nodes.Collector, 

601 # NOTE: following args are unused: 

602 config=None, 

603 session=None, 

604 nodeid=None, 

605 ) -> None: 

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

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

608 session = parent.session 

609 nodes.FSCollector.__init__( 

610 self, fspath, parent=parent, config=config, session=session, nodeid=nodeid 

611 ) 

612 self.name = os.path.basename(str(fspath.dirname)) 

613 

614 def setup(self) -> None: 

615 # not using fixtures to call setup_module here because autouse fixtures 

616 # from packages are not called automatically (#4085) 

617 setup_module = _get_first_non_fixture_func( 

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

619 ) 

620 if setup_module is not None: 

621 _call_with_optional_argument(setup_module, self.obj) 

622 

623 teardown_module = _get_first_non_fixture_func( 

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

625 ) 

626 if teardown_module is not None: 

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

628 self.addfinalizer(func) 

629 

630 def gethookproxy(self, fspath: py.path.local): 

631 return super()._gethookproxy(fspath) 

632 

633 def isinitpath(self, path: py.path.local) -> bool: 

634 return path in self.session._initialpaths 

635 

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

637 this_path = self.fspath.dirpath() 

638 init_module = this_path.join("__init__.py") 

639 if init_module.check(file=1) and path_matches_patterns( 

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

641 ): 

642 yield Module.from_parent(self, fspath=init_module) 

643 pkg_prefixes = set() # type: Set[py.path.local] 

644 for path in this_path.visit(rec=self._recurse, bf=True, sort=True): 

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

646 is_file = path.isfile() 

647 if is_file: 

648 if path.basename == "__init__.py" and path.dirpath() == this_path: 

649 continue 

650 

651 parts_ = parts(path.strpath) 

652 if any( 

653 str(pkg_prefix) in parts_ and pkg_prefix.join("__init__.py") != path 

654 for pkg_prefix in pkg_prefixes 

655 ): 

656 continue 

657 

658 if is_file: 

659 yield from self._collectfile(path) 

660 elif not path.isdir(): 

661 # Broken symlink or invalid/missing file. 

662 continue 

663 elif path.join("__init__.py").check(file=1): 

664 pkg_prefixes.add(path) 

665 

666 

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

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

669 calls func without arguments""" 

670 arg_count = func.__code__.co_argcount 

671 if inspect.ismethod(func): 

672 arg_count -= 1 

673 if arg_count: 

674 func(arg) 

675 else: 

676 func() 

677 

678 

679def _get_first_non_fixture_func(obj: object, names: Iterable[str]): 

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

681 xunit-style function, but only if not marked as a fixture to 

682 avoid calling it twice. 

683 """ 

684 for name in names: 

685 meth = getattr(obj, name, None) 

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

687 return meth 

688 

689 

690class Class(PyCollector): 

691 """ Collector for test methods. """ 

692 

693 @classmethod 

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

695 """ 

696 The public constructor 

697 """ 

698 return super().from_parent(name=name, parent=parent) 

699 

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

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

702 return [] 

703 if hasinit(self.obj): 

704 assert self.parent is not None 

705 self.warn( 

706 PytestCollectionWarning( 

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

708 "__init__ constructor (from: %s)" 

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

710 ) 

711 ) 

712 return [] 

713 elif hasnew(self.obj): 

714 assert self.parent is not None 

715 self.warn( 

716 PytestCollectionWarning( 

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

718 "__new__ constructor (from: %s)" 

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

720 ) 

721 ) 

722 return [] 

723 

724 self._inject_setup_class_fixture() 

725 self._inject_setup_method_fixture() 

726 

727 return [Instance.from_parent(self, name="()")] 

728 

729 def _inject_setup_class_fixture(self) -> None: 

730 """Injects a hidden autouse, class scoped fixture into the collected class object 

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

732 

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

734 other fixtures (#517). 

735 """ 

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

737 teardown_class = getattr(self.obj, "teardown_class", None) 

738 if setup_class is None and teardown_class is None: 

739 return 

740 

741 @fixtures.fixture(autouse=True, scope="class") 

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

743 if setup_class is not None: 

744 func = getimfunc(setup_class) 

745 _call_with_optional_argument(func, self.obj) 

746 yield 

747 if teardown_class is not None: 

748 func = getimfunc(teardown_class) 

749 _call_with_optional_argument(func, self.obj) 

750 

751 self.obj.__pytest_setup_class = xunit_setup_class_fixture 

752 

753 def _inject_setup_method_fixture(self) -> None: 

754 """Injects a hidden autouse, function scoped fixture into the collected class object 

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

756 

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

758 other fixtures (#517). 

759 """ 

760 setup_method = _get_first_non_fixture_func(self.obj, ("setup_method",)) 

761 teardown_method = getattr(self.obj, "teardown_method", None) 

762 if setup_method is None and teardown_method is None: 

763 return 

764 

765 @fixtures.fixture(autouse=True, scope="function") 

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

767 method = request.function 

768 if setup_method is not None: 

769 func = getattr(self, "setup_method") 

770 _call_with_optional_argument(func, method) 

771 yield 

772 if teardown_method is not None: 

773 func = getattr(self, "teardown_method") 

774 _call_with_optional_argument(func, method) 

775 

776 self.obj.__pytest_setup_method = xunit_setup_method_fixture 

777 

778 

779class Instance(PyCollector): 

780 _ALLOW_MARKERS = False # hack, destroy later 

781 # instances share the object with their parents in a way 

782 # that duplicates markers instances if not taken out 

783 # can be removed at node structure reorganization time 

784 

785 def _getobj(self): 

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

787 assert self.parent is not None 

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

789 return obj() 

790 

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

792 self.session._fixturemanager.parsefactories(self) 

793 return super().collect() 

794 

795 def newinstance(self): 

796 self.obj = self._getobj() 

797 return self.obj 

798 

799 

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

801 init = getattr(obj, "__init__", None) # type: object 

802 if init: 

803 return init != object.__init__ 

804 return False 

805 

806 

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

808 new = getattr(obj, "__new__", None) # type: object 

809 if new: 

810 return new != object.__new__ 

811 return False 

812 

813 

814class CallSpec2: 

815 def __init__(self, metafunc: "Metafunc") -> None: 

816 self.metafunc = metafunc 

817 self.funcargs = {} # type: Dict[str, object] 

818 self._idlist = [] # type: List[str] 

819 self.params = {} # type: Dict[str, object] 

820 # Used for sorting parametrized resources. 

821 self._arg2scopenum = {} # type: Dict[str, int] 

822 self.marks = [] # type: List[Mark] 

823 self.indices = {} # type: Dict[str, int] 

824 

825 def copy(self) -> "CallSpec2": 

826 cs = CallSpec2(self.metafunc) 

827 cs.funcargs.update(self.funcargs) 

828 cs.params.update(self.params) 

829 cs.marks.extend(self.marks) 

830 cs.indices.update(self.indices) 

831 cs._arg2scopenum.update(self._arg2scopenum) 

832 cs._idlist = list(self._idlist) 

833 return cs 

834 

835 def _checkargnotcontained(self, arg: str) -> None: 

836 if arg in self.params or arg in self.funcargs: 

837 raise ValueError("duplicate {!r}".format(arg)) 

838 

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

840 try: 

841 return self.params[name] 

842 except KeyError as e: 

843 raise ValueError(name) from e 

844 

845 @property 

846 def id(self) -> str: 

847 return "-".join(map(str, self._idlist)) 

848 

849 def setmulti2( 

850 self, 

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

852 argnames: typing.Sequence[str], 

853 valset: Iterable[object], 

854 id: str, 

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

856 scopenum: int, 

857 param_index: int, 

858 ) -> None: 

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

860 self._checkargnotcontained(arg) 

861 valtype_for_arg = valtypes[arg] 

862 if valtype_for_arg == "params": 

863 self.params[arg] = val 

864 elif valtype_for_arg == "funcargs": 

865 self.funcargs[arg] = val 

866 else: # pragma: no cover 

867 assert False, "Unhandled valtype for arg: {}".format(valtype_for_arg) 

868 self.indices[arg] = param_index 

869 self._arg2scopenum[arg] = scopenum 

870 self._idlist.append(id) 

871 self.marks.extend(normalize_mark_list(marks)) 

872 

873 

874class Metafunc: 

875 """ 

876 Metafunc objects are passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook. 

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

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

879 test function is defined. 

880 """ 

881 

882 def __init__( 

883 self, 

884 definition: "FunctionDefinition", 

885 fixtureinfo: fixtures.FuncFixtureInfo, 

886 config: Config, 

887 cls=None, 

888 module=None, 

889 ) -> None: 

890 self.definition = definition 

891 

892 #: access to the :class:`_pytest.config.Config` object for the test session 

893 self.config = config 

894 

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

896 self.module = module 

897 

898 #: underlying python test function 

899 self.function = definition.obj 

900 

901 #: set of fixture names required by the test function 

902 self.fixturenames = fixtureinfo.names_closure 

903 

904 #: class object where the test function is defined in or ``None``. 

905 self.cls = cls 

906 

907 self._calls = [] # type: List[CallSpec2] 

908 self._arg2fixturedefs = fixtureinfo.name2fixturedefs 

909 

910 @property 

911 def funcargnames(self) -> List[str]: 

912 """ alias attribute for ``fixturenames`` for pre-2.3 compatibility""" 

913 warnings.warn(FUNCARGNAMES, stacklevel=2) 

914 return self.fixturenames 

915 

916 def parametrize( 

917 self, 

918 argnames: Union[str, List[str], Tuple[str, ...]], 

919 argvalues: Iterable[Union[ParameterSet, typing.Sequence[object], object]], 

920 indirect: Union[bool, typing.Sequence[str]] = False, 

921 ids: Optional[ 

922 Union[ 

923 Iterable[Union[None, str, float, int, bool]], 

924 Callable[[Any], Optional[object]], 

925 ] 

926 ] = None, 

927 scope: "Optional[_Scope]" = None, 

928 *, 

929 _param_mark: Optional[Mark] = None 

930 ) -> None: 

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

932 of argvalues for the given argnames. Parametrization is performed 

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

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

935 

936 :arg argnames: a comma-separated string denoting one or more argument 

937 names, or a list/tuple of argument strings. 

938 

939 :arg argvalues: The list of argvalues determines how often a 

940 test is invoked with different argument values. If only one 

941 argname was specified argvalues is a list of values. If N 

942 argnames were specified, argvalues must be a list of N-tuples, 

943 where each tuple-element specifies a value for its respective 

944 argname. 

945 

946 :arg indirect: The list of argnames or boolean. A list of arguments' 

947 names (subset of argnames). If True the list contains all names from 

948 the argnames. Each argvalue corresponding to an argname in this list will 

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

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

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

952 

953 :arg ids: sequence of (or generator for) ids for ``argvalues``, 

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

955 

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

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

958 ``bool``, or ``None``. 

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

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

961 

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

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

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

965 dashes ("-")). 

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

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

968 

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

970 the argvalues. 

971 

972 :arg scope: if specified it denotes the scope of the parameters. 

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

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

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

976 """ 

977 from _pytest.fixtures import scope2index 

978 

979 argnames, parameters = ParameterSet._for_parametrize( 

980 argnames, 

981 argvalues, 

982 self.function, 

983 self.config, 

984 nodeid=self.definition.nodeid, 

985 ) 

986 del argvalues 

987 

988 if "request" in argnames: 

989 fail( 

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

991 pytrace=False, 

992 ) 

993 

994 if scope is None: 

995 scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) 

996 

997 self._validate_if_using_arg_names(argnames, indirect) 

998 

999 arg_values_types = self._resolve_arg_value_types(argnames, indirect) 

1000 

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

1002 if _param_mark and _param_mark._param_ids_from: 

1003 generated_ids = _param_mark._param_ids_from._param_ids_generated 

1004 if generated_ids is not None: 

1005 ids = generated_ids 

1006 

1007 ids = self._resolve_arg_ids( 

1008 argnames, ids, parameters, nodeid=self.definition.nodeid 

1009 ) 

1010 

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

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

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

1014 

1015 scopenum = scope2index( 

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

1017 ) 

1018 

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

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

1021 # of all calls 

1022 newcalls = [] 

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

1024 for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)): 

1025 newcallspec = callspec.copy() 

1026 newcallspec.setmulti2( 

1027 arg_values_types, 

1028 argnames, 

1029 param_set.values, 

1030 param_id, 

1031 param_set.marks, 

1032 scopenum, 

1033 param_index, 

1034 ) 

1035 newcalls.append(newcallspec) 

1036 self._calls = newcalls 

1037 

1038 def _resolve_arg_ids( 

1039 self, 

1040 argnames: typing.Sequence[str], 

1041 ids: Optional[ 

1042 Union[ 

1043 Iterable[Union[None, str, float, int, bool]], 

1044 Callable[[Any], Optional[object]], 

1045 ] 

1046 ], 

1047 parameters: typing.Sequence[ParameterSet], 

1048 nodeid: str, 

1049 ) -> List[str]: 

1050 """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given 

1051 to ``parametrize``. 

1052 

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

1054 :param ids: the ids parameter of the parametrized call (see docs). 

1055 :param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``. 

1056 :param str str: the nodeid of the item that generated this parametrized call. 

1057 :rtype: List[str] 

1058 :return: the list of ids for each argname given 

1059 """ 

1060 if ids is None: 

1061 idfn = None 

1062 ids_ = None 

1063 elif callable(ids): 

1064 idfn = ids 

1065 ids_ = None 

1066 else: 

1067 idfn = None 

1068 ids_ = self._validate_ids(ids, parameters, self.function.__name__) 

1069 return idmaker(argnames, parameters, idfn, ids_, self.config, nodeid=nodeid) 

1070 

1071 def _validate_ids( 

1072 self, 

1073 ids: Iterable[Union[None, str, float, int, bool]], 

1074 parameters: typing.Sequence[ParameterSet], 

1075 func_name: str, 

1076 ) -> List[Union[None, str]]: 

1077 try: 

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

1079 except TypeError: 

1080 try: 

1081 iter(ids) 

1082 except TypeError as e: 

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

1084 num_ids = len(parameters) 

1085 

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

1087 if num_ids != len(parameters) and num_ids != 0: 

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

1089 fail(msg.format(func_name, len(parameters), num_ids), pytrace=False) 

1090 

1091 new_ids = [] 

1092 for idx, id_value in enumerate(itertools.islice(ids, num_ids)): 

1093 if id_value is None or isinstance(id_value, str): 

1094 new_ids.append(id_value) 

1095 elif isinstance(id_value, (float, int, bool)): 

1096 new_ids.append(str(id_value)) 

1097 else: 

1098 msg = "In {}: ids must be list of string/float/int/bool, found: {} (type: {!r}) at index {}" 

1099 fail( 

1100 msg.format(func_name, saferepr(id_value), type(id_value), idx), 

1101 pytrace=False, 

1102 ) 

1103 return new_ids 

1104 

1105 def _resolve_arg_value_types( 

1106 self, 

1107 argnames: typing.Sequence[str], 

1108 indirect: Union[bool, typing.Sequence[str]], 

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

1110 """Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg" 

1111 to the function, based on the ``indirect`` parameter of the parametrized() call. 

1112 

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

1114 :param indirect: same ``indirect`` parameter of ``parametrize()``. 

1115 :rtype: Dict[str, str] 

1116 A dict mapping each arg name to either: 

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

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

1119 """ 

1120 if isinstance(indirect, bool): 

1121 valtypes = dict.fromkeys( 

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

1123 ) # type: Dict[str, Literal["params", "funcargs"]] 

1124 elif isinstance(indirect, Sequence): 

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

1126 for arg in indirect: 

1127 if arg not in argnames: 

1128 fail( 

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

1130 self.function.__name__, arg 

1131 ), 

1132 pytrace=False, 

1133 ) 

1134 valtypes[arg] = "params" 

1135 else: 

1136 fail( 

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

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

1139 ), 

1140 pytrace=False, 

1141 ) 

1142 return valtypes 

1143 

1144 def _validate_if_using_arg_names( 

1145 self, 

1146 argnames: typing.Sequence[str], 

1147 indirect: Union[bool, typing.Sequence[str]], 

1148 ) -> None: 

1149 """ 

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

1151 

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

1153 :param indirect: same ``indirect`` parameter of ``parametrize()``. 

1154 :raise ValueError: if validation fails. 

1155 """ 

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

1157 func_name = self.function.__name__ 

1158 for arg in argnames: 

1159 if arg not in self.fixturenames: 

1160 if arg in default_arg_names: 

1161 fail( 

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

1163 func_name, arg 

1164 ), 

1165 pytrace=False, 

1166 ) 

1167 else: 

1168 if isinstance(indirect, Sequence): 

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

1170 else: 

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

1172 fail( 

1173 "In {}: function uses no {} '{}'".format(func_name, name, arg), 

1174 pytrace=False, 

1175 ) 

1176 

1177 

1178def _find_parametrized_scope( 

1179 argnames: typing.Sequence[str], 

1180 arg2fixturedefs: Mapping[str, typing.Sequence[fixtures.FixtureDef]], 

1181 indirect: Union[bool, typing.Sequence[str]], 

1182) -> "fixtures._Scope": 

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

1184 

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

1186 

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

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

1189 

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

1191 """ 

1192 if isinstance(indirect, Sequence): 

1193 all_arguments_are_fixtures = len(indirect) == len(argnames) 

1194 else: 

1195 all_arguments_are_fixtures = bool(indirect) 

1196 

1197 if all_arguments_are_fixtures: 

1198 fixturedefs = arg2fixturedefs or {} 

1199 used_scopes = [ 

1200 fixturedef[0].scope 

1201 for name, fixturedef in fixturedefs.items() 

1202 if name in argnames 

1203 ] 

1204 if used_scopes: 

1205 # Takes the most narrow scope from used fixtures 

1206 for scope in reversed(fixtures.scopes): 

1207 if scope in used_scopes: 

1208 return scope 

1209 

1210 return "function" 

1211 

1212 

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

1214 if config is None: 

1215 escape_option = False 

1216 else: 

1217 escape_option = config.getini( 

1218 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" 

1219 ) 

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

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

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

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

1224 

1225 

1226def _idval( 

1227 val: object, 

1228 argname: str, 

1229 idx: int, 

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

1231 nodeid: Optional[str], 

1232 config: Optional[Config], 

1233) -> str: 

1234 if idfn: 

1235 try: 

1236 generated_id = idfn(val) 

1237 if generated_id is not None: 

1238 val = generated_id 

1239 except Exception as e: 

1240 prefix = "{}: ".format(nodeid) if nodeid is not None else "" 

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

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

1243 raise ValueError(msg) from e 

1244 elif config: 

1245 hook_id = config.hook.pytest_make_parametrize_id( 

1246 config=config, val=val, argname=argname 

1247 ) # type: Optional[str] 

1248 if hook_id: 

1249 return hook_id 

1250 

1251 if isinstance(val, STRING_TYPES): 

1252 return _ascii_escaped_by_config(val, config) 

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

1254 return str(val) 

1255 elif isinstance(val, REGEX_TYPE): 

1256 return ascii_escaped(val.pattern) 

1257 elif val is NOTSET: 

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

1259 pass 

1260 elif isinstance(val, enum.Enum): 

1261 return str(val) 

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

1263 # name of a class, function, module, etc. 

1264 name = getattr(val, "__name__") # type: str 

1265 return name 

1266 return str(argname) + str(idx) 

1267 

1268 

1269def _idvalset( 

1270 idx: int, 

1271 parameterset: ParameterSet, 

1272 argnames: Iterable[str], 

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

1274 ids: Optional[List[Union[None, str]]], 

1275 nodeid: Optional[str], 

1276 config: Optional[Config], 

1277) -> str: 

1278 if parameterset.id is not None: 

1279 return parameterset.id 

1280 id = None if ids is None or idx >= len(ids) else ids[idx] 

1281 if id is None: 

1282 this_id = [ 

1283 _idval(val, argname, idx, idfn, nodeid=nodeid, config=config) 

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

1285 ] 

1286 return "-".join(this_id) 

1287 else: 

1288 return _ascii_escaped_by_config(id, config) 

1289 

1290 

1291def idmaker( 

1292 argnames: Iterable[str], 

1293 parametersets: Iterable[ParameterSet], 

1294 idfn: Optional[Callable[[Any], Optional[object]]] = None, 

1295 ids: Optional[List[Union[None, str]]] = None, 

1296 config: Optional[Config] = None, 

1297 nodeid: Optional[str] = None, 

1298) -> List[str]: 

1299 resolved_ids = [ 

1300 _idvalset( 

1301 valindex, parameterset, argnames, idfn, ids, config=config, nodeid=nodeid 

1302 ) 

1303 for valindex, parameterset in enumerate(parametersets) 

1304 ] 

1305 

1306 # All IDs must be unique! 

1307 unique_ids = set(resolved_ids) 

1308 if len(unique_ids) != len(resolved_ids): 

1309 

1310 # Record the number of occurrences of each test ID 

1311 test_id_counts = Counter(resolved_ids) 

1312 

1313 # Map the test ID to its next suffix 

1314 test_id_suffixes = defaultdict(int) # type: Dict[str, int] 

1315 

1316 # Suffix non-unique IDs to make them unique 

1317 for index, test_id in enumerate(resolved_ids): 

1318 if test_id_counts[test_id] > 1: 

1319 resolved_ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id]) 

1320 test_id_suffixes[test_id] += 1 

1321 

1322 return resolved_ids 

1323 

1324 

1325def show_fixtures_per_test(config): 

1326 from _pytest.main import wrap_session 

1327 

1328 return wrap_session(config, _show_fixtures_per_test) 

1329 

1330 

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

1332 import _pytest.config 

1333 

1334 session.perform_collect() 

1335 curdir = py.path.local() 

1336 tw = _pytest.config.create_terminal_writer(config) 

1337 verbose = config.getvalue("verbose") 

1338 

1339 def get_best_relpath(func): 

1340 loc = getlocation(func, curdir) 

1341 return curdir.bestrelpath(py.path.local(loc)) 

1342 

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

1344 argname = fixture_def.argname 

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

1346 return 

1347 if verbose > 0: 

1348 bestrel = get_best_relpath(fixture_def.func) 

1349 funcargspec = "{} -- {}".format(argname, bestrel) 

1350 else: 

1351 funcargspec = argname 

1352 tw.line(funcargspec, green=True) 

1353 fixture_doc = inspect.getdoc(fixture_def.func) 

1354 if fixture_doc: 

1355 write_docstring(tw, fixture_doc) 

1356 else: 

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

1358 

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

1360 # Not all items have _fixtureinfo attribute. 

1361 info = getattr(item, "_fixtureinfo", None) # type: Optional[FuncFixtureInfo] 

1362 if info is None or not info.name2fixturedefs: 

1363 # This test item does not use any fixtures. 

1364 return 

1365 tw.line() 

1366 tw.sep("-", "fixtures used by {}".format(item.name)) 

1367 # TODO: Fix this type ignore. 

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

1369 # dict key not used in loop but needed for sorting 

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

1371 assert fixturedefs is not None 

1372 if not fixturedefs: 

1373 continue 

1374 # last item is expected to be the one used by the test item 

1375 write_fixture(fixturedefs[-1]) 

1376 

1377 for session_item in session.items: 

1378 write_item(session_item) 

1379 

1380 

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

1382 from _pytest.main import wrap_session 

1383 

1384 return wrap_session(config, _showfixtures_main) 

1385 

1386 

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

1388 import _pytest.config 

1389 

1390 session.perform_collect() 

1391 curdir = py.path.local() 

1392 tw = _pytest.config.create_terminal_writer(config) 

1393 verbose = config.getvalue("verbose") 

1394 

1395 fm = session._fixturemanager 

1396 

1397 available = [] 

1398 seen = set() # type: Set[Tuple[str, str]] 

1399 

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

1401 assert fixturedefs is not None 

1402 if not fixturedefs: 

1403 continue 

1404 for fixturedef in fixturedefs: 

1405 loc = getlocation(fixturedef.func, curdir) 

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

1407 continue 

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

1409 available.append( 

1410 ( 

1411 len(fixturedef.baseid), 

1412 fixturedef.func.__module__, 

1413 curdir.bestrelpath(py.path.local(loc)), 

1414 fixturedef.argname, 

1415 fixturedef, 

1416 ) 

1417 ) 

1418 

1419 available.sort() 

1420 currentmodule = None 

1421 for baseid, module, bestrel, argname, fixturedef in available: 

1422 if currentmodule != module: 

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

1424 tw.line() 

1425 tw.sep("-", "fixtures defined from {}".format(module)) 

1426 currentmodule = module 

1427 if verbose <= 0 and argname[0] == "_": 

1428 continue 

1429 tw.write(argname, green=True) 

1430 if fixturedef.scope != "function": 

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

1432 if verbose > 0: 

1433 tw.write(" -- %s" % bestrel, yellow=True) 

1434 tw.write("\n") 

1435 loc = getlocation(fixturedef.func, curdir) 

1436 doc = inspect.getdoc(fixturedef.func) 

1437 if doc: 

1438 write_docstring(tw, doc) 

1439 else: 

1440 tw.line(" {}: no docstring available".format(loc), red=True) 

1441 tw.line() 

1442 

1443 

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

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

1446 tw.line(indent + line) 

1447 

1448 

1449class Function(PyobjMixin, nodes.Item): 

1450 """ a Function Item is responsible for setting up and executing a 

1451 Python test function. 

1452 """ 

1453 

1454 # disable since functions handle it themselves 

1455 _ALLOW_MARKERS = False 

1456 

1457 def __init__( 

1458 self, 

1459 name: str, 

1460 parent, 

1461 config: Optional[Config] = None, 

1462 callspec: Optional[CallSpec2] = None, 

1463 callobj=NOTSET, 

1464 keywords=None, 

1465 session: Optional[Session] = None, 

1466 fixtureinfo: Optional[FuncFixtureInfo] = None, 

1467 originalname: Optional[str] = None, 

1468 ) -> None: 

1469 """ 

1470 param name: the full function name, including any decorations like those 

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

1472 param parent: the parent Node. 

1473 param config: the pytest Config object 

1474 param callspec: if given, this is function has been parametrized and the callspec contains 

1475 meta information about the parametrization. 

1476 param callobj: if given, the object which will be called when the Function is invoked, 

1477 otherwise the callobj will be obtained from ``parent`` using ``originalname`` 

1478 param keywords: keywords bound to the function object for "-k" matching. 

1479 param session: the pytest Session object 

1480 param fixtureinfo: fixture information already resolved at this fixture node. 

1481 param originalname: 

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

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

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

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

1486 """ 

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

1488 

1489 if callobj is not NOTSET: 

1490 self.obj = callobj 

1491 

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

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

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

1495 #: explicitly). 

1496 #: 

1497 #: .. versionadded:: 3.0 

1498 self.originalname = originalname or name 

1499 

1500 # note: when FunctionDefinition is introduced, we should change ``originalname`` 

1501 # to a readonly property that returns FunctionDefinition.name 

1502 

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

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

1505 if callspec: 

1506 self.callspec = callspec 

1507 # this is total hostile and a mess 

1508 # keywords are broken by design by now 

1509 # this will be redeemed later 

1510 for mark in callspec.marks: 

1511 # feel free to cry, this was broken for years before 

1512 # and keywords cant fix it per design 

1513 self.keywords[mark.name] = mark 

1514 self.own_markers.extend(normalize_mark_list(callspec.marks)) 

1515 if keywords: 

1516 self.keywords.update(keywords) 

1517 

1518 # todo: this is a hell of a hack 

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

1520 

1521 self.keywords.update( 

1522 { 

1523 mark.name: True 

1524 for mark in self.iter_markers() 

1525 if mark.name not in self.keywords 

1526 } 

1527 ) 

1528 

1529 if fixtureinfo is None: 

1530 fixtureinfo = self.session._fixturemanager.getfixtureinfo( 

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

1532 ) 

1533 self._fixtureinfo = fixtureinfo # type: FuncFixtureInfo 

1534 self.fixturenames = fixtureinfo.names_closure 

1535 self._initrequest() 

1536 

1537 @classmethod 

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

1539 """ 

1540 The public constructor 

1541 """ 

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

1543 

1544 def _initrequest(self) -> None: 

1545 self.funcargs = {} # type: Dict[str, object] 

1546 self._request = fixtures.FixtureRequest(self) 

1547 

1548 @property 

1549 def function(self): 

1550 "underlying python 'function' object" 

1551 return getimfunc(self.obj) 

1552 

1553 def _getobj(self): 

1554 assert self.parent is not None 

1555 return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined] 

1556 

1557 @property 

1558 def _pyfuncitem(self): 

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

1560 return self 

1561 

1562 @property 

1563 def funcargnames(self) -> List[str]: 

1564 """ alias attribute for ``fixturenames`` for pre-2.3 compatibility""" 

1565 warnings.warn(FUNCARGNAMES, stacklevel=2) 

1566 return self.fixturenames 

1567 

1568 def runtest(self) -> None: 

1569 """ execute the underlying test function. """ 

1570 self.ihook.pytest_pyfunc_call(pyfuncitem=self) 

1571 

1572 def setup(self) -> None: 

1573 if isinstance(self.parent, Instance): 

1574 self.parent.newinstance() 

1575 self.obj = self._getobj() 

1576 self._request._fillfixtures() 

1577 

1578 def _prunetraceback(self, excinfo: ExceptionInfo) -> None: 

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

1580 code = _pytest._code.Code(get_real_func(self.obj)) 

1581 path, firstlineno = code.path, code.firstlineno 

1582 traceback = excinfo.traceback 

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

1584 if ntraceback == traceback: 

1585 ntraceback = ntraceback.cut(path=path) 

1586 if ntraceback == traceback: 

1587 ntraceback = ntraceback.filter(filter_traceback) 

1588 if not ntraceback: 

1589 ntraceback = traceback 

1590 

1591 excinfo.traceback = ntraceback.filter() 

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

1593 # only show a single-line message for each frame 

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

1595 if len(excinfo.traceback) > 2: 

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

1597 entry.set_repr_style("short") 

1598 

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

1600 def repr_failure( # type: ignore[override] 

1601 self, excinfo: ExceptionInfo[BaseException], 

1602 ) -> Union[str, TerminalRepr]: 

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

1604 if style == "auto": 

1605 style = "long" 

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

1607 

1608 

1609class FunctionDefinition(Function): 

1610 """ 

1611 internal hack until we get actual definition nodes instead of the 

1612 crappy metafunc hack 

1613 """ 

1614 

1615 def runtest(self) -> None: 

1616 raise RuntimeError("function definitions are not supposed to be used") 

1617 

1618 setup = runtest