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 os 

6import sys 

7import warnings 

8from collections import Counter 

9from collections import defaultdict 

10from collections.abc import Sequence 

11from functools import partial 

12from textwrap import dedent 

13from typing import List 

14from typing import Tuple 

15 

16import py 

17 

18import _pytest 

19from _pytest import fixtures 

20from _pytest import nodes 

21from _pytest._code import filter_traceback 

22from _pytest.compat import ascii_escaped 

23from _pytest.compat import get_default_arg_names 

24from _pytest.compat import get_real_func 

25from _pytest.compat import getfslineno 

26from _pytest.compat import getimfunc 

27from _pytest.compat import getlocation 

28from _pytest.compat import is_generator 

29from _pytest.compat import iscoroutinefunction 

30from _pytest.compat import NOTSET 

31from _pytest.compat import REGEX_TYPE 

32from _pytest.compat import safe_getattr 

33from _pytest.compat import safe_isclass 

34from _pytest.compat import STRING_TYPES 

35from _pytest.config import hookimpl 

36from _pytest.deprecated import FUNCARGNAMES 

37from _pytest.main import FSHookProxy 

38from _pytest.mark import MARK_GEN 

39from _pytest.mark.structures import get_unpacked_marks 

40from _pytest.mark.structures import normalize_mark_list 

41from _pytest.outcomes import fail 

42from _pytest.outcomes import skip 

43from _pytest.pathlib import parts 

44from _pytest.warning_types import PytestCollectionWarning 

45from _pytest.warning_types import PytestUnhandledCoroutineWarning 

46 

47 

48def pyobj_property(name): 

49 def get(self): 

50 node = self.getparent(getattr(__import__("pytest"), name)) 

51 if node is not None: 

52 return node.obj 

53 

54 doc = "python {} object this node was collected from (can be None).".format( 

55 name.lower() 

56 ) 

57 return property(get, None, None, doc) 

58 

59 

60def pytest_addoption(parser): 

61 group = parser.getgroup("general") 

62 group.addoption( 

63 "--fixtures", 

64 "--funcargs", 

65 action="store_true", 

66 dest="showfixtures", 

67 default=False, 

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

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

70 ) 

71 group.addoption( 

72 "--fixtures-per-test", 

73 action="store_true", 

74 dest="show_fixtures_per_test", 

75 default=False, 

76 help="show fixtures per test", 

77 ) 

78 parser.addini( 

79 "python_files", 

80 type="args", 

81 # NOTE: default is also used in AssertionRewritingHook. 

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

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

84 ) 

85 parser.addini( 

86 "python_classes", 

87 type="args", 

88 default=["Test"], 

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

90 ) 

91 parser.addini( 

92 "python_functions", 

93 type="args", 

94 default=["test"], 

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

96 ) 

97 parser.addini( 

98 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support", 

99 type="bool", 

100 default=False, 

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

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

103 ) 

104 

105 group.addoption( 

106 "--import-mode", 

107 default="prepend", 

108 choices=["prepend", "append"], 

109 dest="importmode", 

110 help="prepend/append to sys.path when importing test modules, " 

111 "default is to prepend.", 

112 ) 

113 

114 

115def pytest_cmdline_main(config): 

116 if config.option.showfixtures: 

117 showfixtures(config) 

118 return 0 

119 if config.option.show_fixtures_per_test: 

120 show_fixtures_per_test(config) 

121 return 0 

122 

123 

124def pytest_generate_tests(metafunc): 

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

126 metafunc.parametrize(*marker.args, **marker.kwargs) 

127 

128 

129def pytest_configure(config): 

130 config.addinivalue_line( 

131 "markers", 

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

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

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

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

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

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

138 "see https://docs.pytest.org/en/latest/parametrize.html for more info " 

139 "and examples.", 

140 ) 

141 config.addinivalue_line( 

142 "markers", 

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

144 "all of the specified fixtures. see " 

145 "https://docs.pytest.org/en/latest/fixture.html#usefixtures ", 

146 ) 

147 

148 

149@hookimpl(trylast=True) 

150def pytest_pyfunc_call(pyfuncitem): 

151 def async_warn(): 

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

153 msg += "You need to install a suitable plugin for your async framework, for example:\n" 

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

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

156 msg += " - pytest-tornasync" 

157 warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid))) 

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

159 

160 testfunction = pyfuncitem.obj 

161 if iscoroutinefunction(testfunction) or ( 

162 sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction) 

163 ): 

164 async_warn() 

165 funcargs = pyfuncitem.funcargs 

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

167 result = testfunction(**testargs) 

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

169 async_warn() 

170 return True 

171 

172 

173def pytest_collect_file(path, parent): 

174 ext = path.ext 

175 if ext == ".py": 

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

177 if not path_matches_patterns( 

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

179 ): 

180 return 

181 ihook = parent.session.gethookproxy(path) 

182 return ihook.pytest_pycollect_makemodule(path=path, parent=parent) 

183 

184 

185def path_matches_patterns(path, patterns): 

