Coverage for src/pytest_cases/fixture_core2.py: 81%

198 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-09-26 21:52 +0000

1# Authors: Sylvain MARIE <sylvain.marie@se.com> 

2# + All contributors to <https://github.com/smarie/python-pytest-cases> 

3# 

4# License: 3-clause BSD, <https://github.com/smarie/python-pytest-cases/blob/master/LICENSE> 

5from __future__ import division 

6 

7from inspect import isgeneratorfunction 

8from itertools import product 

9from warnings import warn 

10 

11from decopatch import function_decorator, DECORATED 

12from makefun import with_signature, add_signature_parameters, remove_signature_parameters, wraps 

13 

14import pytest 

15import sys 

16 

17try: # python 3.3+ 

18 from inspect import signature, Parameter 

19except ImportError: 

20 from funcsigs import signature, Parameter # noqa 

21 

22try: # native coroutines, python 3.5+ 

23 from inspect import iscoroutinefunction 

24except ImportError: 

25 def iscoroutinefunction(obj): 

26 return False 

27 

28try: # native async generators, python 3.6+ 

29 from inspect import isasyncgenfunction 

30except ImportError: 

31 def isasyncgenfunction(obj): 

32 return False 

33 

34try: # type hints, python 3+ 

35 from typing import Callable, Union, Any, List, Iterable, Sequence # noqa 

36 from types import ModuleType # noqa 

37except ImportError: 

38 pass 

39 

40from .common_pytest_lazy_values import get_lazy_args 

41from .common_pytest import get_pytest_parametrize_marks, make_marked_parameter_value, get_param_argnames_as_list, \ 

42 combine_ids, is_marked_parameter_value, pytest_fixture, resolve_ids, extract_parameterset_info, make_test_ids 

43from .common_pytest_marks import PYTEST3_OR_GREATER, PYTEST8_OR_GREATER 

44from .fixture__creation import get_caller_module, check_name_available, WARN, CHANGE 

45from .fixture_core1_unions import ignore_unused, is_used_request, NOT_USED, _make_unpack_fixture 

46 

47 

48def param_fixture(argname, # type: str 

49 argvalues, # type: Iterable[Any] 

50 autouse=False, # type: bool 

51 ids=None, # type: Union[Callable, Iterable[str]] 

52 scope="function", # type: str 

53 hook=None, # type: Callable[[Callable], Callable] 

54 debug=False, # type: bool 

55 **kwargs): 

56 """ 

57 Identical to `param_fixtures` but for a single parameter name, so that you can assign its output to a single 

58 variable. 

59 

60 ```python 

61 import pytest 

62 from pytest_cases import param_fixtures, param_fixture 

63 

64 # create a single parameter fixture 

65 my_parameter = param_fixture("my_parameter", [1, 2, 3, 4]) 

66 

67 @pytest.fixture 

68 def fixture_uses_param(my_parameter): 

69 ... 

70 

71 def test_uses_param(my_parameter, fixture_uses_param): 

72 ... 

73 ``` 

74 

75 :param argname: see fixture `name` 

76 :param argvalues: see fixture `params` 

77 :param autouse: see fixture `autouse` 

78 :param ids: see fixture `ids` 

79 :param scope: see fixture `scope` 

80 :param hook: an optional hook to apply to each fixture function that is created during this call. The hook function 

81 will be called every time a fixture is about to be created. It will receive a single argument (the function 

82 implementing the fixture) and should return the function to use. For example you can use `saved_fixture` from 

83 `pytest-harvest` as a hook in order to save all such created fixtures in the fixture store. 

84 :param debug: print debug messages on stdout to analyze fixture creation (use pytest -s to see them) 

85 :param kwargs: any other argument for 'fixture' 

86 :return: the create fixture 

87 """ 

88 if "," in argname: 88 ↛ 89line 88 didn't jump to line 89, because the condition on line 88 was never true

89 raise ValueError("`param_fixture` is an alias for `param_fixtures` that can only be used for a single " 

90 "parameter name. Use `param_fixtures` instead - but note that it creates several fixtures.") 

91 elif len(argname.replace(' ', '')) == 0: 91 ↛ 92line 91 didn't jump to line 92, because the condition on line 91 was never true

92 raise ValueError("empty argname") 

93 

