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

1import functools 

2import inspect 

3import itertools 

4import sys 

5import warnings 

6from collections import defaultdict 

7from collections import deque 

8from collections import OrderedDict 

9from typing import Dict 

10from typing import List 

11from typing import Tuple 

12 

13import attr 

14import py 

15 

16import _pytest 

17from _pytest._code.code import FormattedExcinfo 

18from _pytest._code.code import TerminalRepr 

19from _pytest.compat import _format_args 

20from _pytest.compat import _PytestWrapper 

21from _pytest.compat import get_real_func 

22from _pytest.compat import get_real_method 

23from _pytest.compat import getfslineno 

24from _pytest.compat import getfuncargnames 

25from _pytest.compat import getimfunc 

26from _pytest.compat import getlocation 

27from _pytest.compat import is_generator 

28from _pytest.compat import NOTSET 

29from _pytest.compat import safe_getattr 

30from _pytest.compat import TYPE_CHECKING 

31from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS 

32from _pytest.deprecated import FUNCARGNAMES 

33from _pytest.outcomes import fail 

34from _pytest.outcomes import TEST_OUTCOME 

35 

36if TYPE_CHECKING: 

37 from typing import Type 

38 

39 from _pytest import nodes 

40 

41 

42@attr.s(frozen=True) 

43class PseudoFixtureDef: 

44 cached_result = attr.ib() 

45 scope = attr.ib() 

46 

47 

48def pytest_sessionstart(session): 

49 import _pytest.python 

50 import _pytest.nodes 

51 

52 scopename2class.update( 

53 { 

54 "package": _pytest.python.Package, 

55 "class": _pytest.python.Class, 

56 "module": _pytest.python.Module, 

57 "function": _pytest.nodes.Item, 

58 "session": _pytest.main.Session, 

59 } 

60 ) 

61 session._fixturemanager = FixtureManager(session) 

62 

63 

64scopename2class = {} # type: Dict[str, Type[nodes.Node]] 

65 

66scope2props = dict(session=()) # type: Dict[str, Tuple[str, ...]] 

67scope2props["package"] = ("fspath",) 

68scope2props["module"] = ("fspath", "module") 

69scope2props["class"] = scope2props["module"] + ("cls",) 

70scope2props["instance"] = scope2props["class"] + ("instance",) 

71scope2props["function"] = scope2props["instance"] + ("function", "keywords") 

72 

73 

74def scopeproperty(name=None, doc=None): 

75 def decoratescope(func): 

76 scopename = name or func.__name__ 

77 

78 def provide(self): 

79 if func.__name__ in scope2props[self.scope]: 

80 return func(self) 

81 raise AttributeError( 

82 "{} not available in {}-scoped context".format(scopename, self.scope) 

83 ) 

84 

85 return property(provide, None, None, func.__doc__) 

86 

87 return decoratescope 

88 

89 

90def get_scope_package(node, fixturedef): 

91 import pytest 

92 

93 cls = pytest.Package 

94 current = node 

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

96 while current and ( 

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

98 ): 

99 current = current.parent 

100 if current is None: 

101 return node.session 

102 return current 

103 

104 

105def get_scope_node(node, scope): 

106 cls = scopename2class.get(scope) 

107 if cls is None: 

108 raise ValueError("unknown scope") 

109 return node.getparent(cls) 

110 

111 

112def add_funcarg_pseudo_fixture_def(collector, metafunc, fixturemanager): 

113 # this function will transform all collected calls to a functions 

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

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

116 # an existing FixtureDef structure for all arguments. 

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

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

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

120 return # this function call does not have direct parametrization 

121 # collect funcargs of all callspecs into a list of values 

122 arg2params = {} 

123 arg2scope = {} 

124 for callspec in metafunc._calls: 

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

126 assert argname not in callspec.params 

127 callspec.params[argname] = argvalue 

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

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

130 arg2params_list.append(argvalue) 

131 if argname not in arg2scope: 

132 scopenum = callspec._arg2scopenum.get(argname, scopenum_function) 

133 arg2scope[argname] = scopes[scopenum] 

134 callspec.funcargs.clear() 

135 

136 # register artificial FixtureDef's so that later at test execution 

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

138 arg2fixturedefs = metafunc._arg2fixturedefs 

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

140 # if we have a scope that is higher than function we need 

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

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

143 # node related to the scope. 

144 scope = arg2scope[argname] 

145 node = None 

146 if scope != "function": 

147 node = get_scope_node(collector, scope) 

148 if node is None: 

149 assert scope == "class" and isinstance(collector, _pytest.python.Module) 

150 # use module-level collector for class-scope (for now) 

151 node = collector 

152 if node and argname in node._name2pseudofixturedef: 

153 arg2fixturedefs[argname] = [node._name2pseudofixturedef[argname]] 

154 else: 

155 fixturedef = FixtureDef( 

156 fixturemanager, 

157 "", 

158 argname, 

159 get_direct_param_fixture_func, 

160 arg2scope[argname], 

161 valuelist, 

162 False, 

163 False, 

164 ) 

165 arg2fixturedefs[argname] = [fixturedef] 

166 if node is not None: 

167 node._name2pseudofixturedef[argname] = fixturedef 

168 

169 

170def getfixturemarker(obj): 

171 """ return fixturemarker or None if it doesn't exist or raised 

172 exceptions.""" 

173 try: 

174 return getattr(obj, "_pytestfixturefunction", None) 

175 except TEST_OUTCOME: 

176 # some objects raise errors like request (from flask import request) 

177 # we don't expect them to be fixture functions 

178 return None 

179 

180 

181def get_parametrized_fixture_keys(item, scopenum): 

182 """ return list of keys for all parametrized arguments which match 

183 the specified scope. """ 

184 assert scopenum < scopenum_function # function 

185 try: 

186 cs = item.callspec 

187 except AttributeError: 

188 pass 

189 else: 

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

191 # sort this so that different calls to 