186 """Returns True if the given py.path.local matches one of the patterns in the list of globs given""" 

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

188 

189 

190def pytest_pycollect_makemodule(path, parent): 

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

192 return Package(path, parent) 

193 return Module(path, parent) 

194 

195 

196@hookimpl(hookwrapper=True) 

197def pytest_pycollect_makeitem(collector, name, obj): 

198 outcome = yield 

199 res = outcome.get_result() 

200 if res is not None: 

201 return 

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

203 if safe_isclass(obj): 

204 if collector.istestclass(obj, name): 

205 outcome.force_result(Class(name, parent=collector)) 

206 elif collector.istestfunction(obj, name): 

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

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

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

210 # or a functools.wrapped. 

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

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

213 filename, lineno = getfslineno(obj) 

214 warnings.warn_explicit( 

215 message=PytestCollectionWarning( 

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

217 ), 

218 category=None, 

219 filename=str(filename), 

220 lineno=lineno + 1, 

221 ) 

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

223 if is_generator(obj): 

224 res = Function(name, parent=collector) 

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

226 name=name 

227 ) 

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

229 res.warn(PytestCollectionWarning(reason)) 

230 else: 

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

232 outcome.force_result(res) 

233 

234 

235class PyobjContext: 

236 module = pyobj_property("Module") 

237 cls = pyobj_property("Class") 

238 instance = pyobj_property("Instance") 

239 

240 

241class PyobjMixin(PyobjContext): 

242 _ALLOW_MARKERS = True 

243 

244 @property 

245 def obj(self): 

246 """Underlying Python object.""" 

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

248 if obj is None: 

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

250 # XXX evil hack 

251 # used to avoid Instance collector marker duplication 

252 if self._ALLOW_MARKERS: 

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

254 return obj 

255 

256 @obj.setter 

257 def obj(self, value): 

258 self._obj = value 

259 

260 def _getobj(self): 

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

262 return getattr(self.parent.obj, self.name) 

263 

264 def getmodpath(self, stopatmodule=True, includemodule=False): 

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

266 chain = self.listchain() 

267 chain.reverse() 

268 parts = [] 

269 for node in chain: 

270 if isinstance(node, Instance): 

271 continue 

272 name = node.name 

273 if isinstance(node, Module): 

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

275 if stopatmodule: 

276 if includemodule: 

277 parts.append(name) 

278 break 

279 parts.append(name) 

280 parts.reverse() 

281 return ".".join(parts) 

282 

283 def reportinfo(self) -> Tuple[str, int, str]: 

284 # XXX caching? 

285 obj = self.obj 

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

287 if isinstance(compat_co_firstlineno, int): 

288 # nose compatibility 

289 fspath = sys.modules[obj.__module__].__file__ 

290 if fspath.endswith(".pyc"): 

291 fspath = fspath[:-1] 

292 lineno = compat_co_firstlineno 

293 else: 

294 fspath, lineno = getfslineno(obj) 

295 modpath = self.getmodpath() 

296 assert isinstance(lineno, int) 

297 return fspath, lineno, modpath 

298 

299 

300class PyCollector(PyobjMixin, nodes.Collector): 

301 def funcnamefilter(self, name): 

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

303 

304 def isnosetest(self, obj): 

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

306 @nose.tools.istest decorator 

307 """ 

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

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

310 # function) as test classes. 

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

312 

313 def classnamefilter(self, name): 

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

315 

316 def istestfunction(self, obj, name): 

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

318 if isinstance(obj, staticmethod): 

319 # static methods need to be unwrapped 

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

321 return ( 

322 safe_getattr(obj, "__call__", False) 

323 and fixtures.getfixturemarker(obj) is None 

324 ) 

325 else: 

326 return False 

327 

328 def istestclass(self, obj, name): 

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

330 

331 def _matches_prefix_or_glob_option(self, option_name, name): 

332 """ 

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

334 in ini configuration. 