94 # todo what if this is called in a class ? 

95 caller_module = get_caller_module() 

96 

97 return _create_param_fixture(caller_module, argname, argvalues, autouse=autouse, ids=ids, scope=scope, 

98 hook=hook, debug=debug, **kwargs) 

99 

100 

101def _create_param_fixture(fixtures_dest, 

102 argname, # type: str 

103 argvalues, # type: Sequence[Any] 

104 autouse=False, # type: bool 

105 ids=None, # type: Union[Callable, Iterable[str]] 

106 scope="function", # type: str 

107 hook=None, # type: Callable[[Callable], Callable] 

108 auto_simplify=False, 

109 debug=False, 

110 **kwargs): 

111 """ Internal method shared with param_fixture and param_fixtures """ 

112 

113 if auto_simplify and len(argvalues) == 1: 

114 # Simplification: do not parametrize the fixture, it will directly return the single value 

115 argvalue_to_return = argvalues[0] 

116 if is_marked_parameter_value(argvalue_to_return): 116 ↛ 119line 116 didn't jump to line 119, because the condition on line 116 was never true

117 # Warning in that case the argvalues should not be a pytest.param. 

118 # argvalue_to_return = get_marked_parameter_values(argvalue_to_return) 

119 raise ValueError("When auto_simplify=True the argvalue can not be a pytest.param") 

120 

121 # create the fixture - set its name so that the optional hook can read it easily 

122 @with_signature("%s(request)" % argname) 

123 def __param_fixture(request): 

124 # do not forget to resolve the lazy values ! 

125 return get_lazy_args(argvalue_to_return, request) 

126 

127 if debug: 

128 print("Creating unparametrized fixture %r returning %r" % (argname, argvalue_to_return)) 

129 

130 fix = fixture(name=argname, scope=scope, autouse=autouse, ids=ids, hook=hook, **kwargs)(__param_fixture) 

131 else: 

132 # create the fixture - set its name so that the optional hook can read it easily 

133 @with_signature("%s(request)" % argname) 

134 def __param_fixture(request): 

135 return get_lazy_args(request.param, request) 

136 

137 if debug: 

138 print("Creating parametrized fixture %r returning %r" % (argname, argvalues)) 

139 

140 fix = fixture(name=argname, scope=scope, autouse=autouse, params=argvalues, ids=ids, 

141 hook=hook, **kwargs)(__param_fixture) 

142 

143 # Dynamically add fixture to caller's module as explained in https://github.com/pytest-dev/pytest/issues/2424 

144 check_name_available(fixtures_dest, argname, if_name_exists=WARN, caller=param_fixture) 

145 setattr(fixtures_dest, argname, fix) 

146 

147 return fix 

148 

149 

150def param_fixtures(argnames, # type: str 

151 argvalues, # type: Iterable[Any] 

152 autouse=False, # type: bool 

153 ids=None, # type: Union[Callable, Iterable[str]] 

154 scope="function", # type: str 

155 hook=None, # type: Callable[[Callable], Callable] 

156 debug=False, # type: bool 

157 **kwargs): 

158 """ 

159 Creates one or several "parameters" fixtures - depending on the number or coma-separated names in `argnames`. The 

160 created fixtures are automatically registered into the callers' module, but you may wish to assign them to 

161 variables for convenience. In that case make sure that you use the same names, e.g. 

162 `p, q = param_fixtures('p,q', [(0, 1), (2, 3)])`. 

163 

164 Note that the (argnames, argvalues, ids) signature is similar to `@pytest.mark.parametrize` for consistency, 

165 see https://docs.pytest.org/en/latest/reference.html?highlight=pytest.param#pytest-mark-parametrize 

166 

167 ```python 

168 import pytest 

169 from pytest_cases import param_fixtures, param_fixture 

170 

171 # create a 2-tuple parameter fixture 

172 arg1, arg2 = param_fixtures("arg1, arg2", [(1, 2), (3, 4)]) 

173 

174 @pytest.fixture 

175 def fixture_uses_param2(arg2): 

176 ... 

177 

178 def test_uses_param2(arg1, arg2, fixture_uses_param2): 

179 ... 

180 ``` 

181 

182 :param argnames: same as `@pytest.mark.parametrize` `argnames`. 

183 :param argvalues: same as `@pytest.mark.parametrize` `argvalues`. 

184 :param autouse: see fixture `autouse` 

185 :param ids: same as `@pytest.mark.parametrize` `ids` 

186 :param scope: see fixture `scope` 

187 :param hook: an optional hook to apply to each fixture function that is created during this call. The hook function 

188 will be called every time a fixture is about to be created. It will receive a single argument (the function 

189 implementing the fixture) and should return the function to use. For example you can use `saved_fixture` from 

190 `pytest-harvest` as a hook in order to save all such created fixtures in the fixture store. 

191 :param debug: print debug messages on stdout to analyze fixture creation (use pytest -s to see them) 

192 :param kwargs: any other argument for the created 'fixtures' 

193 :return: the created fixtures 

194 """ 