192 # get_parametrized_fixture_keys will be deterministic. 

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

194 if cs._arg2scopenum[argname] != scopenum: 

195 continue 

196 if scopenum == 0: # session 

197 key = (argname, param_index) 

198 elif scopenum == 1: # package 

199 key = (argname, param_index, item.fspath.dirpath()) 

200 elif scopenum == 2: # module 

201 key = (argname, param_index, item.fspath) 

202 elif scopenum == 3: # class 

203 key = (argname, param_index, item.fspath, item.cls) 

204 yield key 

205 

206 

207# algorithm for sorting on a per-parametrized resource setup basis 

208# it is called for scopenum==0 (session) first and performs sorting 

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

210# setups and teardowns 

211 

212 

213def reorder_items(items): 

214 argkeys_cache = {} 

215 items_by_argkey = {} 

216 for scopenum in range(0, scopenum_function): 

217 argkeys_cache[scopenum] = d = {} 

218 items_by_argkey[scopenum] = item_d = defaultdict(deque) 

219 for item in items: 

220 keys = OrderedDict.fromkeys(get_parametrized_fixture_keys(item, scopenum)) 

221 if keys: 

222 d[item] = keys 

223 for key in keys: 

224 item_d[key].append(item) 

225 items = OrderedDict.fromkeys(items) 

226 return list(reorder_items_atscope(items, argkeys_cache, items_by_argkey, 0)) 

227 

228 

229def fix_cache_order(item, argkeys_cache, items_by_argkey): 

230 for scopenum in range(0, scopenum_function): 

231 for key in argkeys_cache[scopenum].get(item, []): 

232 items_by_argkey[scopenum][key].appendleft(item) 

233 

234 

235def reorder_items_atscope(items, argkeys_cache, items_by_argkey, scopenum): 

236 if scopenum >= scopenum_function or len(items) < 3: 

237 return items 

238 ignore = set() 

239 items_deque = deque(items) 

240 items_done = OrderedDict() 

241 scoped_items_by_argkey = items_by_argkey[scopenum] 

242 scoped_argkeys_cache = argkeys_cache[scopenum] 

243 while items_deque: 

244 no_argkey_group = OrderedDict() 

245 slicing_argkey = None 

246 while items_deque: 

247 item = items_deque.popleft() 

248 if item in items_done or item in no_argkey_group: 

249 continue 

250 argkeys = OrderedDict.fromkeys( 

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

252 ) 

253 if not argkeys: 

254 no_argkey_group[item] = None 

255 else: 

256 slicing_argkey, _ = argkeys.popitem() 

257 # we don't have to remove relevant items from later in the deque because they'll just be ignored 

258 matching_items = [ 

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

260 ] 

261 for i in reversed(matching_items): 

262 fix_cache_order(i, argkeys_cache, items_by_argkey) 

263 items_deque.appendleft(i) 

264 break 

265 if no_argkey_group: 

266 no_argkey_group = reorder_items_atscope( 

267 no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1 

268 ) 

269 for item in no_argkey_group: 

270 items_done[item] = None 

271 ignore.add(slicing_argkey) 

272 return items_done 

273 

274 

275def fillfixtures(function): 

276 """ fill missing funcargs for a test function. """ 

277 try: 

278 request = function._request 

279 except AttributeError: 

280 # XXX this special code path is only expected to execute 

281 # with the oejskit plugin. It uses classes with funcargs 

282 # and we thus have to work a bit to allow this. 

283 fm = function.session._fixturemanager 

284 fi = fm.getfixtureinfo(function.parent, function.obj, None) 

285 function._fixtureinfo = fi 

286 request = function._request = FixtureRequest(function) 

287 request._fillfixtures() 

288 # prune out funcargs for jstests 

289 newfuncargs = {} 

290 for name in fi.argnames: 

291 newfuncargs[name] = function.funcargs[name] 

292 function.funcargs = newfuncargs 

293 else: 

294 request._fillfixtures() 

295 

296 

297def get_direct_param_fixture_func(request): 

298 return request.param 

299 

300 

301@attr.s(slots=True) 

302class FuncFixtureInfo: 

303 # original function argument names 

304 argnames = attr.ib(type=tuple) 

305 # argnames that function immediately requires. These include argnames + 

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

307 # definitions. 

308 initialnames = attr.ib(type=tuple) 

309 names_closure = attr.ib() # List[str] 

310 name2fixturedefs = attr.ib() # List[str, List[FixtureDef]] 

311 

312 def prune_dependency_tree(self): 

313 """Recompute names_closure from initialnames and name2fixturedefs 

314 

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

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

317 

318 This method is needed because direct parametrization may shadow some 

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

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

321 of argnames may get reduced. 

322 """ 

323 closure = set() 

324 working_set = set(self.initialnames) 

325 while working_set: 

326 argname = working_set.pop() 

327 # argname may be smth not included in the original names_closure, 

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

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

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

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

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

333 closure.add(argname) 

334 if argname in self.name2fixturedefs: 

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

336 

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

338 

339 

340class FixtureRequest: 

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

342 

343 A request object gives access to the requesting test context 

344 and has an optional ``param`` attribute in case 

345 the fixture is parametrized indirectly. 

