33except ImportError :
44 from funcsigs import signature
55
6- import pytest
6+ from distutils .version import LooseVersion
7+ from warnings import warn
78
9+ import pytest
10+ from _pytest .mark import ParameterSet
811
912# Create a symbol that will work to create a fixture containing 'yield', whatever the pytest version
1013# Note: if more prevision is needed, use if LooseVersion(pytest.__version__) < LooseVersion('3.0.0')
1417 yield_fixture = pytest .fixture
1518
1619
17- class _LegacyMark :
18- __slots__ = "args" , "kwargs"
19-
20- def __init__ (self , * args , ** kwargs ):
21- self .args = args
22- self .kwargs = kwargs
23-
24-
20+ # ------------ container for the mark information that we grab from the fixtures (`@pytest_fixture_plus`)
2521class _ParametrizationMark :
2622 """
27- Represents the information required by `decorate_pytest_fixture_plus ` to work.
23+ Represents the information required by `@pytest_fixture_plus ` to work.
2824 """
2925 __slots__ = "param_names" , "param_values" , "param_ids"
3026
@@ -40,9 +36,36 @@ def __init__(self, mark):
4036 self .param_ids = bound .arguments .get ('ids' , None )
4137
4238
39+ # -------- tools to get the parametrization mark whatever the pytest version
40+ class _LegacyMark :
41+ __slots__ = "args" , "kwargs"
42+
43+ def __init__ (self , * args , ** kwargs ):
44+ self .args = args
45+ self .kwargs = kwargs
46+
47+
48+ def get_pytest_marks_on_function (f ):
49+ """
50+ Utility to return *ALL* pytest marks (not only parametrization) applied on a function
51+
52+ :param f:
53+ :return:
54+ """
55+ try :
56+ return f .pytestmark
57+ except AttributeError :
58+ try :
59+ # old pytest < 3: marks are set as fields on the function object
60+ # but they do not have a particulat type, their type is 'instance'...
61+ return [v for v in vars (f ).values () if str (v ).startswith ("<MarkInfo '" )]
62+ except AttributeError :
63+ return []
64+
65+
4366def get_pytest_parametrize_marks (f ):
4467 """
45- Returns the @pytest.mark.parametrize marks associated with a function
68+ Returns the @pytest.mark.parametrize marks associated with a function (and only those)
4669
4770 :param f:
4871 :return: a tuple containing all 'parametrize' marks
@@ -74,3 +97,101 @@ def get_parametrize_signature():
7497 :return: a reference signature representing
7598 """
7699 return signature (_pytest_mark_parametrize )
100+
101+
102+ # ---------- test ids utils ---------
103+ def get_test_ids_from_param_values (param_names ,
104+ param_values ,
105+ ):
106+ """
107+ Replicates pytest behaviour to generate the ids when there are several parameters in a single `parametrize`
108+
109+ :param param_names:
110+ :param param_values:
111+ :return:
112+ """
113+ nb_params = len (param_names )
114+ if nb_params == 0 :
115+ raise ValueError ("empty list provided" )
116+ elif nb_params == 1 :
117+ paramids = tuple (str (v ) for v in param_values )
118+ else :
119+ paramids = []
120+ for vv in param_values :
121+ if len (vv ) != nb_params :
122+ raise ValueError ("Inconsistent lenghts for parameter names and values: '%s' and '%s'"
123+ "" % (param_names , vv ))
124+ paramids .append ('-' .join ([str (v ) for v in vv ]))
125+ return paramids
126+
127+
128+ # ---- ParameterSet api ---
129+ def is_marked_parameter_value (v ):
130+ return isinstance (v , ParameterSet )
131+
132+
133+ def get_marked_parameter_marks (v ):
134+ return v .marks
135+
136+
137+ def get_marked_parameter_values (v ):
138+ return v .values
139+
140+
141+ # ---- tools to reapply marks on test parameter values, whatever the pytest version ----
142+
143+ # Compatibility for the way we put marks on single parameters in the list passed to @pytest.mark.parametrize
144+ # see https://docs.pytest.org/en/3.3.0/skipping.html?highlight=mark%20parametrize#skip-xfail-with-parametrize
145+
146+ try :
147+ # check if pytest.param exists
148+ _ = pytest .param
149+ except AttributeError :
150+ # if not this is how it was done
151+ # see e.g. https://docs.pytest.org/en/2.9.2/skipping.html?highlight=mark%20parameter#skip-xfail-with-parametrize
152+ def make_marked_parameter_value (c , marks ):
153+ if len (marks ) > 1 :
154+ raise ValueError ("Multiple marks on parameters not supported for old versions of pytest" )
155+ else :
156+ # get a decorator for each of the markinfo
157+ marks_mod = transform_marks_into_decorators (marks )
158+
159+ # decorate
160+ return marks_mod [0 ](c )
161+ else :
162+ # Otherwise pytest.param exists, it is easier
163+ def make_marked_parameter_value (c , marks ):
164+ # get a decorator for each of the markinfo
165+ marks_mod = transform_marks_into_decorators (marks )
166+
167+ # decorate
168+ return pytest .param (c , marks = marks_mod )
169+
170+
171+ def transform_marks_into_decorators (marks ):
172+ """
173+ Transforms the provided marks (MarkInfo) obtained from marked cases, into MarkDecorator so that they can
174+ be re-applied to generated pytest parameters in the global @pytest.mark.parametrize.
175+
176+ :param marks:
177+ :return:
178+ """
179+ marks_mod = []
180+ try :
181+ for m in marks :
182+ md = pytest .mark .MarkDecorator ()
183+ if LooseVersion (pytest .__version__ ) >= LooseVersion ('3.0.0' ):
184+ md .mark = m
185+ else :
186+ md .name = m .name
187+ # md.markname = m.name
188+ md .args = m .args
189+ md .kwargs = m .kwargs
190+
191+ # markinfodecorator = getattr(pytest.mark, markinfo.name)
192+ # markinfodecorator(*markinfo.args)
193+
194+ marks_mod .append (md )
195+ except Exception as e :
196+ warn ("Caught exception while trying to mark case: [%s] %s" % (type (e ), e ))
197+ return marks_mod
0 commit comments