195 argnames_lst = get_param_argnames_as_list(argnames) 

196 

197 caller_module = get_caller_module() 

198 

199 if len(argnames_lst) < 2: 

200 return _create_param_fixture(caller_module, argnames, argvalues, autouse=autouse, ids=ids, scope=scope, 

201 hook=hook, debug=debug, **kwargs) 

202 else: 

203 return _create_params_fixture(caller_module, argnames_lst, argvalues, autouse=autouse, ids=ids, scope=scope, 

204 hook=hook, debug=debug, **kwargs) 

205 

206 

207def _create_params_fixture(fixtures_dest, 

208 argnames_lst, # type: Sequence[str] 

209 argvalues, # type: Sequence[Any] 

210 autouse=False, # type: bool 

211 ids=None, # type: Union[Callable, Iterable[str]] 

212 scope="function", # type: str 

213 hook=None, # type: Callable[[Callable], Callable] 

214 debug=False, # type: bool 

215 **kwargs): 

216 argnames = ','.join(argnames_lst) 

217 created_fixtures = [] 

218 

219 # create the root fixture that will contain all parameter values 

220 # note: we sort the list so that the first in alphabetical order appears first. Indeed pytest uses this order. 

221 root_fixture_name = "%s__param_fixtures_root" % ('_'.join(sorted(argnames_lst))) 

222 

223 # Dynamically add fixture to caller's module as explained in https://github.com/pytest-dev/pytest/issues/2424 

224 root_fixture_name = check_name_available(fixtures_dest, root_fixture_name, if_name_exists=CHANGE, 

225 caller=param_fixtures) 

226 

227 if debug: 227 ↛ 228line 227 didn't jump to line 228, because the condition on line 227 was never true

228 print("Creating parametrized 'root' fixture %r returning %r" % (root_fixture_name, argvalues)) 

229 

230 @fixture(name=root_fixture_name, autouse=autouse, scope=scope, hook=hook, **kwargs) 

231 @pytest.mark.parametrize(argnames, argvalues, ids=ids) 

232 @with_signature("%s(%s)" % (root_fixture_name, argnames)) 

233 def _root_fixture(**_kwargs): 

234 return tuple(_kwargs[k] for k in argnames_lst) 

235 

236 # Override once again the symbol with the correct contents 

237 setattr(fixtures_dest, root_fixture_name, _root_fixture) 

238 

239 # finally create the sub-fixtures 

240 for param_idx, argname in enumerate(argnames_lst): 

241 # create the fixture 

242 # To fix late binding issue with `param_idx` we add an extra layer of scope: a factory function 

243 # See https://stackoverflow.com/questions/3431676/creating-functions-in-a-loop 

244 def _create_fixture(_param_idx): 

245 

246 if debug: 246 ↛ 247line 246 didn't jump to line 247, because the condition on line 246 was never true

247 print("Creating nonparametrized 'view' fixture %r returning %r[%s]" 

248 % (argname, root_fixture_name, _param_idx)) 

249 

250 @fixture(name=argname, scope=scope, autouse=autouse, hook=hook, **kwargs) 

251 @with_signature("%s(%s)" % (argname, root_fixture_name)) 

252 def _param_fixture(**_kwargs): 

253 params = _kwargs.pop(root_fixture_name) 

254 return params[_param_idx] 

255 

256 return _param_fixture 

257 

258 # create it 

259 fix = _create_fixture(param_idx) 

260 

261 # add to module 

262 check_name_available(fixtures_dest, argname, if_name_exists=WARN, caller=param_fixtures) 