346 """ 

347 

348 def __init__(self, pyfuncitem): 

349 self._pyfuncitem = pyfuncitem 

350 #: fixture for which this request is being performed 

351 self.fixturename = None 

352 #: Scope string, one of "function", "class", "module", "session" 

353 self.scope = "function" 

354 self._fixture_defs = {} # argname -> FixtureDef 

355 fixtureinfo = pyfuncitem._fixtureinfo 

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

357 self._arg2index = {} 

358 self._fixturemanager = pyfuncitem.session._fixturemanager 

359 

360 @property 

361 def fixturenames(self): 

362 """names of all active fixtures in this request""" 

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

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

365 return result 

366 

367 @property 

368 def funcargnames(self): 

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

370 warnings.warn(FUNCARGNAMES, stacklevel=2) 

371 return self.fixturenames 

372 

373 @property 

374 def node(self): 

375 """ underlying collection node (depends on current request scope)""" 

376 return self._getscopeitem(self.scope) 

377 

378 def _getnextfixturedef(self, argname): 

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

380 if fixturedefs is None: 

381 # we arrive here because of a dynamic call to 

382 # getfixturevalue(argname) usage which was naturally 

383 # not known at parsing/collection time 

384 parentid = self._pyfuncitem.parent.nodeid 

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

386 self._arg2fixturedefs[argname] = fixturedefs 

387 # fixturedefs list is immutable so we maintain a decreasing index 

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

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

390 raise FixtureLookupError(argname, self) 

391 self._arg2index[argname] = index 

392 return fixturedefs[index] 

393 

394 @property 

395 def config(self): 

396 """ the pytest config object associated with this request. """ 

397 return self._pyfuncitem.config 

398 

399 @scopeproperty() 

400 def function(self): 

401 """ test function object if the request has a per-function scope. """ 

402 return self._pyfuncitem.obj 

403 

404 @scopeproperty("class") 

405 def cls(self): 

406 """ class (can be None) where the test function was collected. """ 

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

408 if clscol: 

409 return clscol.obj 

410 

411 @property 

412 def instance(self): 

413 """ instance (can be None) on which test function was collected. """ 

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

415 try: 

416 return self._pyfuncitem._testcase 

417 except AttributeError: 

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

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

420 

421 @scopeproperty() 

422 def module(self): 

423 """ python module object where the test function was collected. """ 

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

425 

426 @scopeproperty() 

427 def fspath(self) -> py.path.local: 

428 """ the file system path of the test module which collected this test. """ 

429 return self._pyfuncitem.fspath 

430 

431 @property 

432 def keywords(self): 

433 """ keywords/markers dictionary for the underlying node. """ 

434 return self.node.keywords 

435 

436 @property 

437 def session(self): 

438 """ pytest session object. """ 

439 return self._pyfuncitem.session 

440 

441 def addfinalizer(self, finalizer): 

442 """ add finalizer/teardown function to be called after the 

443 last test within the requesting test context finished 

444 execution. """ 

445 # XXX usually this method is shadowed by fixturedef specific ones 

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

447 

448 def _addfinalizer(self, finalizer, scope): 

449 colitem = self._getscopeitem(scope) 

450 self._pyfuncitem.session._setupstate.addfinalizer( 

451 finalizer=finalizer, colitem=colitem 

452 ) 

453 

454 def applymarker(self, marker): 

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

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

457 on all function invocations. 

458 

459 :arg marker: a :py:class:`_pytest.mark.MarkDecorator` object 

460 created by a call to ``pytest.mark.NAME(...)``. 

461 """ 

462 self.node.add_marker(marker) 

463 

464 def raiseerror(self, msg): 

465 """ raise a FixtureLookupError with the given message. """ 

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

467 

468 def _fillfixtures(self): 

469 item = self._pyfuncitem 

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

471 for argname in fixturenames: 

472 if argname not in item.funcargs: 

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

474 

475 def getfixturevalue(self, argname): 

476 """ Dynamically run a named fixture function. 

477 

478 Declaring fixtures via function argument is recommended where possible. 

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

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

481 or test function body. 

482 """ 

483 return self._get_active_fixturedef(argname).cached_result[0] 

484 

485 def _get_active_fixturedef(self, argname): 

486 try: 

487 return self._fixture_defs[argname] 

488 except KeyError: 

489 try: 

490 fixturedef = self._getnextfixturedef(argname) 

491 except FixtureLookupError: 

492 if argname == "request": 

493 cached_result = (self, [0], None) 

494 scope = "function" 

495 return PseudoFixtureDef(cached_result, scope) 

496 raise 

497 # remove indent to prevent the python3 exception 

498 # from leaking into the call 

499 self._compute_fixture_value(fixturedef) 

500 self._fixture_defs[argname] = fixturedef 

501 return fixturedef 

502 

503 def _get_fixturestack(self): 

504 current = self 

505 values = [] 

506 while 1: 

507 fixturedef = getattr(current, "_fixturedef", None) 

508 if fixturedef is None: 

509 values.reverse() 

510 return values 

511 values.append(fixturedef) 

512 current = current._parent_request 

513 

514 def _compute_fixture_value(self, fixturedef): 

515 """ 

516 Creates a SubRequest based on "self" and calls the execute method of the given fixturedef object. This will 

517 force the FixtureDef object to throw away any previous results and compute a new fixture value, which 

518 will be stored into the FixtureDef object itself. 

519 

520 :param FixtureDef fixturedef: 

521 """ 

522 # prepare a subrequest object before calling fixture function 

523 # (latter managed by fixturedef) 

524 argname = fixturedef.argname 

525 funcitem = self._pyfuncitem 

526 scope = fixturedef.scope 

527 try: 

528 param = funcitem.callspec.getparam(argname) 

529 except (AttributeError, ValueError): 

530 param = NOTSET 

531 param_index = 0 

532 has_params = fixturedef.params is not None 

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

534 if has_params and fixtures_not_supported: 

535 msg = ( 

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

537 "Node id: {nodeid}\n" 

538 "Function type: {typename}" 

539 ).format( 

540 name=funcitem.name, 

541 nodeid=funcitem.nodeid, 

542 typename=type(funcitem).__name__, 

543 ) 

544 fail(msg, pytrace=False) 

545 if has_params: 

546 frame = inspect.stack()[3] 

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

548 source_path = frameinfo.filename 

549 source_lineno = frameinfo.lineno 

550 source_path = py.path.local(source_path) 

551 if source_path.relto(funcitem.config.rootdir): 

552 source_path = source_path.relto(funcitem.config.rootdir) 

553 msg = ( 

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

555 " {}\n\n" 

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

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

558 funcitem.nodeid, 

559 fixturedef.argname, 

560 getlocation(fixturedef.func, funcitem.config.rootdir), 

561 source_path, 

562 source_lineno, 

563 ) 

