28
28
from ..compat import NOTSET
29
29
from ..compat import NotSetType
30
30
from _pytest .config import Config
31
+ from _pytest .deprecated import check_ispytest
31
32
from _pytest .outcomes import fail
32
33
from _pytest .warning_types import PytestUnknownMarkWarning
33
34
@@ -200,21 +201,38 @@ def _for_parametrize(
200
201
201
202
202
203
@final
203
- @attr .s (frozen = True )
204
+ @attr .s (frozen = True , init = False , auto_attribs = True )
204
205
class Mark :
205
206
#: Name of the mark.
206
- name = attr . ib ( type = str )
207
+ name : str
207
208
#: Positional arguments of the mark decorator.
208
- args = attr . ib ( type = Tuple [Any , ...])
209
+ args : Tuple [Any , ...]
209
210
#: Keyword arguments of the mark decorator.
210
- kwargs = attr . ib ( type = Mapping [str , Any ])
211
+ kwargs : Mapping [str , Any ]
211
212
212
213
#: Source Mark for ids with parametrize Marks.
213
- _param_ids_from = attr . ib ( type = Optional ["Mark" ], default = None , repr = False )
214
+ _param_ids_from : Optional ["Mark" ] = attr . ib ( default = None , repr = False )
214
215
#: Resolved/generated ids with parametrize Marks.
215
- _param_ids_generated = attr .ib (
216
- type = Optional [Sequence [str ]], default = None , repr = False
217
- )
216
+ _param_ids_generated : Optional [Sequence [str ]] = attr .ib (default = None , repr = False )
217
+
218
+ def __init__ (
219
+ self ,
220
+ name : str ,
221
+ args : Tuple [Any , ...],
222
+ kwargs : Mapping [str , Any ],
223
+ param_ids_from : Optional ["Mark" ] = None ,
224
+ param_ids_generated : Optional [Sequence [str ]] = None ,
225
+ * ,
226
+ _ispytest : bool = False ,
227
+ ) -> None :
228
+ """:meta private:"""
229
+ check_ispytest (_ispytest )
230
+ # Weirdness to bypass frozen=True.
231
+ object .__setattr__ (self , "name" , name )
232
+ object .__setattr__ (self , "args" , args )
233
+ object .__setattr__ (self , "kwargs" , kwargs )
234
+ object .__setattr__ (self , "_param_ids_from" , param_ids_from )
235
+ object .__setattr__ (self , "_param_ids_generated" , param_ids_generated )
218
236
219
237
def _has_param_ids (self ) -> bool :
220
238
return "ids" in self .kwargs or len (self .args ) >= 4
@@ -243,20 +261,21 @@ def combined_with(self, other: "Mark") -> "Mark":
243
261
self .args + other .args ,
244
262
dict (self .kwargs , ** other .kwargs ),
245
263
param_ids_from = param_ids_from ,
264
+ _ispytest = True ,
246
265
)
247
266
248
267
249
268
# A generic parameter designating an object to which a Mark may
250
269
# be applied -- a test function (callable) or class.
251
270
# Note: a lambda is not allowed, but this can't be represented.
252
- _Markable = TypeVar ("_Markable " , bound = Union [Callable [..., object ], type ])
271
+ Markable = TypeVar ("Markable " , bound = Union [Callable [..., object ], type ])
253
272
254
273
255
- @attr .s
274
+ @attr .s ( init = False , auto_attribs = True )
256
275
class MarkDecorator :
257
276
"""A decorator for applying a mark on test functions and classes.
258
277
259
- MarkDecorators are created with ``pytest.mark``::
278
+ `` MarkDecorators`` are created with ``pytest.mark``::
260
279
261
280
mark1 = pytest.mark.NAME # Simple MarkDecorator
262
281
mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator
@@ -267,7 +286,7 @@ class MarkDecorator:
267
286
def test_function():
268
287
pass
269
288
270
- When a MarkDecorator is called it does the following:
289
+ When a `` MarkDecorator`` is called, it does the following:
271
290
272
291
1. If called with a single class as its only positional argument and no
273
292
additional keyword arguments, it attaches the mark to the class so it
@@ -276,19 +295,24 @@ def test_function():
276
295
2. If called with a single function as its only positional argument and
277
296
no additional keyword arguments, it attaches the mark to the function,
278
297
containing all the arguments already stored internally in the
279
- MarkDecorator.
298
+ `` MarkDecorator`` .
280
299
281
- 3. When called in any other case, it returns a new MarkDecorator instance
282
- with the original MarkDecorator's content updated with the arguments
283
- passed to this call.
300
+ 3. When called in any other case, it returns a new `` MarkDecorator``
301
+ instance with the original `` MarkDecorator`` 's content updated with
302
+ the arguments passed to this call.
284
303
285
- Note: The rules above prevent MarkDecorators from storing only a single
286
- function or class reference as their positional argument with no
304
+ Note: The rules above prevent a ``MarkDecorator`` from storing only a
305
+ single function or class reference as its positional argument with no
287
306
additional keyword or positional arguments. You can work around this by
288
307
using `with_args()`.
289
308
"""
290
309
291
- mark = attr .ib (type = Mark , validator = attr .validators .instance_of (Mark ))
310
+ mark : Mark
311
+
312
+ def __init__ (self , mark : Mark , * , _ispytest : bool = False ) -> None :
313
+ """:meta private:"""
314
+ check_ispytest (_ispytest )
315
+ self .mark = mark
292
316
293
317
@property
294
318
def name (self ) -> str :
@@ -307,6 +331,7 @@ def kwargs(self) -> Mapping[str, Any]:
307
331
308
332
@property
309
333
def markname (self ) -> str :
334
+ """:meta private:"""
310
335
return self .name # for backward-compat (2.4.1 had this attr)
311
336
312
337
def __repr__ (self ) -> str :
@@ -317,17 +342,15 @@ def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator":
317
342
318
343
Unlike calling the MarkDecorator, with_args() can be used even
319
344
if the sole argument is a callable/class.
320
-
321
- :rtype: MarkDecorator
322
345
"""
323
- mark = Mark (self .name , args , kwargs )
324
- return self . __class__ (self .mark .combined_with (mark ))
346
+ mark = Mark (self .name , args , kwargs , _ispytest = True )
347
+ return MarkDecorator (self .mark .combined_with (mark ), _ispytest = True )
325
348
326
349
# Type ignored because the overloads overlap with an incompatible
327
350
# return type. Not much we can do about that. Thankfully mypy picks
328
351
# the first match so it works out even if we break the rules.
329
352
@overload
330
- def __call__ (self , arg : _Markable ) -> _Markable : # type: ignore[misc]
353
+ def __call__ (self , arg : Markable ) -> Markable : # type: ignore[misc]
331
354
pass
332
355
333
356
@overload
@@ -386,7 +409,7 @@ def store_mark(obj, mark: Mark) -> None:
386
409
387
410
class _SkipMarkDecorator (MarkDecorator ):
388
411
@overload # type: ignore[override,misc]
389
- def __call__ (self , arg : _Markable ) -> _Markable :
412
+ def __call__ (self , arg : Markable ) -> Markable :
390
413
...
391
414
392
415
@overload
@@ -404,7 +427,7 @@ def __call__( # type: ignore[override]
404
427
405
428
class _XfailMarkDecorator (MarkDecorator ):
406
429
@overload # type: ignore[override,misc]
407
- def __call__ (self , arg : _Markable ) -> _Markable :
430
+ def __call__ (self , arg : Markable ) -> Markable :
408
431
...
409
432
410
433
@overload
@@ -465,9 +488,6 @@ def test_function():
465
488
applies a 'slowtest' :class:`Mark` on ``test_function``.
466
489
"""
467
490
468
- _config : Optional [Config ] = None
469
- _markers : Set [str ] = set ()
470
-
471
491
# See TYPE_CHECKING above.
472
492
if TYPE_CHECKING :
473
493
skip : _SkipMarkDecorator
@@ -477,7 +497,13 @@ def test_function():
477
497
usefixtures : _UsefixturesMarkDecorator
478
498
filterwarnings : _FilterwarningsMarkDecorator
479
499
500
+ def __init__ (self , * , _ispytest : bool = False ) -> None :
501
+ check_ispytest (_ispytest )
502
+ self ._config : Optional [Config ] = None
503
+ self ._markers : Set [str ] = set ()
504
+
480
505
def __getattr__ (self , name : str ) -> MarkDecorator :
506
+ """Generate a new :class:`MarkDecorator` with the given name."""
481
507
if name [0 ] == "_" :
482
508
raise AttributeError ("Marker name must NOT start with underscore" )
483
509
@@ -515,10 +541,10 @@ def __getattr__(self, name: str) -> MarkDecorator:
515
541
2 ,
516
542
)
517
543
518
- return MarkDecorator (Mark (name , (), {}) )
544
+ return MarkDecorator (Mark (name , (), {}, _ispytest = True ), _ispytest = True )
519
545
520
546
521
- MARK_GEN = MarkGenerator ()
547
+ MARK_GEN = MarkGenerator (_ispytest = True )
522
548
523
549
524
550
@final
0 commit comments