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>
5 from __future__ import division
6
7 from inspect import isgeneratorfunction
8 from itertools import product
9 from warnings import warn
10
11 from decopatch import function_decorator, DECORATED
12 from makefun import with_signature, add_signature_parameters, remove_signature_parameters, wraps
13
14 import pytest
15 import sys
16
17 try: # python 3.3+
18 from inspect import signature, Parameter
19 except ImportError:
20 from funcsigs import signature, Parameter # noqa
21
-
E261
At least two spaces before inline comment
22 try: # native coroutines, python 3.5+
23 from inspect import iscoroutinefunction
24 except ImportError:
25 def iscoroutinefunction(obj):
26 return False
27
-
E261
At least two spaces before inline comment
28 try: # native async generators, python 3.6+
29 from inspect import isasyncgenfunction
30 except ImportError:
31 def isasyncgenfunction(obj):
32 return False
33
34 try: # type hints, python 3+
35 from typing import Callable, Union, Any, List, Iterable, Sequence # noqa
36 from types import ModuleType # noqa
37 except ImportError:
38 pass
39
40 from .common_pytest_lazy_values import get_lazy_args
41 from .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
43 from .common_pytest_marks import PYTEST3_OR_GREATER, PYTEST8_OR_GREATER
44 from .fixture__creation import get_caller_module, check_name_available, WARN, CHANGE
45 from .fixture_core1_unions import ignore_unused, is_used_request, NOT_USED, _make_unpack_fixture
46
47
48 def 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:
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:
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
101 def _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):
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
150 def 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
207 def _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:
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:
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)
277 def 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
290 fixture_plus = pytest_fixture_plus
291 """Deprecated too"""
292
293
294 @function_decorator
295 def 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
341 class 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
351 class 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
369 def _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:
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:
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:
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:
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):
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:
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
-
E225
Missing whitespace around operator
544 if isasyncgenfunction(fixture_func)and sys.version_info >= (3, 6):
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):
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):
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)