564 ) 

565 fail(msg, pytrace=False) 

566 else: 

567 param_index = funcitem.callspec.indices[argname] 

568 # if a parametrize invocation set a scope it will override 

569 # the static scope defined with the fixture function 

570 paramscopenum = funcitem.callspec._arg2scopenum.get(argname) 

571 if paramscopenum is not None: 

572 scope = scopes[paramscopenum] 

573 

574 subrequest = SubRequest(self, scope, param, param_index, fixturedef) 

575 

576 # check if a higher-level scoped fixture accesses a lower level one 

577 subrequest._check_scope(argname, self.scope, scope) 

578 try: 

579 # call the fixture function 

580 fixturedef.execute(request=subrequest) 

581 finally: 

582 self._schedule_finalizers(fixturedef, subrequest) 

583 

584 def _schedule_finalizers(self, fixturedef, subrequest): 

585 # if fixture function failed it might have registered finalizers 

586 self.session._setupstate.addfinalizer( 

587 functools.partial(fixturedef.finish, request=subrequest), subrequest.node 

588 ) 

589 

590 def _check_scope(self, argname, invoking_scope, requested_scope): 

591 if argname == "request": 

592 return 

593 if scopemismatch(invoking_scope, requested_scope): 

594 # try to report something helpful 

595 lines = self._factorytraceback() 

596 fail( 

597 "ScopeMismatch: You tried to access the %r scoped " 

598 "fixture %r with a %r scoped request object, " 

599 "involved factories\n%s" 

600 % ((requested_scope, argname, invoking_scope, "\n".join(lines))), 

601 pytrace=False, 

602 ) 

603 

604 def _factorytraceback(self): 

605 lines = [] 

606 for fixturedef in self._get_fixturestack(): 

607 factory = fixturedef.func 

608 fs, lineno = getfslineno(factory) 

609 p = self._pyfuncitem.session.fspath.bestrelpath(fs) 

610 args = _format_args(factory) 

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

612 return lines 

613 

614 def _getscopeitem(self, scope): 

615 if scope == "function": 

616 # this might also be a non-function Item despite its attribute name 

617 return self._pyfuncitem 

618 if scope == "package": 

619 node = get_scope_package(self._pyfuncitem, self._fixturedef) 

620 else: 

621 node = get_scope_node(self._pyfuncitem, scope) 

622 if node is None and scope == "class": 

623 # fallback to function item itself 

624 node = self._pyfuncitem 

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

626 scope, self._pyfuncitem 

627 ) 

628 return node 

629 

630 def __repr__(self): 

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

632 

633 

634class SubRequest(FixtureRequest): 

635 """ a sub request for handling getting a fixture from a 

636 test function/fixture. """ 

637 

638 def __init__(self, request, scope, param, param_index, fixturedef): 

639 self._parent_request = request 

640 self.fixturename = fixturedef.argname 

641 if param is not NOTSET: 

642 self.param = param 

643 self.param_index = param_index 

644 self.scope = scope 

645 self._fixturedef = fixturedef 

646 self._pyfuncitem = request._pyfuncitem 

647 self._fixture_defs = request._fixture_defs 

648 self._arg2fixturedefs = request._arg2fixturedefs 

649 self._arg2index = request._arg2index 

650 self._fixturemanager = request._fixturemanager 

651 

652 def __repr__(self): 

653 return "<SubRequest {!r} for {!r}>".format(self.fixturename, self._pyfuncitem) 

654 

655 def addfinalizer(self, finalizer): 

656 self._fixturedef.addfinalizer(finalizer) 

657 

658 def _schedule_finalizers(self, fixturedef, subrequest): 

659 # if the executing fixturedef was not explicitly requested in the argument list (via 

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

661 # first 

662 if fixturedef.argname not in self.fixturenames: 

663 fixturedef.addfinalizer( 

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

665 ) 

666 super()._schedule_finalizers(fixturedef, subrequest) 

667 

668 

669scopes = "session package module class function".split() 

670scopenum_function = scopes.index("function") 

671 

672 

673def scopemismatch(currentscope, newscope): 

674 return scopes.index(newscope) > scopes.index(currentscope) 

675 

676 

677def scope2index(scope, descr, where=None): 

678 """Look up the index of ``scope`` and raise a descriptive value error 

679 if not defined. 

680 """ 

681 try: 

682 return scopes.index(scope) 

683 except ValueError: 

684 fail( 

685 "{} {}got an unexpected scope value '{}'".format( 

686 descr, "from {} ".format(where) if where else "", scope 

687 ), 

688 pytrace=False, 

689 ) 

690 

691 

692class FixtureLookupError(LookupError): 

693 """ could not return a requested Fixture (missing or invalid). """ 

694 

695 def __init__(self, argname, request, msg=None): 

696 self.argname = argname 

697 self.request = request 

698 self.fixturestack = request._get_fixturestack() 

699 self.msg = msg 

700 

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

702 tblines = [] # type: List[str] 

703 addline = tblines.append 

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

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

706 msg = self.msg 

707 if msg is not None: 

708 # the last fixture raise an error, let's present 

709 # it at the requesting side 

710 stack = stack[:-1] 

711 for function in stack: 

712 fspath, lineno = getfslineno(function) 

713 try: 

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

715 except (IOError, IndexError, TypeError): 

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

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

718 else: 

719 addline("file {}, line {}".format(fspath, lineno + 1)) 

720 for i, line in enumerate(lines): 

721 line = line.rstrip() 

722 addline(" " + line) 

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

724 break 

725 

726 if msg is None: 

727 fm = self.request._fixturemanager 

728 available = set() 

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

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

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

732 if faclist: 

733 available.add(name) 

734 if self.argname in available: 

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

736 self.argname 

737 ) 

738 else: 

739 msg = "fixture '{}' not found".format(self.argname) 

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

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