335 """ 

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

337 if name.startswith(option): 

338 return True 

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

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

341 # and fnmatch is somewhat expensive to call 

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

343 name, option 

344 ): 

345 return True 

346 return False 

347 

348 def collect(self): 

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

350 return [] 

351 

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

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

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

355 for basecls in inspect.getmro(self.obj.__class__): 

356 dicts.append(basecls.__dict__) 

357 seen = {} 

358 values = [] 

359 for dic in dicts: 

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

361 if name in seen: 

362 continue 

363 seen[name] = True 

364 res = self._makeitem(name, obj) 

365 if res is None: 

366 continue 

367 if not isinstance(res, list): 

368 res = [res] 

369 values.extend(res) 

370 values.sort(key=lambda item: item.reportinfo()[:2]) 

371 return values 

372 

373 def _makeitem(self, name, obj): 

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

375 return self.ihook.pytest_pycollect_makeitem(collector=self, name=name, obj=obj) 

376 

377 def _genfunctions(self, name, funcobj): 

378 module = self.getparent(Module).obj 

379 clscol = self.getparent(Class) 

380 cls = clscol and clscol.obj or None 

381 fm = self.session._fixturemanager 

382 

383 definition = FunctionDefinition(name=name, parent=self, callobj=funcobj) 

384 fixtureinfo = fm.getfixtureinfo(definition, funcobj, cls) 

385 

386 metafunc = Metafunc( 

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

388 ) 

389 methods = [] 

390 if hasattr(module, "pytest_generate_tests"): 

391 methods.append(module.pytest_generate_tests) 

392 if hasattr(cls, "pytest_generate_tests"): 

393 methods.append(cls().pytest_generate_tests) 

394 

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

396 

397 if not metafunc._calls: 

398 yield Function(name, parent=self, fixtureinfo=fixtureinfo) 

399 else: 

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

401 fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm) 

402 

403 # add_funcarg_pseudo_fixture_def may have shadowed some fixtures 

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

405 # function really needs. 

406 fixtureinfo.prune_dependency_tree() 

407 

408 for callspec in metafunc._calls: 

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

410 yield Function( 

411 name=subname, 

412 parent=self, 

413 callspec=callspec, 

414 callobj=funcobj, 

415 fixtureinfo=fixtureinfo, 

416 keywords={callspec.id: True}, 

417 originalname=name, 

418 ) 

419 

420 

421class Module(nodes.File, PyCollector): 

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

423 

424 def _getobj(self): 

425 return self._importtestmodule() 

426 

427 def collect(self): 

428 self._inject_setup_module_fixture() 

429 self._inject_setup_function_fixture() 

430 self.session._fixturemanager.parsefactories(self) 

431 return super().collect() 

432 

433 def _inject_setup_module_fixture(self): 

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

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

436 

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

438 other fixtures (#517). 

439 """ 

440 setup_module = _get_first_non_fixture_func( 

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

442 ) 

443 teardown_module = _get_first_non_fixture_func( 

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

445 ) 

446 

447 if setup_module is None and teardown_module is None: 

448 return 

449 

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

451 def xunit_setup_module_fixture(request): 

452 if setup_module is not None: 

453 _call_with_optional_argument(setup_module, request.module) 

454 yield 

455 if teardown_module is not None: 

456 _call_with_optional_argument(teardown_module, request.module) 

457 

458 self.obj.__pytest_setup_module = xunit_setup_module_fixture 

459 

460 def _inject_setup_function_fixture(self): 

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

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

463 

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