263 setattr(fixtures_dest, argname, fix) 

264 

265 # collect to return the whole list eventually 

266 created_fixtures.append(fix) 

267 

268 return created_fixtures 

269 

270 

271# Fix for https://github.com/smarie/python-pytest-cases/issues/71 

272# In order for pytest to allow users to import this symbol in conftest.py 

273# they should be declared as optional plugin hooks. 

274# A workaround otherwise would be to remove the 'pytest_' name prefix 

275# See https://github.com/pytest-dev/pytest/issues/6475 

276@pytest.hookimpl(optionalhook=True) 

277def pytest_fixture_plus(*args, 

278 **kwargs): 

279 warn("`pytest_fixture_plus` and `fixture_plus` are deprecated. Please use the new alias `fixture`. " 

280 "See https://github.com/pytest-dev/pytest/issues/6475", category=DeprecationWarning, stacklevel=2) 

281 if len(args) == 1: 

282 if callable(args[0]): 

283 return _decorate_fixture_plus(args[0], _caller_module_offset_when_unpack=2, **kwargs) 

284 

285 def _fixture_plus(f): 

286 return _decorate_fixture_plus(f, *args, _caller_module_offset_when_unpack=2, **kwargs) 

287 return _fixture_plus 

288 

289 

290fixture_plus = pytest_fixture_plus 

291"""Deprecated too""" 

292 

293 

294@function_decorator 

295def fixture(scope="function", # type: str 

296 autouse=False, # type: bool 

297 name=None, # type: str 

298 unpack_into=None, # type: Iterable[str] 

299 hook=None, # type: Callable[[Callable], Callable] 

300 fixture_func=DECORATED, # noqa 

301 **kwargs): 

302 """ decorator to mark a fixture factory function. 

303 

304 Identical to `@pytest.fixture` decorator, except that 

305 

306 - when used in a fixture union (either explicit `fixture_union` or indirect through `@parametrize`+`fixture_ref` 

307 or `@parametrize_with_cases`), it will not be setup/teardown unnecessarily in tests that do not require it. 

308 

309 - it supports multi-parametrization with `@pytest.mark.parametrize` as requested in 

310 https://github.com/pytest-dev/pytest/issues/3960. As a consequence it does not support the `params` and `ids` 

311 arguments anymore. 

312 

313 - it supports a new argument `unpack_into` where you can provide names for fixtures where to unpack this fixture 

314 into. 

315 

316 As a consequence it does not support the `params` and `ids` arguments anymore. 

317 

318 :param scope: the scope for which this fixture is shared, one of "function" (default), "class", "module" or 

319 "session". 

320 :param autouse: if True, the fixture func is activated for all tests that can see it. If False (the default) then 

321 an explicit reference is needed to activate the fixture. 

322 :param name: the name of the fixture. This defaults to the name of the decorated function. Note: If a fixture is 

323 used in the same module in which it is defined, the function name of the fixture will be shadowed by the 

324 function arg that requests the fixture; one way to resolve this is to name the decorated function 

325 ``fixture_<fixturename>`` and then use ``@pytest.fixture(name='<fixturename>')``. 

326 :param unpack_into: an optional iterable of names, or string containing coma-separated names, for additional 

327 fixtures to create to represent parts of this fixture. See `unpack_fixture` for details. 

328 :param hook: an optional hook to apply to each fixture function that is created during this call. The hook function 

329 will be called every time a fixture is about to be created. It will receive a single argument (the function 

330 implementing the fixture) and should return the function to use. For example you can use `saved_fixture` from 

331 `pytest-harvest` as a hook in order to save all such created fixtures in the fixture store. 

332 :param kwargs: other keyword arguments for `@pytest.fixture` 

333 """ 

334 # todo what if this is called in a class ? 

335 

336 # the offset is 3 because of @function_decorator (decopatch library) 

337 return _decorate_fixture_plus(fixture_func, scope=scope, autouse=autouse, name=name, unpack_into=unpack_into, 

338 hook=hook, _caller_module_offset_when_unpack=3, **kwargs) 

339 

340 

341class FixtureParam(object): 

342 __slots__ = 'argnames', 

343 

344 def __init__(self, argnames): 

345 self.argnames = argnames 

346 