742 

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

744 

745 

746class FixtureLookupErrorRepr(TerminalRepr): 

747 def __init__(self, filename, firstlineno, tblines, errorstring, argname): 

748 self.tblines = tblines 

749 self.errorstring = errorstring 

750 self.filename = filename 

751 self.firstlineno = firstlineno 

752 self.argname = argname 

753 

754 def toterminal(self, tw: py.io.TerminalWriter) -> None: 

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

756 for tbline in self.tblines: 

757 tw.line(tbline.rstrip()) 

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

759 if lines: 

760 tw.line( 

761 "{} {}".format(FormattedExcinfo.fail_marker, lines[0].strip()), 

762 red=True, 

763 ) 

764 for line in lines[1:]: 

765 tw.line( 

766 "{} {}".format(FormattedExcinfo.flow_marker, line.strip()), 

767 red=True, 

768 ) 

769 tw.line() 

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

771 

772 

773def fail_fixturefunc(fixturefunc, msg): 

774 fs, lineno = getfslineno(fixturefunc) 

775 location = "{}:{}".format(fs, lineno + 1) 

776 source = _pytest._code.Source(fixturefunc) 

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

778 

779 

780def call_fixture_func(fixturefunc, request, kwargs): 

781 yieldctx = is_generator(fixturefunc) 

782 if yieldctx: 

783 it = fixturefunc(**kwargs) 

784 res = next(it) 

785 finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, it) 

786 request.addfinalizer(finalizer) 

787 else: 

788 res = fixturefunc(**kwargs) 

789 return res 

790 

791 

792def _teardown_yield_fixture(fixturefunc, it): 

793 """Executes the teardown of a fixture function by advancing the iterator after the 

794 yield and ensure the iteration ends (if not it means there is more than one yield in the function)""" 

795 try: 

796 next(it) 

797 except StopIteration: 

798 pass 

799 else: 

800 fail_fixturefunc( 

801 fixturefunc, "yield_fixture function has more than one 'yield'" 

802 ) 

803 

804 

805def _eval_scope_callable(scope_callable, fixture_name, config): 

806 try: 

807 result = scope_callable(fixture_name=fixture_name, config=config) 

808 except Exception: 

809 raise TypeError( 

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

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

812 scope_callable, fixture_name 

813 ) 

814 ) 

815 if not isinstance(result, str): 

816 fail( 

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

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

819 pytrace=False, 

820 ) 

821 return result 

822 

823 

824class FixtureDef: 

825 """ A container for a factory definition. """ 

826 

827 def __init__( 

828 self, 

829 fixturemanager, 

830 baseid, 

831 argname, 

832 func, 

833 scope, 

834 params, 

835 unittest=False, 

836 ids=None, 

837 ): 

838 self._fixturemanager = fixturemanager 

839 self.baseid = baseid or "" 

840 self.has_location = baseid is not None 

841 self.func = func 

842 self.argname = argname 

843 if callable(scope): 

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

845 self.scope = scope 

846 self.scopenum = scope2index( 

847 scope or "function", 

848 descr="Fixture '{}'".format(func.__name__), 

849 where=baseid, 

850 ) 

851 self.params = params 

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

853 self.unittest = unittest 

854 self.ids = ids 

855 self._finalizers = [] 

856 

857 def addfinalizer(self, finalizer): 

858 self._finalizers.append(finalizer) 

859 

860 def finish(self, request): 

861 exceptions = [] 

862 try: 

863 while self._finalizers: 

864 try: 

865 func = self._finalizers.pop() 

866 func() 

867 except: # noqa 

868 exceptions.append(sys.exc_info()) 

869 if exceptions: 

870 _, val, tb = exceptions[0] 

871 # Ensure to not keep frame references through traceback. 

872 del exceptions 

873 raise val.with_traceback(tb) 

874 finally: 

875 hook = self._fixturemanager.session.gethookproxy(request.node.fspath) 

876 hook.pytest_fixture_post_finalizer(fixturedef=self, request=request) 

877 # even if finalization fails, we invalidate 

878 # the cached fixture value and remove 

879 # all finalizers because they may be bound methods which will 

880 # keep instances alive 

881 if hasattr(self, "cached_result"): 

882 del self.cached_result 

883 self._finalizers = [] 

884 

885 def execute(self, request): 

886 # get required arguments and register our own finish() 

887 # with their finalization 

888 for argname in self.argnames: 

889 fixturedef = request._get_active_fixturedef(argname) 

890 if argname != "request": 

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

892 

893 my_cache_key = self.cache_key(request) 

894 cached_result = getattr(self, "cached_result", None) 

895 if cached_result is not None: 

896 result, cache_key, err = cached_result 

897 if my_cache_key == cache_key: 

898 if err is not None: 

899 _, val, tb = err 

900 raise val.with_traceback(tb) 

901 else: 

902 return result 

903 # we have a previous but differently parametrized fixture instance 

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

905 self.finish(request) 

906 assert not hasattr(self, "cached_result") 

907 

908 hook = self._fixturemanager.session.gethookproxy(request.node.fspath) 

909 return hook.pytest_fixture_setup(fixturedef=self, request=request) 

910 

911 def cache_key(self, request): 

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

913 

914 def __repr__(self): 

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

916 self.argname, self.scope, self.baseid 

917 ) 

918 

919 

920def resolve_fixture_function(fixturedef, request): 

921 """Gets the actual callable that can be called to obtain the fixture value, dealing with unittest-specific 

922 instances and bound methods. 

923 """ 

924 fixturefunc = fixturedef.func 

925 if fixturedef.unittest: 

926 if request.instance is not None: 

927 # bind the unbound method to the TestCase instance 

928 fixturefunc = fixturedef.func.__get__(request.instance) 

929 else: 

930 # the fixture function needs to be bound to the actual 

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

932 # as expected. 

933 if request.instance is not None: 

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

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

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

937 request.instance, fixturefunc.__self__.__class__ 

