mark: export pytest.MarkDecorator for typing purposes
The type cannot be constructed directly, but is exported for use in type annotations, since it is reachable through existing public API.
This commit is contained in:
parent
2ec372df8b
commit
69c302479e
|
@ -1,5 +1,6 @@
|
||||||
Directly constructing the following classes is now deprecated:
|
Directly constructing the following classes is now deprecated:
|
||||||
|
|
||||||
- ``_pytest.mark.structures.Mark``
|
- ``_pytest.mark.structures.Mark``
|
||||||
|
- ``_pytest.mark.structures.MarkDecorator``
|
||||||
|
|
||||||
These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0.
|
These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0.
|
||||||
|
|
|
@ -3,6 +3,7 @@ The types of objects used in pytest's API are now exported so they may be used i
|
||||||
The newly-exported types are:
|
The newly-exported types are:
|
||||||
|
|
||||||
- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
|
- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
|
||||||
|
- ``pytest.MarkDecorator`` for :class:`mark decorators <pytest.MarkDecorator>`.
|
||||||
|
|
||||||
Constructing them directly is not supported; they are only meant for use in type annotations.
|
Constructing them directly is not supported; they are only meant for use in type annotations.
|
||||||
Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0.
|
Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0.
|
||||||
|
|
|
@ -849,7 +849,7 @@ Item
|
||||||
MarkDecorator
|
MarkDecorator
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. autoclass:: _pytest.mark.MarkDecorator
|
.. autoclass:: pytest.MarkDecorator()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -551,7 +551,7 @@ class FixtureRequest:
|
||||||
on all function invocations.
|
on all function invocations.
|
||||||
|
|
||||||
:param marker:
|
:param marker:
|
||||||
A :py:class:`_pytest.mark.MarkDecorator` object created by a call
|
A :class:`pytest.MarkDecorator` object created by a call
|
||||||
to ``pytest.mark.NAME(...)``.
|
to ``pytest.mark.NAME(...)``.
|
||||||
"""
|
"""
|
||||||
self.node.add_marker(marker)
|
self.node.add_marker(marker)
|
||||||
|
|
|
@ -268,14 +268,14 @@ class Mark:
|
||||||
# A generic parameter designating an object to which a Mark may
|
# A generic parameter designating an object to which a Mark may
|
||||||
# be applied -- a test function (callable) or class.
|
# be applied -- a test function (callable) or class.
|
||||||
# Note: a lambda is not allowed, but this can't be represented.
|
# Note: a lambda is not allowed, but this can't be represented.
|
||||||
_Markable = TypeVar("_Markable", bound=Union[Callable[..., object], type])
|
Markable = TypeVar("Markable", bound=Union[Callable[..., object], type])
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s(init=False, auto_attribs=True)
|
||||||
class MarkDecorator:
|
class MarkDecorator:
|
||||||
"""A decorator for applying a mark on test functions and classes.
|
"""A decorator for applying a mark on test functions and classes.
|
||||||
|
|
||||||
MarkDecorators are created with ``pytest.mark``::
|
``MarkDecorators`` are created with ``pytest.mark``::
|
||||||
|
|
||||||
mark1 = pytest.mark.NAME # Simple MarkDecorator
|
mark1 = pytest.mark.NAME # Simple MarkDecorator
|
||||||
mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator
|
mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator
|
||||||
|
@ -286,7 +286,7 @@ class MarkDecorator:
|
||||||
def test_function():
|
def test_function():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
When a MarkDecorator is called it does the following:
|
When a ``MarkDecorator`` is called, it does the following:
|
||||||
|
|
||||||
1. If called with a single class as its only positional argument and no
|
1. If called with a single class as its only positional argument and no
|
||||||
additional keyword arguments, it attaches the mark to the class so it
|
additional keyword arguments, it attaches the mark to the class so it
|
||||||
|
@ -295,19 +295,24 @@ class MarkDecorator:
|
||||||
2. If called with a single function as its only positional argument and
|
2. If called with a single function as its only positional argument and
|
||||||
no additional keyword arguments, it attaches the mark to the function,
|
no additional keyword arguments, it attaches the mark to the function,
|
||||||
containing all the arguments already stored internally in the
|
containing all the arguments already stored internally in the
|
||||||
MarkDecorator.
|
``MarkDecorator``.
|
||||||
|
|
||||||
3. When called in any other case, it returns a new MarkDecorator instance
|
3. When called in any other case, it returns a new ``MarkDecorator``
|
||||||
with the original MarkDecorator's content updated with the arguments
|
instance with the original ``MarkDecorator``'s content updated with
|
||||||
passed to this call.
|
the arguments passed to this call.
|
||||||
|
|
||||||
Note: The rules above prevent MarkDecorators from storing only a single
|
Note: The rules above prevent a ``MarkDecorator`` from storing only a
|
||||||
function or class reference as their positional argument with no
|
single function or class reference as its positional argument with no
|
||||||
additional keyword or positional arguments. You can work around this by
|
additional keyword or positional arguments. You can work around this by
|
||||||
using `with_args()`.
|
using `with_args()`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mark = attr.ib(type=Mark, validator=attr.validators.instance_of(Mark))
|
mark: Mark
|
||||||
|
|
||||||
|
def __init__(self, mark: Mark, *, _ispytest: bool = False) -> None:
|
||||||
|
""":meta private:"""
|
||||||
|
check_ispytest(_ispytest)
|
||||||
|
self.mark = mark
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> str:
|
def name(self) -> str:
|
||||||
|
@ -326,6 +331,7 @@ class MarkDecorator:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def markname(self) -> str:
|
def markname(self) -> str:
|
||||||
|
""":meta private:"""
|
||||||
return self.name # for backward-compat (2.4.1 had this attr)
|
return self.name # for backward-compat (2.4.1 had this attr)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
@ -336,17 +342,15 @@ class MarkDecorator:
|
||||||
|
|
||||||
Unlike calling the MarkDecorator, with_args() can be used even
|
Unlike calling the MarkDecorator, with_args() can be used even
|
||||||
if the sole argument is a callable/class.
|
if the sole argument is a callable/class.
|
||||||
|
|
||||||
:rtype: MarkDecorator
|
|
||||||
"""
|
"""
|
||||||
mark = Mark(self.name, args, kwargs, _ispytest=True)
|
mark = Mark(self.name, args, kwargs, _ispytest=True)
|
||||||
return self.__class__(self.mark.combined_with(mark))
|
return MarkDecorator(self.mark.combined_with(mark), _ispytest=True)
|
||||||
|
|
||||||
# Type ignored because the overloads overlap with an incompatible
|
# Type ignored because the overloads overlap with an incompatible
|
||||||
# return type. Not much we can do about that. Thankfully mypy picks
|
# return type. Not much we can do about that. Thankfully mypy picks
|
||||||
# the first match so it works out even if we break the rules.
|
# the first match so it works out even if we break the rules.
|
||||||
@overload
|
@overload
|
||||||
def __call__(self, arg: _Markable) -> _Markable: # type: ignore[misc]
|
def __call__(self, arg: Markable) -> Markable: # type: ignore[misc]
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
@ -405,7 +409,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class _SkipMarkDecorator(MarkDecorator):
|
class _SkipMarkDecorator(MarkDecorator):
|
||||||
@overload # type: ignore[override,misc]
|
@overload # type: ignore[override,misc]
|
||||||
def __call__(self, arg: _Markable) -> _Markable:
|
def __call__(self, arg: Markable) -> Markable:
|
||||||
...
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
@ -423,7 +427,7 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class _XfailMarkDecorator(MarkDecorator):
|
class _XfailMarkDecorator(MarkDecorator):
|
||||||
@overload # type: ignore[override,misc]
|
@overload # type: ignore[override,misc]
|
||||||
def __call__(self, arg: _Markable) -> _Markable:
|
def __call__(self, arg: Markable) -> Markable:
|
||||||
...
|
...
|
||||||
|
|
||||||
@overload
|
@overload
|
||||||
|
@ -534,7 +538,7 @@ class MarkGenerator:
|
||||||
2,
|
2,
|
||||||
)
|
)
|
||||||
|
|
||||||
return MarkDecorator(Mark(name, (), {}, _ispytest=True))
|
return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True)
|
||||||
|
|
||||||
|
|
||||||
MARK_GEN = MarkGenerator()
|
MARK_GEN = MarkGenerator()
|
||||||
|
|
|
@ -23,6 +23,7 @@ from _pytest.logging import LogCaptureFixture
|
||||||
from _pytest.main import Session
|
from _pytest.main import Session
|
||||||
from _pytest.mark import Mark
|
from _pytest.mark import Mark
|
||||||
from _pytest.mark import MARK_GEN as mark
|
from _pytest.mark import MARK_GEN as mark
|
||||||
|
from _pytest.mark import MarkDecorator
|
||||||
from _pytest.mark import param
|
from _pytest.mark import param
|
||||||
from _pytest.monkeypatch import MonkeyPatch
|
from _pytest.monkeypatch import MonkeyPatch
|
||||||
from _pytest.nodes import Collector
|
from _pytest.nodes import Collector
|
||||||
|
@ -91,6 +92,7 @@ __all__ = [
|
||||||
"main",
|
"main",
|
||||||
"mark",
|
"mark",
|
||||||
"Mark",
|
"Mark",
|
||||||
|
"MarkDecorator",
|
||||||
"Module",
|
"Module",
|
||||||
"MonkeyPatch",
|
"MonkeyPatch",
|
||||||
"Package",
|
"Package",
|
||||||
|
|
Loading…
Reference in New Issue