347 def __repr__(self): 

348 return "FixtureParam(argnames=%s)" % self.argnames 

349 

350 

351class CombinedFixtureParamValue(object): 

352 """Represents a parameter value created when @parametrize is used on a @fixture """ 

353 __slots__ = 'param_defs', 'argvalues', 

354 

355 def __init__(self, 

356 param_defs, # type: Iterable[FixtureParam] 

357 argvalues): 

358 self.param_defs = param_defs 

359 self.argvalues = argvalues 

360 

361 def iterparams(self): 

362 return ((pdef.argnames, v) for pdef, v in zip(self.param_defs, self.argvalues)) 

363 

364 def __repr__(self): 

365 list_str = " ; ".join(["<%r: %s>" % (a, v) for a, v in self.iterparams()]) 

366 return "CombinedFixtureParamValue(%s)" % list_str 

367 

368 

369def _decorate_fixture_plus(fixture_func, 

370 scope="function", # type: str 

371 autouse=False, # type: bool 

372 name=None, # type: str 

373 unpack_into=None, # type: Iterable[str] 

374 hook=None, # type: Callable[[Callable], Callable] 

375 _caller_module_offset_when_unpack=3, # type: int 

376 **kwargs): 

377 """ decorator to mark a fixture factory function. 

378 

379 Identical to `@pytest.fixture` decorator, except that 

380 

381 - it supports multi-parametrization with `@pytest.mark.parametrize` as requested in 

382 https://github.com/pytest-dev/pytest/issues/3960. As a consequence it does not support the `params` and `ids` 

383 arguments anymore. 

384 

385 - it supports a new argument `unpack_into` where you can provide names for fixtures where to unpack this fixture 

386 into. 

387 

388 :param scope: the scope for which this fixture is shared, one of "function" (default), "class", "module" or 

389 "session". 

390 :param autouse: if True, the fixture func is activated for all tests that can see it. If False (the default) then 

391 an explicit reference is needed to activate the fixture. 

392 :param name: the name of the fixture. This defaults to the name of the decorated function. Note: If a fixture is 

393 used in the same module in which it is defined, the function name of the fixture will be shadowed by the 

394 function arg that requests the fixture; one way to resolve this is to name the decorated function 

395 ``fixture_<fixturename>`` and then use ``@pytest.fixture(name='<fixturename>')``. 

396 :param unpack_into: an optional iterable of names, or string containing coma-separated names, for additional 

397 fixtures to create to represent parts of this fixture. See `unpack_fixture` for details. 

398 :param hook: an optional hook to apply to each fixture function that is created during this call. The hook function 

399 will be called every time a fixture is about to be created. It will receive a single argument (the function 

400 implementing the fixture) and should return the function to use. For example you can use `saved_fixture` from 

401 `pytest-harvest` as a hook in order to save all such created fixtures in the fixture store. 

402 :param kwargs: other keyword arguments for `@pytest.fixture` 

403 """ 

404 if name is not None: 

405 # Compatibility for the 'name' argument 

406 if PYTEST3_OR_GREATER: 406 ↛ 409line 406 didn't jump to line 409, because the condition on line 406 was never false

407 # pytest version supports "name" keyword argument 

408 kwargs['name'] = name 

409 elif name is not None: 

410 # 'name' argument is not supported in this old version, use the __name__ trick. 

411 fixture_func.__name__ = name 

412 

413 # if unpacking is requested, do it first 

414 if unpack_into is not None: 

415 # get the future fixture name if needed 

416 if name is None: 416 ↛ 420line 416 didn't jump to line 420, because the condition on line 416 was never false

417 name = fixture_func.__name__ 

418 

419 # get caller module to create the symbols 

420 caller_module = get_caller_module(frame_offset=_caller_module_offset_when_unpack) 

421 

422 # note that we cannot use in_cls=True since we have no way to assign the unpacked fixtures to the class 

423 _make_unpack_fixture(caller_module, unpack_into, name, hook=hook, in_cls=False) 

424 

425 # (1) Collect all @pytest.mark.parametrize markers (including those created by usage of @cases_data) 

426 parametrizer_marks = get_pytest_parametrize_marks(fixture_func, pop=PYTEST8_OR_GREATER) 

427 if len(parametrizer_marks) < 1: 

