2
2
import warnings
3
3
from collections import namedtuple
4
4
from collections .abc import MutableMapping
5
+ from typing import Any
5
6
from typing import Iterable
6
7
from typing import List
8
+ from typing import Mapping
7
9
from typing import Optional
10
+ from typing import Sequence
8
11
from typing import Set
12
+ from typing import Tuple
9
13
from typing import Union
10
14
11
15
import attr
@@ -140,28 +144,32 @@ def _for_parametrize(cls, argnames, argvalues, func, config, function_definition
140
144
141
145
@attr .s (frozen = True )
142
146
class Mark :
143
- #: name of the mark
147
+ #: Name of the mark.
144
148
name = attr .ib (type = str )
145
- #: positional arguments of the mark decorator
146
- args = attr .ib () # List[object]
147
- #: keyword arguments of the mark decorator
148
- kwargs = attr .ib () # Dict [str, object]
149
+ #: Positional arguments of the mark decorator.
150
+ args = attr .ib (type = Tuple [ Any , ...])
151
+ #: Keyword arguments of the mark decorator.
152
+ kwargs = attr .ib (type = Mapping [str , Any ])
149
153
150
- #: source Mark for ids with parametrize Marks
154
+ #: Source Mark for ids with parametrize Marks.
151
155
_param_ids_from = attr .ib (type = Optional ["Mark" ], default = None , repr = False )
152
- #: resolved/generated ids with parametrize Marks
153
- _param_ids_generated = attr .ib (type = Optional [List [str ]], default = None , repr = False )
156
+ #: Resolved/generated ids with parametrize Marks.
157
+ _param_ids_generated = attr .ib (
158
+ type = Optional [Sequence [str ]], default = None , repr = False
159
+ )
154
160
155
- def _has_param_ids (self ):
161
+ def _has_param_ids (self ) -> bool :
156
162
return "ids" in self .kwargs or len (self .args ) >= 4
157
163
158
164
def combined_with (self , other : "Mark" ) -> "Mark" :
159
- """
160
- :param other: the mark to combine with
165
+ """Return a new Mark which is a combination of this
166
+ Mark and another Mark.
167
+
168
+ Combines by appending args and merging kwargs.
169
+
170
+ :param other: The mark to combine with.
161
171
:type other: Mark
162
172
:rtype: Mark
163
-
164
- combines by appending args and merging the mappings
165
173
"""
166
174
assert self .name == other .name
167
175
@@ -183,76 +191,77 @@ def combined_with(self, other: "Mark") -> "Mark":
183
191
184
192
@attr .s
185
193
class MarkDecorator :
186
- """ A decorator for test functions and test classes. When applied
187
- it will create :class:`Mark` objects which are often created like this::
194
+ """A decorator for applying a mark on test functions and classes.
195
+
196
+ MarkDecorators are created with ``pytest.mark``::
188
197
189
- mark1 = pytest.mark.NAME # simple MarkDecorator
190
- mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator
198
+ mark1 = pytest.mark.NAME # Simple MarkDecorator
199
+ mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator
191
200
192
201
and can then be applied as decorators to test functions::
193
202
194
203
@mark2
195
204
def test_function():
196
205
pass
197
206
198
- When a MarkDecorator instance is called it does the following:
207
+ When a MarkDecorator is called it does the following:
199
208
200
209
1. If called with a single class as its only positional argument and no
201
- additional keyword arguments, it attaches itself to the class so it
210
+ additional keyword arguments, it attaches the mark to the class so it
202
211
gets applied automatically to all test cases found in that class.
203
- 2. If called with a single function as its only positional argument and
204
- no additional keyword arguments, it attaches a MarkInfo object to the
205
- function, containing all the arguments already stored internally in
206
- the MarkDecorator.
207
- 3. When called in any other case, it performs a 'fake construction' call,
208
- i.e. it returns a new MarkDecorator instance with the original
209
- MarkDecorator's content updated with the arguments passed to this
210
- call.
211
-
212
- Note: The rules above prevent MarkDecorator objects from storing only a
213
- single function or class reference as their positional argument with no
214
- additional keyword or positional arguments.
215
212
213
+ 2. If called with a single function as its only positional argument and
214
+ no additional keyword arguments, it attaches the mark to the function,
215
+ containing all the arguments already stored internally in the
216
+ MarkDecorator.
217
+
218
+ 3. When called in any other case, it returns a new MarkDecorator instance
219
+ with the original MarkDecorator's content updated with the arguments
220
+ passed to this call.
221
+
222
+ Note: The rules above prevent MarkDecorators from storing only a single
223
+ function or class reference as their positional argument with no
224
+ additional keyword or positional arguments. You can work around this by
225
+ using `with_args()`.
216
226
"""
217
227
218
- mark = attr .ib (validator = attr .validators .instance_of (Mark ))
228
+ mark = attr .ib (type = Mark , validator = attr .validators .instance_of (Mark ))
219
229
220
230
@property
221
- def name (self ):
222
- """alias for mark.name"""
231
+ def name (self ) -> str :
232
+ """Alias for mark.name. """
223
233
return self .mark .name
224
234
225
235
@property
226
- def args (self ):
227
- """alias for mark.args"""
236
+ def args (self ) -> Tuple [ Any , ...] :
237
+ """Alias for mark.args. """
228
238
return self .mark .args
229
239
230
240
@property
231
- def kwargs (self ):
232
- """alias for mark.kwargs"""
241
+ def kwargs (self ) -> Mapping [ str , Any ] :
242
+ """Alias for mark.kwargs. """
233
243
return self .mark .kwargs
234
244
235
245
@property
236
- def markname (self ):
246
+ def markname (self ) -> str :
237
247
return self .name # for backward-compat (2.4.1 had this attr)
238
248
239
- def __repr__ (self ):
249
+ def __repr__ (self ) -> str :
240
250
return "<MarkDecorator {!r}>" .format (self .mark )
241
251
242
- def with_args (self , * args , ** kwargs ) :
243
- """ return a MarkDecorator with extra arguments added
252
+ def with_args (self , * args : object , ** kwargs : object ) -> "MarkDecorator" :
253
+ """Return a MarkDecorator with extra arguments added.
244
254
245
- unlike call this can be used even if the sole argument is a callable/class
255
+ Unlike calling the MarkDecorator, with_args() can be used even
256
+ if the sole argument is a callable/class.
246
257
247
258
:return: MarkDecorator
248
259
"""
249
-
250
260
mark = Mark (self .name , args , kwargs )
251
261
return self .__class__ (self .mark .combined_with (mark ))
252
262
253
- def __call__ (self , * args , ** kwargs ):
254
- """ if passed a single callable argument: decorate it with mark info.
255
- otherwise add *args/**kwargs in-place to mark information. """
263
+ def __call__ (self , * args : object , ** kwargs : object ):
264
+ """Call the MarkDecorator."""
256
265
if args and not kwargs :
257
266
func = args [0 ]
258
267
is_class = inspect .isclass (func )
@@ -288,27 +297,31 @@ def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List
288
297
return [x for x in extracted if isinstance (x , Mark )]
289
298
290
299
291
- def store_mark (obj , mark ):
292
- """store a Mark on an object
293
- this is used to implement the Mark declarations/decorators correctly
300
+ def store_mark (obj , mark : Mark ) -> None :
301
+ """Store a Mark on an object.
302
+
303
+ This is used to implement the Mark declarations/decorators correctly.
294
304
"""
295
305
assert isinstance (mark , Mark ), mark
296
- # always reassign name to avoid updating pytestmark
297
- # in a reference that was only borrowed
306
+ # Always reassign name to avoid updating pytestmark in a reference that
307
+ # was only borrowed.
298
308
obj .pytestmark = get_unpacked_marks (obj ) + [mark ]
299
309
300
310
301
311
class MarkGenerator :
302
- """ Factory for :class:`MarkDecorator` objects - exposed as
303
- a ``pytest.mark`` singleton instance. Example::
312
+ """Factory for :class:`MarkDecorator` objects - exposed as
313
+ a ``pytest.mark`` singleton instance.
314
+
315
+ Example::
304
316
305
317
import pytest
318
+
306
319
@pytest.mark.slowtest
307
320
def test_function():
308
321
pass
309
322
310
- will set a 'slowtest' :class:`MarkInfo` object
311
- on the ``test_function`` object. """
323
+ applies a 'slowtest' :class:`Mark` on ``test_function``.
324
+ """
312
325
313
326
_config = None
314
327
_markers = set () # type: Set[str]
0 commit comments