938 ): 

939 return fixturefunc 

940 fixturefunc = getimfunc(fixturedef.func) 

941 if fixturefunc != fixturedef.func: 

942 fixturefunc = fixturefunc.__get__(request.instance) 

943 return fixturefunc 

944 

945 

946def pytest_fixture_setup(fixturedef, request): 

947 """ Execution of fixture setup. """ 

948 kwargs = {} 

949 for argname in fixturedef.argnames: 

950 fixdef = request._get_active_fixturedef(argname) 

951 result, arg_cache_key, exc = fixdef.cached_result 

952 request._check_scope(argname, request.scope, fixdef.scope) 

953 kwargs[argname] = result 

954 

955 fixturefunc = resolve_fixture_function(fixturedef, request) 

956 my_cache_key = fixturedef.cache_key(request) 

957 try: 

958 result = call_fixture_func(fixturefunc, request, kwargs) 

959 except TEST_OUTCOME: 

960 fixturedef.cached_result = (None, my_cache_key, sys.exc_info()) 

961 raise 

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

963 return result 

964 

965 

966def _ensure_immutable_ids(ids): 

967 if ids is None: 

968 return 

969 if callable(ids): 

970 return ids 

971 return tuple(ids) 

972 

973 

974def wrap_function_to_error_out_if_called_directly(function, fixture_marker): 

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

976 instead of used as an argument in a test function. 

977 """ 

978 message = ( 

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

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

981 "See https://docs.pytest.org/en/latest/fixture.html for more information about fixtures, and\n" 

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

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

984 

985 @functools.wraps(function) 

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

987 fail(message, pytrace=False) 

988 

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

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

991 result.__pytest_wrapped__ = _PytestWrapper(function) 

992 

993 return result 

994 

995 

996@attr.s(frozen=True) 

997class FixtureFunctionMarker: 

998 scope = attr.ib() 

999 params = attr.ib(converter=attr.converters.optional(tuple)) 

1000 autouse = attr.ib(default=False) 

1001 # Ignore type because of https://github.com/python/mypy/issues/6172. 

1002 ids = attr.ib(default=None, converter=_ensure_immutable_ids) # type: ignore 

1003 name = attr.ib(default=None) 

1004 

1005 def __call__(self, function): 

1006 if inspect.isclass(function): 

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

1008 

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

1010 raise ValueError( 

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

1012 ) 

1013 

1014 function = wrap_function_to_error_out_if_called_directly(function, self) 

1015 

1016 name = self.name or function.__name__ 

1017 if name == "request": 

1018 location = getlocation(function) 

1019 fail( 

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

1021 location 

1022 ), 

1023 pytrace=False, 

1024 ) 

1025 function._pytestfixturefunction = self 

1026 return function 

1027 

1028 

1029FIXTURE_ARGS_ORDER = ("scope", "params", "autouse", "ids", "name") 

1030 

1031 

1032def _parse_fixture_args(callable_or_scope, *args, **kwargs): 

1033 arguments = { 

1034 "scope": "function", 

1035 "params": None, 

1036 "autouse": False, 

1037 "ids": None, 

1038 "name": None, 

1039 } 

1040 kwargs = { 

1041 key: value for key, value in kwargs.items() if arguments.get(key) != value 

1042 } 

1043 

1044 fixture_function = None 

1045 if isinstance(callable_or_scope, str): 

1046 args = list(args) 

1047 args.insert(0, callable_or_scope) 

1048 else: 

1049 fixture_function = callable_or_scope 

1050 

1051 positionals = set() 

1052 for positional, argument_name in zip(args, FIXTURE_ARGS_ORDER): 

1053 arguments[argument_name] = positional 

1054 positionals.add(argument_name) 

1055 

1056 duplicated_kwargs = {kwarg for kwarg in kwargs.keys() if kwarg in positionals} 

1057 if duplicated_kwargs: 

1058 raise TypeError( 

1059 "The fixture arguments are defined as positional and keyword: {}. " 

1060 "Use only keyword arguments.".format(", ".join(duplicated_kwargs)) 

1061 ) 

1062 

1063 if positionals: 

1064 warnings.warn(FIXTURE_POSITIONAL_ARGUMENTS, stacklevel=2) 

1065 

1066 arguments.update(kwargs) 

1067 

1068 return fixture_function, arguments 

1069 

1070 

1071def fixture( 

1072 callable_or_scope=None, 

1073 *args, 

1074 scope="function", 

1075 params=None, 

1076 autouse=False, 

1077 ids=None, 

1078 name=None 

1079): 

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

1081 

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

1083 fixture function. 

1084 

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

1086 invocation ahead of running tests: test 

1087 modules or classes can use the ``pytest.mark.usefixtures(fixturename)`` 

1088 marker. 

1089 

1090 Test functions can directly use fixture names as input 

1091 arguments in which case the fixture instance returned from the fixture 

1092 function will be injected. 

1093 

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

1095 statements. When using ``yield`` the code block after the ``yield`` statement is executed 

1096 as teardown code regardless of the test outcome, and must yield exactly once. 

1097 

1098 :arg scope: the scope for which this fixture is shared, one of 

1099 ``"function"`` (default), ``"class"``, ``"module"``, 

1100 ``"package"`` or ``"session"`` (``"package"`` is considered **experimental** 

1101 at this time). 

1102 

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

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

1105 

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

1107 

1108 :arg params: an optional list of parameters which will cause multiple 

1109 invocations of the fixture function and all of the tests 

1110 using it. 

1111 The current parameter is available in ``request.param``. 

1112 

1113 :arg autouse: if True, the fixture func is activated for all tests that 

1114 can see it. If False (the default) then an explicit 

1115 reference is needed to activate the fixture. 

1116 

1117 :arg ids: list of string ids each corresponding to the params 

1118 so that they are part of the test id. If no ids are provided 

1119 they will be generated automatically from the params. 

1120 

1121 :arg name: the name of the fixture. This defaults to the name of the 

1122 decorated function. If a fixture is used in the same module in 

1123 which it is defined, the function name of the fixture will be 

1124 shadowed by the function arg that requests the fixture; one way 

1125 to resolve this is to name the decorated function 

1126 ``fixture_<fixturename>`` and then use 

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

1128 """ 