428 # make the fixture union-aware 

429 wrapped_fixture_func = ignore_unused(fixture_func) 

430 

431 # resolve possibly infinite generators of ids here 

432 if 'params' in kwargs and 'ids' in kwargs: 

433 kwargs['ids'] = resolve_ids(kwargs['ids'], kwargs['params'], full_resolve=False) 

434 

435 # transform the created wrapper into a fixture 

436 return pytest_fixture(scope=scope, autouse=autouse, hook=hook, **kwargs)(wrapped_fixture_func) 

437 

438 else: 

439 if 'params' in kwargs: 439 ↛ 440line 439 didn't jump to line 440, because the condition on line 439 was never true

440 raise ValueError( 

441 "With `fixture` you cannot mix usage of the keyword argument `params` and of " 

442 "the pytest.mark.parametrize marks") 

443 

444 # (2) create the huge "param" containing all params combined 

445 # --loop (use the same order to get it right) 

446 param_defs = [] 

447 params_values = [] 

448 params_ids = [] 

449 params_marks = [] 

450 for pmark in parametrizer_marks: 

451 # -- pmark is a single @pytest.parametrize mark. -- 

452 

453 # check number of parameter names in this parameterset 

454 if len(pmark.param_names) < 1: 454 ↛ 455line 454 didn't jump to line 455, because the condition on line 454 was never true

455 raise ValueError("Fixture function '%s' decorated with '@fixture' has an empty parameter " 

456 "name in a @pytest.mark.parametrize mark") 

457 

458 # remember the argnames 

459 param_defs.append(FixtureParam(pmark.param_names)) 

460 

461 # separate specific configuration (pytest.param()) from the values 

462 custom_pids, _pmarks, _pvalues = extract_parameterset_info(pmark.param_names, pmark.param_values, check_nb=True) 

463 

464 # get the ids by merging/creating the various possibilities 

465 _paramids = make_test_ids(argnames=pmark.param_names, argvalues=_pvalues, 

466 global_ids=pmark.param_ids, id_marks=custom_pids) 

467 

468 # Finally store the ids, marks, and values for this parameterset 

469 params_ids.append(_paramids) 

470 params_marks.append(tuple(_pmarks)) 

471 params_values.append(tuple(_pvalues)) 

472 

473 # (3) generate the ids and values, possibly reapplying marks 

474 if len(param_defs) == 1: 

475 # A single @parametrize : we can simplify - that will be more readable 

476 final_ids = params_ids[0] 

477 final_marks = params_marks[0] 

478 # note: we dot his even for a single @parametrize as it allows `current_case` to get the parameter names easily 

479 final_values = [CombinedFixtureParamValue(param_defs, (v,)) for v in params_values[0]] 

480 

481 # reapply the marks 

482 for i, marks in enumerate(final_marks): 

483 if marks is not None: 

484 final_values[i] = make_marked_parameter_value((final_values[i],), marks=marks) 

485 else: 

486 # Multiple @parametrize: since pytest does not support several, we merge them with "appearance" of several 

487 # --equivalent id 

488 final_ids = combine_ids(product(*params_ids)) 

489 # --merge all values, we'll unpack them in the wrapper below 

490 final_values = [CombinedFixtureParamValue(param_defs, v) for v in product(*params_values)] 

491 

492 final_marks = tuple(product(*params_marks)) 

493 

494 # reapply the marks 

495 for i, marks in enumerate(final_marks): 

496 ms = [m for mm in marks if mm is not None for m in mm] 

497 if len(ms) > 0: 

498 final_values[i] = make_marked_parameter_value((final_values[i],), marks=ms) 

499 

500 if len(final_values) != len(final_ids): 500 ↛ 501line 500 didn't jump to line 501, because the condition on line 500 was never true

501 raise ValueError("Internal error related to fixture parametrization- please report") 

502 

503 # (4) wrap the fixture function so as to remove the parameter names and add 'request' if needed 

504 all_param_names = tuple(v for pnames in param_defs for v in pnames.argnames) 

505 

506 # --create the new signature that we want to expose to pytest 

507 old_sig = signature(fixture_func) 

508 for p in all_param_names: 

509 if p not in old_sig.parameters: 509 ↛ 510line 509 didn't jump to line 510, because the condition on line 509 was never true