465 other fixtures (#517). 

466 """ 

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

468 teardown_function = _get_first_non_fixture_func( 

469 self.obj, ("teardown_function",) 

470 ) 

471 if setup_function is None and teardown_function is None: 

472 return 

473 

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

475 def xunit_setup_function_fixture(request): 

476 if request.instance is not None: 

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

478 # setup_method handle this 

479 yield 

480 return 

481 if setup_function is not None: 

482 _call_with_optional_argument(setup_function, request.function) 

483 yield 

484 if teardown_function is not None: 

485 _call_with_optional_argument(teardown_function, request.function) 

486 

487 self.obj.__pytest_setup_function = xunit_setup_function_fixture 

488 

489 def _importtestmodule(self): 

490 # we assume we are only called once per module 

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

492 try: 

493 mod = self.fspath.pyimport(ensuresyspath=importmode) 

494 except SyntaxError: 

495 raise self.CollectError( 

496 _pytest._code.ExceptionInfo.from_current().getrepr(style="short") 

497 ) 

498 except self.fspath.ImportMismatchError: 

499 e = sys.exc_info()[1] 

500 raise self.CollectError( 

501 "import file mismatch:\n" 

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

503 " %s\n" 

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

505 " %s\n" 

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

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

508 ) 

509 except ImportError: 

510 from _pytest._code.code import ExceptionInfo 

511 

512 exc_info = ExceptionInfo.from_current() 

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

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

515 exc_repr = ( 

516 exc_info.getrepr(style="short") 

517 if exc_info.traceback 

518 else exc_info.exconly() 

519 ) 

520 formatted_tb = str(exc_repr) 

521 raise self.CollectError( 

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

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

524 "Traceback:\n" 

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

526 ) 

527 except _pytest.runner.Skipped as e: 

528 if e.allow_module_level: 

529 raise 

530 raise self.CollectError( 

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

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

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

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

535 ) 

536 self.config.pluginmanager.consider_module(mod) 

537 return mod 

538 

539 

540class Package(Module): 

541 def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None): 

542 session = parent.session 

543 nodes.FSCollector.__init__( 

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

545 ) 

546 self.name = fspath.dirname 

547 self.trace = session.trace 

548 self._norecursepatterns = session._norecursepatterns 

549 self.fspath = fspath 

550 

551 def setup(self): 

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

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

554 setup_module = _get_first_non_fixture_func( 

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

556 ) 

557 if setup_module is not None: 

558 _call_with_optional_argument(setup_module, self.obj) 

559 

560 teardown_module = _get_first_non_fixture_func( 

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

562 ) 

563 if teardown_module is not None: 

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

565 self.addfinalizer(func) 

566 

567 def _recurse(self, dirpath): 

568 if dirpath.basename == "__pycache__": 

569 return False 

570 ihook = self.gethookproxy(dirpath.dirpath()) 

571 if ihook.pytest_ignore_collect(path=dirpath, config=self.config): 

572 return 

573 for pat in self._norecursepatterns: 

574 if dirpath.check(fnmatch=pat): 

575 return False 

576 ihook = self.gethookproxy(dirpath) 

577 ihook.pytest_collect_directory(path=dirpath, parent=self) 

578 return True 

579 

580 def gethookproxy(self, fspath): 

581 # check if we have the common case of running 

582 # hooks with all conftest.py filesall conftest.py 

583 pm = self.config.pluginmanager 

584 my_conftestmodules = pm._getconftestmodules(fspath) 

585 remove_mods = pm._conftest_plugins.difference(my_conftestmodules) 

586 if remove_mods: 

587 # one or more conftests are not in use at this fspath 

588 proxy = FSHookProxy(fspath, pm, remove_mods) 

589 else: 

590 # all plugins are active for this fspath 

591 proxy = self.config.hook 

592 return proxy 

593 

594 def _collectfile(self, path, handle_dupes=True): 

595 assert ( 

596 path.isfile() 

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

598 path, path.isdir(), path.exists(), path.islink() 

599 ) 

600 ihook = self.gethookproxy(path) 

601 if not self.isinitpath(path): 

602 if ihook.pytest_ignore_collect(path=path, config=self.config): 

603 return () 

604 

605 if handle_dupes: 

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

607 if not keepduplicates: 

608 duplicate_paths = self.config.pluginmanager._duplicatepaths 

609 if path in duplicate_paths: 

610 return () 

611 else: 

612 duplicate_paths.add(path) 

613 

614 if self.fspath == path: # __init__.py 

615 return [self] 

616 

617 return ihook.pytest_collect_file(path=path, parent=self) 

618 

619 def isinitpath(self, path): 

620 return path in self.session._initialpaths 

621 

622 def collect(self): 

623 this_path = self.fspath.dirpath() 

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

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

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

627 ): 

628 yield Module(init_module, self) 

629 pkg_prefixes = set() 

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

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

632 is_file = path.isfile() 

633 if is_file: 

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

635 continue 

636 

637 parts_ = parts(path.strpath) 

638 if any( 

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

640 for pkg_prefix in pkg_prefixes 

641 ): 

642 continue 

643 

644 if is_file: 

645 yield from self._collectfile(path) 

646 elif not path.isdir(): 

647 # Broken symlink or invalid/missing file. 

648 continue 

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

650 pkg_prefixes.add(path) 

651 

652 

653def _call_with_optional_argument(func, arg): 

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

655 calls func without arguments""" 

656 arg_count = func.__code__.co_argcount 

657 if inspect.ismethod(func): 

658 arg_count -= 1 

659 if arg_count: 

660 func(arg) 

661 else: 

662 func() 

663 

664 

665def _get_first_non_fixture_func(obj, names): 

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

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

668 avoid calling it twice. 

669 """ 

670 for name in names: 

671 meth = getattr(obj, name, None) 

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

673 return meth 

674 

675 

676class Class(PyCollector): 

677 """ Collector for test methods. """ 

678 

679 def collect(self): 

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

681 return [] 

682 if hasinit(self.obj): 

683 self.warn( 

684 PytestCollectionWarning( 

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

686 "__init__ constructor (from: %s)" 

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

688 ) 

689 ) 

690 return [] 

691 elif hasnew(self.obj): 

692 self.warn( 

693 PytestCollectionWarning( 

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

695 "__new__ constructor (from: %s)" 

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

697 ) 

698 ) 

699 return [] 

700 

701 self._inject_setup_class_fixture() 

702 self._inject_setup_method_fixture() 

703 

704 return [Instance(name="()", parent=self)] 

705 

706 def _inject_setup_class_fixture(self): 

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

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

709 

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

711 other fixtures (#517). 

712 """ 

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

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

715 if setup_class is None and teardown_class is None: 

716 return 

717 

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

719 def xunit_setup_class_fixture(cls): 

720 if setup_class is not None: 

721 func = getimfunc(setup_class) 

722 _call_with_optional_argument(func, self.obj) 

723 yield 

724 if teardown_class is not None: 

725 func = getimfunc(teardown_class) 

726 _call_with_optional_argument(func, self.obj) 

727 

728 self.obj.__pytest_setup_class = xunit_setup_class_fixture 

729 

730 def _inject_setup_method_fixture(self): 

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

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

733 

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

735 other fixtures (#517). 

736 """ 

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

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

739 if setup_method is None and teardown_method is None: 

740 return 

741 

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

743 def xunit_setup_method_fixture(self, request): 

744 method = request.function 

745 if setup_method is not None: 

746 func = getattr(self, "setup_method") 

747 _call_with_optional_argument(func, method) 

748 yield 

749 if teardown_method is not None: 

750 func = getattr(self, "teardown_method") 

751 _call_with_optional_argument(func, method) 

752 

753 self.obj.__pytest_setup_method = xunit_setup_method_fixture 

754 

755 

756class Instance(PyCollector): 

757 _ALLOW_MARKERS = False # hack, destroy later 

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

759 # that duplicates markers instances if not taken out 

760 # can be removed at node structure reorganization time 

761 

762 def _getobj(self): 

763 return self.parent.obj() 

764 

765 def collect(self): 

766 self.session._fixturemanager.parsefactories(self) 

767 return super().collect() 

768 

769 def newinstance(self): 

770 self.obj = self._getobj() 

771 return self.obj 

772 

773 

774class FunctionMixin(PyobjMixin): 

775 """ mixin for the code common to Function and Generator. 

776 """ 

777 

778 def setup(self): 

779 """ perform setup for this test function. """ 

780 if isinstance(self.parent, Instance): 

781 self.parent.newinstance() 

782 self.obj = self._getobj() 

783 

784 def _prunetraceback(self, excinfo): 

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

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

787 path, firstlineno = code.path, code.firstlineno 

788 traceback = excinfo.traceback 

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

790 if ntraceback == traceback: 

791 ntraceback = ntraceback.cut(path=path) 

792 if ntraceback == traceback: 

793 ntraceback = ntraceback.filter(filter_traceback) 

794 if not ntraceback: 

795 ntraceback = traceback 

796 

797 excinfo.traceback = ntraceback.filter() 

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

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

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

801 if len(excinfo.traceback) > 2: 

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

803 entry.set_repr_style("short") 

804 

805 def repr_failure(self, excinfo, outerr=None): 

806 assert outerr is None, "XXX outerr usage is deprecated" 

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

808 if style == "auto": 

809 style = "long" 

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

811 

812 

813def hasinit(obj): 

814 init = getattr(obj, "__init__", None) 

815 if init: 

816 return init != object.__init__ 

817 

818 

819def hasnew(obj): 

820 new = getattr(obj, "__new__", None) 

821 if new: 

822 return new != object.__new__ 

823 

824 

825class CallSpec2: 

826 def __init__(self, metafunc): 

827 self.metafunc = metafunc 

828 self.funcargs = {} 

829 self._idlist = [] 

830 self.params = {} 

831 self._globalid = NOTSET 

832 self._globalparam = NOTSET 

833 self._arg2scopenum = {} # used for sorting parametrized resources 

834 self.marks = [] 

835 self.indices = {} 

836 

837 def copy(self): 

838 cs = CallSpec2(self.metafunc) 

839 cs.funcargs.update(self.funcargs) 

840 cs.params.update(self.params) 

841 cs.marks.extend(self.marks) 

842 cs.indices.update(self.indices) 

843 cs._arg2scopenum.update(self._arg2scopenum) 

844 cs._idlist = list(self._idlist) 

845 cs._globalid = self._globalid 

846 cs._globalparam = self._globalparam 

847 return cs 

848 

849 def _checkargnotcontained(self, arg): 

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

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

852 

853 def getparam(self, name): 

854 try: 

855 return self.params[name] 

856 except KeyError: 

857 if self._globalparam is NOTSET: 

858 raise ValueError(name) 

859 return self._globalparam 

860 

861 @property 

862 def id(self): 

863 return "-".join(map(str, filter(None, self._idlist))) 

864 

865 def setmulti2(self, valtypes, argnames, valset, id, marks, scopenum, param_index): 

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

867 self._checkargnotcontained(arg) 

868 valtype_for_arg = valtypes[arg] 

869 getattr(self, valtype_for_arg)[arg] = val 

870 self.indices[arg] = param_index 

871 self._arg2scopenum[arg] = scopenum 

872 self._idlist.append(id) 

873 self.marks.extend(normalize_mark_list(marks)) 

874 

875 

876class Metafunc: 

877 """ 

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

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

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

881 test function is defined. 

882 """ 

883 

884 def __init__( 

885 self, 

886 definition: "FunctionDefinition", 

887 fixtureinfo, 

888 config, 

889 cls=None, 

890 module=None, 

891 ) -> None: 

892 self.definition = definition 

893 

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

895 self.config = config 

896 

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

898 self.module = module 

899 

900 #: underlying python test function 

901 self.function = definition.obj 

902 

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

904 self.fixturenames = fixtureinfo.names_closure 

905 

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

907 self.cls = cls 

908 

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

910 self._arg2fixturedefs = fixtureinfo.name2fixturedefs 

911 

912 @property 

913 def funcargnames(self): 

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

915 warnings.warn(FUNCARGNAMES, stacklevel=2) 

916 return self.fixturenames 

917 

918 def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None): 

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

920 of argvalues for the given argnames. Parametrization is performed 

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

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

923 

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

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

926 

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

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

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

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

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

932 argname. 

933 

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

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

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

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

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

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

940 

941 :arg ids: list of string ids, or a callable. 

942 If strings, each is corresponding to the argvalues so that they are 

943 part of the test id. If None is given as id of specific test, the 

944 automatically generated id for that argument will be used. 

945 If callable, it should take one argument (a single argvalue) and return 

946 a string or return None. If None, the automatically generated id for that 

947 argument will be used. 

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

949 the argvalues. 

950 

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

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

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

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

955 """ 

956 from _pytest.fixtures import scope2index 

957 from _pytest.mark import ParameterSet 

958 

959 argnames, parameters = ParameterSet._for_parametrize( 

960 argnames, 

961 argvalues, 

962 self.function, 

963 self.config, 

964 function_definition=self.definition, 

965 ) 

966 del argvalues 

967 

968 if "request" in argnames: 

969 fail( 

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

971 pytrace=False, 

972 ) 

973 

974 if scope is None: 

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

976 

977 self._validate_if_using_arg_names(argnames, indirect) 

978 

979 arg_values_types = self._resolve_arg_value_types(argnames, indirect) 

980 

981 ids = self._resolve_arg_ids(argnames, ids, parameters, item=self.definition) 

982 

983 scopenum = scope2index( 

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

985 ) 

986 

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

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

989 # of all calls 

990 newcalls = [] 

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

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

993 newcallspec = callspec.copy() 

994 newcallspec.setmulti2( 

995 arg_values_types, 

996 argnames, 

997 param_set.values, 

998 param_id, 

999 param_set.marks, 

1000 scopenum, 

1001 param_index, 

1002 ) 

1003 newcalls.append(newcallspec) 

1004 self._calls = newcalls 

1005 

1006 def _resolve_arg_ids(self, argnames, ids, parameters, item): 

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

1008 to ``parametrize``. 

1009 

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

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

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

1013 :param Item item: the item that generated this parametrized call. 

1014 :rtype: List[str] 

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

1016 """ 

1017 from _pytest._io.saferepr import saferepr 

1018 

1019 idfn = None 

1020 if callable(ids): 

1021 idfn = ids 

1022 ids = None 

1023 if ids: 

1024 func_name = self.function.__name__ 

1025 if len(ids) != len(parameters): 

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

1027 fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False) 

1028 for id_value in ids: 

1029 if id_value is not None and not isinstance(id_value, str): 

1030 msg = "In {}: ids must be list of strings, found: {} (type: {!r})" 

1031 fail( 

1032 msg.format(func_name, saferepr(id_value), type(id_value)), 

1033 pytrace=False, 

1034 ) 

1035 ids = idmaker(argnames, parameters, idfn, ids, self.config, item=item) 

1036 return ids 

1037 

1038 def _resolve_arg_value_types(self, argnames, indirect): 

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

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

1041 

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

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

1044 :rtype: Dict[str, str] 

1045 A dict mapping each arg name to either: 

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

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

1048 """ 

1049 if isinstance(indirect, bool): 

1050 valtypes = dict.fromkeys(argnames, "params" if indirect else "funcargs") 

1051 elif isinstance(indirect, Sequence): 

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

1053 for arg in indirect: 

1054 if arg not in argnames: 

1055 fail( 

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

1057 self.function.__name__, arg 

1058 ), 

1059 pytrace=False, 

1060 ) 

1061 valtypes[arg] = "params" 

1062 else: 

1063 fail( 

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

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

1066 ), 

1067 pytrace=False, 

1068 ) 