1129 if params is not None: 

1130 params = list(params) 

1131 

1132 fixture_function, arguments = _parse_fixture_args( 

1133 callable_or_scope, 

1134 *args, 

1135 scope=scope, 

1136 params=params, 

1137 autouse=autouse, 

1138 ids=ids, 

1139 name=name 

1140 ) 

1141 scope = arguments.get("scope") 

1142 params = arguments.get("params") 

1143 autouse = arguments.get("autouse") 

1144 ids = arguments.get("ids") 

1145 name = arguments.get("name") 

1146 

1147 if fixture_function and params is None and autouse is False: 

1148 # direct decoration 

1149 return FixtureFunctionMarker(scope, params, autouse, name=name)( 

1150 fixture_function 

1151 ) 

1152 

1153 return FixtureFunctionMarker(scope, params, autouse, ids=ids, name=name) 

1154 

1155 

1156def yield_fixture( 

1157 callable_or_scope=None, 

1158 *args, 

1159 scope="function", 

1160 params=None, 

1161 autouse=False, 

1162 ids=None, 

1163 name=None 

1164): 

1165 """ (return a) decorator to mark a yield-fixture factory function. 

1166 

1167 .. deprecated:: 3.0 

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

1169 """ 

1170 return fixture( 

1171 callable_or_scope, 

1172 *args, 

1173 scope=scope, 

1174 params=params, 

1175 autouse=autouse, 

1176 ids=ids, 

1177 name=name 

1178 ) 

1179 

1180 

1181defaultfuncargprefixmarker = fixture() 

1182 

1183 

1184@fixture(scope="session") 

1185def pytestconfig(request): 

1186 """Session-scoped fixture that returns the :class:`_pytest.config.Config` object. 

1187 

1188 Example:: 

1189 

1190 def test_foo(pytestconfig): 

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

1192 ... 

1193 

1194 """ 

1195 return request.config 

1196 

1197 

1198def pytest_addoption(parser): 

1199 parser.addini( 

1200 "usefixtures", 

1201 type="args", 

1202 default=[], 

1203 help="list of default fixtures to be used with this project", 

1204 ) 

1205 

1206 

1207class FixtureManager: 

1208 """ 

1209 pytest fixtures definitions and information is stored and managed 

1210 from this class. 

1211 

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

1213 fixture function definitions into FixtureDef objects and internal 

1214 data structures. 

1215 

1216 During collection of test functions, metafunc-mechanics instantiate 

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

1218 This FuncFixtureInfo object is later retrieved by Function nodes 

1219 which themselves offer a fixturenames attribute. 

1220 

1221 The FuncFixtureInfo object holds information about fixtures and FixtureDefs 

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

1223 assembled like this: 

1224 

1225 - ini-defined usefixtures 

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

1227 - usefixtures markers at module/class/function level 

1228 - test function funcargs 

1229 

1230 Subsequently the funcfixtureinfo.fixturenames attribute is computed 

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

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

1233 to the fixturenames list. 

1234 

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

1236 by a lookup of their FuncFixtureInfo. 

1237 """ 

1238 

1239 FixtureLookupError = FixtureLookupError 

1240 FixtureLookupErrorRepr = FixtureLookupErrorRepr 

1241 

1242 def __init__(self, session): 

1243 self.session = session 

1244 self.config = session.config 

1245 self._arg2fixturedefs = {} 

1246 self._holderobjseen = set() 

1247 self._arg2finish = {} 

1248 self._nodeid_and_autousenames = [("", self.config.getini("usefixtures"))] 

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

1250 

1251 def _get_direct_parametrize_args(self, node): 

1252 """This function returns all the direct parametrization 

1253 arguments of a node, so we don't mistake them for fixtures 

1254 

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

1256 

1257 This things are done later as well when dealing with parametrization 

1258 so this could be improved 

1259 """ 

1260 from _pytest.mark import ParameterSet 

1261 

1262 parametrize_argnames = [] 

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

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

1265 p_argnames, _ = ParameterSet._parse_parametrize_args( 

1266 *marker.args, **marker.kwargs 

1267 ) 

1268 parametrize_argnames.extend(p_argnames) 

1269 

1270 return parametrize_argnames 

1271 

1272 def getfixtureinfo(self, node, func, cls, funcargs=True): 

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

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

1275 else: 

1276 argnames = () 

1277 

1278 usefixtures = itertools.chain.from_iterable( 

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

1280 ) 

1281 initialnames = tuple(usefixtures) + argnames 

1282 fm = node.session._fixturemanager 

1283 initialnames, names_closure, arg2fixturedefs = fm.getfixtureclosure( 

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

1285 ) 

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

1287 

1288 def pytest_plugin_registered(self, plugin): 

1289 nodeid = None 

1290 try: 

1291 p = py.path.local(plugin.__file__).realpath() 

1292 except AttributeError: 

1293 pass 

1294 else: 

1295 from _pytest import nodes 

1296 

1297 # construct the base nodeid which is later used to check 

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

1299 # by their test id) 

1300 if p.basename.startswith("conftest.py"): 

1301 nodeid = p.dirpath().relto(self.config.rootdir) 

1302 if p.sep != nodes.SEP: 

1303 nodeid = nodeid.replace(p.sep, nodes.SEP) 

1304 

1305 self.parsefactories(plugin, nodeid) 

1306 

1307 def _getautousenames(self, nodeid): 

1308 """ return a tuple of fixture names to be used. """ 

1309 autousenames = [] 

1310 for baseid, basenames in self._nodeid_and_autousenames: 

1311 if nodeid.startswith(baseid): 

1312 if baseid: 

