77from distutils .version import LooseVersion
88from inspect import getmembers , isgeneratorfunction , getmodule
99
10+ from decopatch import function_decorator , DECORATED , with_parenthesis
1011from makefun import with_signature , add_signature_parameters , remove_signature_parameters
1112
1213try : # python 3.3+
@@ -167,11 +168,13 @@ def get_pytest_marks_on_case_func(f):
167168"""Marker that can be used instead of a module name to indicate that the module is the current one"""
168169
169170
171+ @function_decorator
170172def cases_fixture (cases = None , # type: Union[Callable[[Any], Any], Iterable[Callable[[Any], Any]]]
171173 module = None , # type: Union[ModuleType, Iterable[ModuleType]]
172174 case_data_argname = 'case_data' , # type: str
173175 has_tag = None , # type: Any
174176 filter = None , # type: Callable[[List[Any]], bool]
177+ f = DECORATED ,
175178 ** kwargs
176179 ):
177180 """
@@ -239,23 +242,22 @@ def foo_fixture(request):
239242 `module`. It both `has_tag` and `filter` are set, both will be applied in sequence.
240243 :return:
241244 """
242- def _double_decorator (f ):
243- # apply @cases_data (that will translate to a @pytest.mark.parametrize)
244- parametrized_f = cases_data (cases = cases , module = module ,
245- case_data_argname = case_data_argname , has_tag = has_tag , filter = filter )(f )
246- # apply @pytest_fixture_plus
247- return pytest_fixture_plus (** kwargs )(parametrized_f )
248-
249- return _double_decorator
245+ # apply @cases_data (that will translate to a @pytest.mark.parametrize)
246+ parametrized_f = cases_data (cases = cases , module = module ,
247+ case_data_argname = case_data_argname , has_tag = has_tag , filter = filter )(f )
248+ # apply @pytest_fixture_plus
249+ return pytest_fixture_plus (** kwargs )(parametrized_f )
250250
251251
252+ @function_decorator
252253def pytest_fixture_plus (scope = "function" ,
253254 params = None ,
254255 autouse = False ,
255256 ids = None ,
256257 name = None ,
258+ fixture_func = DECORATED ,
257259 ** kwargs ):
258- """ (return a) decorator to mark a fixture factory function.
260+ """ decorator to mark a fixture factory function.
259261
260262 Identical to `@pytest.fixture` decorator, except that it supports multi-parametrization with
261263 `@pytest.mark.parametrize` as requested in https://github.com/pytest-dev/pytest/issues/3960.
@@ -280,52 +282,6 @@ def pytest_fixture_plus(scope="function",
280282 ``@pytest.fixture(name='<fixturename>')``.
281283 :param kwargs: other keyword arguments for `@pytest.fixture`
282284 """
283-
284- if callable (scope ) and params is None and autouse is False :
285- # direct decoration without arguments
286- return decorate_pytest_fixture_plus (scope )
287- else :
288- # arguments have been provided
289- def _decorator (f ):
290- return decorate_pytest_fixture_plus (f ,
291- scope = scope , params = params , autouse = autouse , ids = ids , name = name ,
292- ** kwargs )
293- return _decorator
294-
295-
296- def decorate_pytest_fixture_plus (fixture_func ,
297- scope = "function" ,
298- params = None ,
299- autouse = False ,
300- ids = None ,
301- name = None ,
302- ** kwargs ):
303- """
304- Manual decorator equivalent to `@pytest_fixture_plus`
305-
306- :param fixture_func: the function to decorate
307-
308- :param scope: the scope for which this fixture is shared, one of
309- "function" (default), "class", "module" or "session".
310- :param params: an optional list of parameters which will cause multiple
311- invocations of the fixture function and all of the tests
312- using it.
313- :param autouse: if True, the fixture func is activated for all tests that
314- can see it. If False (the default) then an explicit
315- reference is needed to activate the fixture.
316- :param ids: list of string ids each corresponding to the params
317- so that they are part of the test id. If no ids are provided
318- they will be generated automatically from the params.
319- :param name: the name of the fixture. This defaults to the name of the
320- decorated function. If a fixture is used in the same module in
321- which it is defined, the function name of the fixture will be
322- shadowed by the function arg that requests the fixture; one way
323- to resolve this is to name the decorated function
324- ``fixture_<fixturename>`` and then use
325- ``@pytest.fixture(name='<fixturename>')``.
326- :param kwargs:
327- :return:
328- """
329285 # Compatibility for the 'name' argument
330286 if LooseVersion (pytest .__version__ ) >= LooseVersion ('3.0.0' ):
331287 # pytest version supports "name" keyword argument
@@ -436,11 +392,13 @@ def wrapped_fixture_func(*args, **kwargs):
436392 return fixture_decorator (wrapped_fixture_func )
437393
438394
395+ @function_decorator (custom_disambiguator = with_parenthesis )
439396def cases_data (cases = None , # type: Union[Callable[[Any], Any], Iterable[Callable[[Any], Any]]]
440397 module = None , # type: Union[ModuleType, Iterable[ModuleType]]
441398 case_data_argname = 'case_data' , # type: str
442399 has_tag = None , # type: Any
443- filter = None # type: Callable[[List[Any]], bool]
400+ filter = None , # type: Callable[[List[Any]], bool]
401+ test_func = DECORATED ,
444402 ):
445403 """
446404 Decorates a test function so as to automatically parametrize it with all cases listed in module `module`, or with
@@ -496,28 +454,18 @@ def test_foo(case_data: CaseData):
496454 `module`. It both `has_tag` and `filter` are set, both will be applied in sequence.
497455 :return:
498456 """
499- def datasets_decorator (test_func ):
500- """
501- The generated test function decorator.
502-
503- It is equivalent to @mark.parametrize('case_data', cases) where cases is a tuple containing a CaseDataGetter for
504- all case generator functions
457+ # equivalent to @mark.parametrize('case_data', cases) where cases is a tuple containing a CaseDataGetter for
505458
506- :param test_func:
507- :return:
508- """
509- # First list all cases according to user preferences
510- _cases = get_all_cases (cases , module , test_func , has_tag , filter )
459+ # First list all cases according to user preferences
460+ _cases = get_all_cases (cases , module , test_func , has_tag , filter )
511461
512- # Then transform into required arguments for pytest (applying the pytest marks if needed)
513- marked_cases , cases_ids = get_pytest_parametrize_args (_cases )
462+ # Then transform into required arguments for pytest (applying the pytest marks if needed)
463+ marked_cases , cases_ids = get_pytest_parametrize_args (_cases )
514464
515- # Finally create the pytest decorator and apply it
516- parametrizer = pytest .mark .parametrize (case_data_argname , marked_cases , ids = cases_ids )
465+ # Finally create the pytest decorator and apply it
466+ parametrizer = pytest .mark .parametrize (case_data_argname , marked_cases , ids = cases_ids )
517467
518- return parametrizer (test_func )
519-
520- return datasets_decorator
468+ return parametrizer (test_func )
521469
522470
523471def get_pytest_parametrize_args (cases ):
@@ -542,7 +490,6 @@ def get_pytest_parametrize_args(cases):
542490 return marked_cases , case_ids
543491
544492
545-
546493# Compatibility for the way we put marks on single parameters in the list passed to @pytest.mark.parametrize
547494# see https://docs.pytest.org/en/3.3.0/skipping.html?highlight=mark%20parametrize#skip-xfail-with-parametrize
548495
0 commit comments