1069 return valtypes 

1070 

1071 def _validate_if_using_arg_names(self, argnames, indirect): 

1072 """ 

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

1074 

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

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

1077 :raise ValueError: if validation fails. 

1078 """ 

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

1080 func_name = self.function.__name__ 

1081 for arg in argnames: 

1082 if arg not in self.fixturenames: 

1083 if arg in default_arg_names: 

1084 fail( 

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

1086 func_name, arg 

1087 ), 

1088 pytrace=False, 

1089 ) 

1090 else: 

1091 if isinstance(indirect, (tuple, list)): 

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

1093 else: 

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

1095 fail( 

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

1097 pytrace=False, 

1098 ) 

1099 

1100 

1101def _find_parametrized_scope(argnames, arg2fixturedefs, indirect): 

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

1103 

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

1105 

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

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

1108 

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

1110 """ 

1111 from _pytest.fixtures import scopes 

1112 

1113 if isinstance(indirect, (list, tuple)): 

1114 all_arguments_are_fixtures = len(indirect) == len(argnames) 

1115 else: 

1116 all_arguments_are_fixtures = bool(indirect) 

1117 

1118 if all_arguments_are_fixtures: 

1119 fixturedefs = arg2fixturedefs or {} 

1120 used_scopes = [ 

1121 fixturedef[0].scope 

1122 for name, fixturedef in fixturedefs.items() 

1123 if name in argnames 

1124 ] 

1125 if used_scopes: 

1126 # Takes the most narrow scope from used fixtures 

1127 for scope in reversed(scopes): 

1128 if scope in used_scopes: 

1129 return scope 

1130 

1131 return "function" 

1132 

1133 

1134def _ascii_escaped_by_config(val, config): 

1135 if config is None: 

1136 escape_option = False 

1137 else: 

1138 escape_option = config.getini( 

1139 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support" 

1140 ) 

1141 return val if escape_option else ascii_escaped(val) 

1142 

1143 

1144def _idval(val, argname, idx, idfn, item, config): 

1145 if idfn: 

1146 try: 

1147 generated_id = idfn(val) 

1148 if generated_id is not None: 

1149 val = generated_id 

1150 except Exception as e: 

1151 # See issue https://github.com/pytest-dev/pytest/issues/2169 

1152 msg = "{}: error raised while trying to determine id of parameter '{}' at position {}\n" 

1153 msg = msg.format(item.nodeid, argname, idx) 

1154 raise ValueError(msg) from e 

1155 elif config: 

1156 hook_id = config.hook.pytest_make_parametrize_id( 

1157 config=config, val=val, argname=argname 

1158 ) 

1159 if hook_id: 

1160 return hook_id 

1161 

1162 if isinstance(val, STRING_TYPES): 

1163 return _ascii_escaped_by_config(val, config) 

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

1165 return str(val) 

1166 elif isinstance(val, REGEX_TYPE): 

1167 return ascii_escaped(val.pattern) 

1168 elif isinstance(val, enum.Enum): 

1169 return str(val) 

1170 elif hasattr(val, "__name__") and isinstance(val.__name__, str): 

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

1172 return val.__name__ 

1173 return str(argname) + str(idx) 

1174 

1175 

1176def _idvalset(idx, parameterset, argnames, idfn, ids, item, config): 

1177 if parameterset.id is not None: 

1178 return parameterset.id 

1179 if ids is None or (idx >= len(ids) or ids[idx] is None): 

1180 this_id = [ 

1181 _idval(val, argname, idx, idfn, item=item, config=config) 

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

1183 ] 

1184 return "-".join(this_id) 

1185 else: 

1186 return _ascii_escaped_by_config(ids[idx], config) 

1187 

1188 

1189def idmaker(argnames, parametersets, idfn=None, ids=None, config=None, item=None): 

1190 ids = [ 

1191 _idvalset(valindex, parameterset, argnames, idfn, ids, config=config, item=item) 

1192 for valindex, parameterset in enumerate(parametersets) 

1193 ] 

1194 

1195 # All IDs must be unique! 

1196 unique_ids = set(ids) 

1197 if len(unique_ids) != len(ids): 

1198 

1199 # Record the number of occurrences of each test ID 

1200 test_id_counts = Counter(ids) 

1201 

1202 # Map the test ID to its next suffix 

1203 test_id_suffixes = defaultdict(int) 

1204 

1205 # Suffix non-unique IDs to make them unique 

1206 for index, test_id in enumerate(ids): 

1207 if test_id_counts[test_id] > 1: 

1208 ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id]) 

1209 test_id_suffixes[test_id] += 1 

1210 

1211 return ids 

1212 

1213 

1214def show_fixtures_per_test(config): 

1215 from _pytest.main import wrap_session 

1216 

1217 return wrap_session(config, _show_fixtures_per_test) 

1218 

1219 

1220def _show_fixtures_per_test(config, session): 

1221 import _pytest.config 

1222 

1223 session.perform_collect() 

1224 curdir = py.path.local() 

1225 tw = _pytest.config.create_terminal_writer(config) 

1226 verbose = config.getvalue("verbose") 

1227 

1228 def get_best_relpath(func): 

1229 loc = getlocation(func, curdir) 

1230 return curdir.bestrelpath(loc) 

1231 

1232 def write_fixture(fixture_def): 

1233 argname = fixture_def.argname 

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

1235 return 

1236 if verbose > 0: 

1237 bestrel = get_best_relpath(fixture_def.func) 

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

1239 else: 

1240 funcargspec = argname 

1241 tw.line(funcargspec, green=True) 

1242 fixture_doc = fixture_def.func.__doc__ 

1243 if fixture_doc: 

1244 write_docstring(tw, fixture_doc) 

1245 else: 

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

1247 

1248 def write_item(item): 

1249 try: 

1250 info = item._fixtureinfo 

1251 except AttributeError: 

1252 # doctests items have no _fixtureinfo attribute 

1253 return 

1254 if not info.name2fixturedefs: 

1255 # this test item does not use any fixtures 

1256 return 

1257 tw.line() 

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

1259 tw.sep("-", "({})".format(get_best_relpath(item.function))) 

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

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

1262 assert fixturedefs is not None 

1263 if not fixturedefs: 

1264 continue 

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

1266 write_fixture(fixturedefs[-1]) 

1267 

1268 for session_item in session.items: 

1269 write_item(session_item) 

1270 

1271 

1272def showfixtures(config): 

1273 from _pytest.main import wrap_session 

1274 

1275 return wrap_session(config, _showfixtures_main) 

1276 

1277 

1278def _showfixtures_main(config, session): 

1279 import _pytest.config 

1280 

1281 session.perform_collect() 

1282 curdir = py.path.local() 

1283 tw = _pytest.config.create_terminal_writer(config) 

1284 verbose = config.getvalue("verbose") 

1285 

1286 fm = session._fixturemanager 

1287 

1288 available = [] 

1289 seen = set() 

1290 

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

1292 assert fixturedefs is not None 

1293 if not fixturedefs: 

1294 continue 

1295 for fixturedef in fixturedefs: 

1296 loc = getlocation(fixturedef.func, curdir) 

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

1298 continue 

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

1300 available.append( 

1301 ( 

1302 len(fixturedef.baseid), 

1303 fixturedef.func.__module__, 

1304 curdir.bestrelpath(loc), 

1305 fixturedef.argname, 

1306 fixturedef, 

1307 ) 

1308 ) 

1309 

1310 available.sort() 

1311 currentmodule = None 

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

1313 if currentmodule != module: 

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

1315 tw.line() 

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

1317 currentmodule = module 

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

1319 continue 

1320 tw.write(argname, green=True) 

1321 if fixturedef.scope != "function": 

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

1323 if verbose > 0: 

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

1325 tw.write("\n") 

1326 loc = getlocation(fixturedef.func, curdir) 

1327 doc = fixturedef.func.__doc__ or "" 

1328 if doc: 

1329 write_docstring(tw, doc) 

1330 else: 

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

1332 tw.line() 

1333 

1334 

1335def write_docstring(tw, doc, indent=" "): 

1336 doc = doc.rstrip() 

1337 if "\n" in doc: 

1338 firstline, rest = doc.split("\n", 1) 

1339 else: 

1340 firstline, rest = doc, "" 

1341 

1342 if firstline.strip(): 

1343 tw.line(indent + firstline.strip()) 

1344 

1345 if rest: 

1346 for line in dedent(rest).split("\n"): 

1347 tw.write(indent + line + "\n") 

1348 

1349 

1350class Function(FunctionMixin, nodes.Item): 

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

1352 Python test function. 

1353 """ 