1313 i = len(baseid) 

1314 nextchar = nodeid[i : i + 1] 

1315 if nextchar and nextchar not in ":/": 

1316 continue 

1317 autousenames.extend(basenames) 

1318 return autousenames 

1319 

1320 def getfixtureclosure(self, fixturenames, parentnode, ignore_args=()): 

1321 # collect the closure of all fixtures , starting with the given 

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

1323 # factory definitions anyway, we also return an arg2fixturedefs 

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

1325 # to re-discover fixturedefs again for each fixturename 

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

1327 

1328 parentid = parentnode.nodeid 

1329 fixturenames_closure = self._getautousenames(parentid) 

1330 

1331 def merge(otherlist): 

1332 for arg in otherlist: 

1333 if arg not in fixturenames_closure: 

1334 fixturenames_closure.append(arg) 

1335 

1336 merge(fixturenames) 

1337 

1338 # at this point, fixturenames_closure contains what we call "initialnames", 

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

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

1341 initialnames = tuple(fixturenames_closure) 

1342 

1343 arg2fixturedefs = {} 

1344 lastlen = -1 

1345 while lastlen != len(fixturenames_closure): 

1346 lastlen = len(fixturenames_closure) 

1347 for argname in fixturenames_closure: 

1348 if argname in ignore_args: 

1349 continue 

1350 if argname in arg2fixturedefs: 

1351 continue 

1352 fixturedefs = self.getfixturedefs(argname, parentid) 

1353 if fixturedefs: 

1354 arg2fixturedefs[argname] = fixturedefs 

1355 merge(fixturedefs[-1].argnames) 

1356 

1357 def sort_by_scope(arg_name): 

1358 try: 

1359 fixturedefs = arg2fixturedefs[arg_name] 

1360 except KeyError: 

1361 return scopes.index("function") 

1362 else: 

1363 return fixturedefs[-1].scopenum 

1364 

1365 fixturenames_closure.sort(key=sort_by_scope) 

1366 return initialnames, fixturenames_closure, arg2fixturedefs 

1367 

1368 def pytest_generate_tests(self, metafunc): 

1369 for argname in metafunc.fixturenames: 

1370 faclist = metafunc._arg2fixturedefs.get(argname) 

1371 if faclist: 

1372 fixturedef = faclist[-1] 

1373 if fixturedef.params is not None: 

1374 markers = list(metafunc.definition.iter_markers("parametrize")) 

1375 for parametrize_mark in markers: 

1376 if "argnames" in parametrize_mark.kwargs: 

1377 argnames = parametrize_mark.kwargs["argnames"] 

1378 else: 

1379 argnames = parametrize_mark.args[0] 

1380 

1381 if not isinstance(argnames, (tuple, list)): 

1382 argnames = [ 

1383 x.strip() for x in argnames.split(",") if x.strip() 

1384 ] 

1385 if argname in argnames: 

1386 break 

1387 else: 

1388 metafunc.parametrize( 

1389 argname, 

1390 fixturedef.params, 

1391 indirect=True, 

1392 scope=fixturedef.scope, 

1393 ids=fixturedef.ids, 

1394 ) 

1395 else: 

1396 continue # will raise FixtureLookupError at setup time 

1397 

1398 def pytest_collection_modifyitems(self, items): 

1399 # separate parametrized setups 

1400 items[:] = reorder_items(items) 

1401 

1402 def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False): 

1403 if nodeid is not NOTSET: 

1404 holderobj = node_or_obj 

1405 else: 

1406 holderobj = node_or_obj.obj 

1407 nodeid = node_or_obj.nodeid 

1408 if holderobj in self._holderobjseen: 

1409 return 

1410 

1411 self._holderobjseen.add(holderobj) 

1412 autousenames = [] 

1413 for name in dir(holderobj): 

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

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

1416 obj = safe_getattr(holderobj, name, None) 

1417 marker = getfixturemarker(obj) 

1418 if not isinstance(marker, FixtureFunctionMarker): 

1419 # magic globals with __getattr__ might have got us a wrong 

1420 # fixture attribute 

1421 continue 

1422 

1423 if marker.name: 

1424 name = marker.name 

1425 

1426 # during fixture definition we wrap the original fixture function 

1427 # to issue a warning if called directly, so here we unwrap it in order to not emit the warning 

1428 # when pytest itself calls the fixture function 

1429 obj = get_real_method(obj, holderobj) 

1430 

1431 fixture_def = FixtureDef( 

1432 self, 

1433 nodeid, 

1434 name, 

1435 obj, 

1436 marker.scope, 

1437 marker.params, 

1438 unittest=unittest, 

1439 ids=marker.ids, 

1440 ) 

1441 

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

1443 if fixture_def.has_location: 

1444 faclist.append(fixture_def) 

1445 else: 

1446 # fixturedefs with no location are at the front 

1447 # so this inserts the current fixturedef after the 

1448 # existing fixturedefs from external plugins but 

1449 # before the fixturedefs provided in conftests. 

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

1451 faclist.insert(i, fixture_def) 

1452 if marker.autouse: 

1453 autousenames.append(name) 

1454 

1455 if autousenames: 

1456 self._nodeid_and_autousenames.append((nodeid or "", autousenames)) 

1457 

1458 def getfixturedefs(self, argname, nodeid): 

1459 """ 

1460 Gets a list of fixtures which are applicable to the given node id. 

1461 

1462 :param str argname: name of the fixture to search for 

1463 :param str nodeid: full node id of the requesting test. 

1464 :return: list[FixtureDef] 

1465 """ 

1466 try: 

1467 fixturedefs = self._arg2fixturedefs[argname] 

1468 except KeyError: 

1469 return None 

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

1471 

1472 def _matchfactories(self, fixturedefs, nodeid): 

1473 from _pytest import nodes 

1474 

1475 for fixturedef in fixturedefs: 

1476 if nodes.ischildnode(fixturedef.baseid, nodeid): 

1477 yield fixturedef