510 raise ValueError("parameter '%s' not found in fixture signature '%s%s'" 

511 "" % (p, fixture_func.__name__, old_sig)) 

512 new_sig = remove_signature_parameters(old_sig, *all_param_names) 

513 # add request if needed 

514 func_needs_request = 'request' in old_sig.parameters 

515 if not func_needs_request: 

516 # Add it last so that `self` argument in class functions remains the first 

517 new_sig = add_signature_parameters(new_sig, last=Parameter('request', kind=Parameter.POSITIONAL_OR_KEYWORD)) 

518 

519 # --common routine used below. Fills kwargs with the appropriate names and values from fixture_params 

520 def _map_arguments(*_args, **_kwargs): 

521 request = _kwargs['request'] if func_needs_request else _kwargs.pop('request') 

522 

523 # sanity check: we have created this combined value in the combined parametrization. 

524 _paramz = request.param 

525 if not isinstance(_paramz, CombinedFixtureParamValue): 

526 # This can happen when indirect parametrization has been used. 

527 # In that case we can work but this parameter will not appear in `current_cases` fixture 

528 _paramz = CombinedFixtureParamValue(param_defs, _paramz if len(param_defs) > 1 else (_paramz,)) 

529 

530 # populate the parameters 

531 for p_names, p_argvals in _paramz.iterparams(): 

532 if len(p_names) == 1: 

533 # a single parameter for that generated fixture (@pytest.mark.parametrize with a single name) 

534 _kwargs[p_names[0]] = get_lazy_args(p_argvals, request) 

535 else: 

536 # several parameters for that generated fixture (@pytest.mark.parametrize with several names) 

537 # unpack all of them and inject them in the kwargs 

538 for old_p_name, old_p_value in zip(p_names, p_argvals): 

539 _kwargs[old_p_name] = get_lazy_args(old_p_value, request) 

540 

541 return _args, _kwargs 

542 

543 # --Finally create the fixture function, a wrapper of user-provided fixture with the new signature 

544 if isasyncgenfunction(fixture_func)and sys.version_info >= (3, 6): 544 ↛ 545line 544 didn't jump to line 545, because the condition on line 544 was never true

545 from .pep525 import _decorate_fixture_plus_asyncgen_pep525 

546 wrapped_fixture_func = _decorate_fixture_plus_asyncgen_pep525(fixture_func, new_sig, _map_arguments) 

547 elif iscoroutinefunction(fixture_func) and sys.version_info >= (3, 5): 547 ↛ 548line 547 didn't jump to line 548, because the condition on line 547 was never true

548 from .pep492 import _decorate_fixture_plus_coroutine_pep492 

549 wrapped_fixture_func = _decorate_fixture_plus_coroutine_pep492(fixture_func, new_sig, _map_arguments) 

550 elif isgeneratorfunction(fixture_func): 

551 # generator function (with a yield statement) 

552 if sys.version_info >= (3, 3): 552 ↛ 556line 552 didn't jump to line 556, because the condition on line 552 was never false

553 from .pep380 import _decorate_fixture_plus_generator_pep380 

554 wrapped_fixture_func = _decorate_fixture_plus_generator_pep380(fixture_func, new_sig, _map_arguments) 

555 else: 

556 @wraps(fixture_func, new_sig=new_sig) 

557 def wrapped_fixture_func(*_args, **_kwargs): 

558 if not is_used_request(_kwargs['request']): 

559 yield NOT_USED 

560 else: 

561 _args, _kwargs = _map_arguments(*_args, **_kwargs) 

562 for res in fixture_func(*_args, **_kwargs): 

563 yield res 

564 else: 

565 # normal function with return statement 

566 @wraps(fixture_func, new_sig=new_sig) 

567 def wrapped_fixture_func(*_args, **_kwargs): 

568 if not is_used_request(_kwargs['request']): 

569 return NOT_USED 

570 else: 

571 _args, _kwargs = _map_arguments(*_args, **_kwargs) 

572 return fixture_func(*_args, **_kwargs) 

573 

574 # transform the created wrapper into a fixture 

575 _make_fix = pytest_fixture(scope=scope, params=final_values, autouse=autouse, hook=hook, ids=final_ids, **kwargs) 

576 return _make_fix(wrapped_fixture_func)