1354 

1355 # disable since functions handle it themselves 

1356 _ALLOW_MARKERS = False 

1357 

1358 def __init__( 

1359 self, 

1360 name, 

1361 parent, 

1362 args=None, 

1363 config=None, 

1364 callspec=None, 

1365 callobj=NOTSET, 

1366 keywords=None, 

1367 session=None, 

1368 fixtureinfo=None, 

1369 originalname=None, 

1370 ): 

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

1372 self._args = args 

1373 if callobj is not NOTSET: 

1374 self.obj = callobj 

1375 

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

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

1378 if callspec: 

1379 self.callspec = callspec 

1380 # this is total hostile and a mess 

1381 # keywords are broken by design by now 

1382 # this will be redeemed later 

1383 for mark in callspec.marks: 

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

1385 # and keywords cant fix it per design 

1386 self.keywords[mark.name] = mark 

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

1388 if keywords: 

1389 self.keywords.update(keywords) 

1390 

1391 # todo: this is a hell of a hack 

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

1393 

1394 self.keywords.update( 

1395 { 

1396 mark.name: True 

1397 for mark in self.iter_markers() 

1398 if mark.name not in self.keywords 

1399 } 

1400 ) 

1401 

1402 if fixtureinfo is None: 

1403 fixtureinfo = self.session._fixturemanager.getfixtureinfo( 

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

1405 ) 

1406 self._fixtureinfo = fixtureinfo 

1407 self.fixturenames = fixtureinfo.names_closure 

1408 self._initrequest() 

1409 

1410 #: original function name, without any decorations (for example 

1411 #: parametrization adds a ``"[...]"`` suffix to function names). 

1412 #: 

1413 #: .. versionadded:: 3.0 

1414 self.originalname = originalname 

1415 

1416 def _initrequest(self): 

1417 self.funcargs = {} 

1418 self._request = fixtures.FixtureRequest(self) 

1419 

1420 @property 

1421 def function(self): 

1422 "underlying python 'function' object" 

1423 return getimfunc(self.obj) 

1424 

1425 def _getobj(self): 

1426 name = self.name 

1427 i = name.find("[") # parametrization 

1428 if i != -1: 

1429 name = name[:i] 

1430 return getattr(self.parent.obj, name) 

1431 

1432 @property 

1433 def _pyfuncitem(self): 

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

1435 return self 

1436 

1437 @property 

1438 def funcargnames(self): 

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

1440 warnings.warn(FUNCARGNAMES, stacklevel=2) 

1441 return self.fixturenames 

1442 

1443 def runtest(self): 

1444 """ execute the underlying test function. """ 

1445 self.ihook.pytest_pyfunc_call(pyfuncitem=self) 

1446 

1447 def setup(self): 

1448 super().setup() 

1449 fixtures.fillfixtures(self) 

1450 

1451 

1452class FunctionDefinition(Function): 

1453 """ 

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

1455 crappy metafunc hack 

1456 """ 

1457 

1458 def runtest(self): 

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

1460 

1461 setup = runtest