Merge pull request #7039 from bluetech/markdecorator-doc
Slightly improve Mark and MarkDecorator documentation
This commit is contained in:
commit
272be7ef74
|
@ -2,10 +2,14 @@ import inspect
|
||||||
import warnings
|
import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from collections.abc import MutableMapping
|
from collections.abc import MutableMapping
|
||||||
|
from typing import Any
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import Mapping
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from typing import Sequence
|
||||||
from typing import Set
|
from typing import Set
|
||||||
|
from typing import Tuple
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
@ -140,28 +144,32 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
|
||||||
|
|
||||||
@attr.s(frozen=True)
|
@attr.s(frozen=True)
|
||||||
class Mark:
|
class Mark:
|
||||||
#: name of the mark
|
#: Name of the mark.
|
||||||
name = attr.ib(type=str)
|
name = attr.ib(type=str)
|
||||||
#: positional arguments of the mark decorator
|
#: Positional arguments of the mark decorator.
|
||||||
args = attr.ib() # List[object]
|
args = attr.ib(type=Tuple[Any, ...])
|
||||||
#: keyword arguments of the mark decorator
|
#: Keyword arguments of the mark decorator.
|
||||||
kwargs = attr.ib() # Dict[str, object]
|
kwargs = attr.ib(type=Mapping[str, Any])
|
||||||
|
|
||||||
#: source Mark for ids with parametrize Marks
|
#: Source Mark for ids with parametrize Marks.
|
||||||
_param_ids_from = attr.ib(type=Optional["Mark"], default=None, repr=False)
|
_param_ids_from = attr.ib(type=Optional["Mark"], default=None, repr=False)
|
||||||
#: resolved/generated ids with parametrize Marks
|
#: Resolved/generated ids with parametrize Marks.
|
||||||
_param_ids_generated = attr.ib(type=Optional[List[str]], default=None, repr=False)
|
_param_ids_generated = attr.ib(
|
||||||
|
type=Optional[Sequence[str]], default=None, repr=False
|
||||||
|
)
|
||||||
|
|
||||||
def _has_param_ids(self):
|
def _has_param_ids(self) -> bool:
|
||||||
return "ids" in self.kwargs or len(self.args) >= 4
|
return "ids" in self.kwargs or len(self.args) >= 4
|
||||||
|
|
||||||
def combined_with(self, other: "Mark") -> "Mark":
|
def combined_with(self, other: "Mark") -> "Mark":
|
||||||
"""
|
"""Return a new Mark which is a combination of this
|
||||||
:param other: the mark to combine with
|
Mark and another Mark.
|
||||||
|
|
||||||
|
Combines by appending args and merging kwargs.
|
||||||
|
|
||||||
|
:param other: The mark to combine with.
|
||||||
:type other: Mark
|
:type other: Mark
|
||||||
:rtype: Mark
|
:rtype: Mark
|
||||||
|
|
||||||
combines by appending args and merging the mappings
|
|
||||||
"""
|
"""
|
||||||
assert self.name == other.name
|
assert self.name == other.name
|
||||||
|
|
||||||
|
@ -183,11 +191,12 @@ class Mark:
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
class MarkDecorator:
|
class MarkDecorator:
|
||||||
""" A decorator for test functions and test classes. When applied
|
"""A decorator for applying a mark on test functions and classes.
|
||||||
it will create :class:`Mark` objects which are often created like this::
|
|
||||||
|
|
||||||
mark1 = pytest.mark.NAME # simple MarkDecorator
|
MarkDecorators are created with ``pytest.mark``::
|
||||||
mark2 = pytest.mark.NAME(name1=value) # parametrized MarkDecorator
|
|
||||||
|
mark1 = pytest.mark.NAME # Simple MarkDecorator
|
||||||
|
mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator
|
||||||
|
|
||||||
and can then be applied as decorators to test functions::
|
and can then be applied as decorators to test functions::
|
||||||
|
|
||||||
|
@ -195,64 +204,64 @@ class MarkDecorator:
|
||||||
def test_function():
|
def test_function():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
When a MarkDecorator instance 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 itself to the class so it
|
additional keyword arguments, it attaches the mark to the class so it
|
||||||
gets applied automatically to all test cases found in that class.
|
gets applied automatically to all test cases found in that class.
|
||||||
|
|
||||||
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 a MarkInfo object to the
|
no additional keyword arguments, it attaches the mark to the function,
|
||||||
function, containing all the arguments already stored internally in
|
containing all the arguments already stored internally in the
|
||||||
the MarkDecorator.
|
MarkDecorator.
|
||||||
3. When called in any other case, it performs a 'fake construction' call,
|
|
||||||
i.e. it returns a new MarkDecorator instance with the original
|
|
||||||
MarkDecorator's content updated with the arguments passed to this
|
|
||||||
call.
|
|
||||||
|
|
||||||
Note: The rules above prevent MarkDecorator objects from storing only a
|
3. When called in any other case, it returns a new MarkDecorator instance
|
||||||
single function or class reference as their positional argument with no
|
with the original MarkDecorator's content updated with the arguments
|
||||||
additional keyword or positional arguments.
|
passed to this call.
|
||||||
|
|
||||||
|
Note: The rules above prevent MarkDecorators from storing only a single
|
||||||
|
function or class reference as their positional argument with no
|
||||||
|
additional keyword or positional arguments. You can work around this by
|
||||||
|
using `with_args()`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mark = attr.ib(validator=attr.validators.instance_of(Mark))
|
mark = attr.ib(type=Mark, validator=attr.validators.instance_of(Mark))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self):
|
def name(self) -> str:
|
||||||
"""alias for mark.name"""
|
"""Alias for mark.name."""
|
||||||
return self.mark.name
|
return self.mark.name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def args(self):
|
def args(self) -> Tuple[Any, ...]:
|
||||||
"""alias for mark.args"""
|
"""Alias for mark.args."""
|
||||||
return self.mark.args
|
return self.mark.args
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def kwargs(self):
|
def kwargs(self) -> Mapping[str, Any]:
|
||||||
"""alias for mark.kwargs"""
|
"""Alias for mark.kwargs."""
|
||||||
return self.mark.kwargs
|
return self.mark.kwargs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def markname(self):
|
def markname(self) -> str:
|
||||||
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):
|
def __repr__(self) -> str:
|
||||||
return "<MarkDecorator {!r}>".format(self.mark)
|
return "<MarkDecorator {!r}>".format(self.mark)
|
||||||
|
|
||||||
def with_args(self, *args, **kwargs):
|
def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator":
|
||||||
""" return a MarkDecorator with extra arguments added
|
"""Return a MarkDecorator with extra arguments added.
|
||||||
|
|
||||||
unlike call this can be used even if the sole argument is a callable/class
|
Unlike calling the MarkDecorator, with_args() can be used even
|
||||||
|
if the sole argument is a callable/class.
|
||||||
|
|
||||||
:return: MarkDecorator
|
:return: MarkDecorator
|
||||||
"""
|
"""
|
||||||
|
|
||||||
mark = Mark(self.name, args, kwargs)
|
mark = Mark(self.name, args, kwargs)
|
||||||
return self.__class__(self.mark.combined_with(mark))
|
return self.__class__(self.mark.combined_with(mark))
|
||||||
|
|
||||||
def __call__(self, *args, **kwargs):
|
def __call__(self, *args: object, **kwargs: object):
|
||||||
""" if passed a single callable argument: decorate it with mark info.
|
"""Call the MarkDecorator."""
|
||||||
otherwise add *args/**kwargs in-place to mark information. """
|
|
||||||
if args and not kwargs:
|
if args and not kwargs:
|
||||||
func = args[0]
|
func = args[0]
|
||||||
is_class = inspect.isclass(func)
|
is_class = inspect.isclass(func)
|
||||||
|
@ -288,27 +297,31 @@ def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List
|
||||||
return [x for x in extracted if isinstance(x, Mark)]
|
return [x for x in extracted if isinstance(x, Mark)]
|
||||||
|
|
||||||
|
|
||||||
def store_mark(obj, mark):
|
def store_mark(obj, mark: Mark) -> None:
|
||||||
"""store a Mark on an object
|
"""Store a Mark on an object.
|
||||||
this is used to implement the Mark declarations/decorators correctly
|
|
||||||
|
This is used to implement the Mark declarations/decorators correctly.
|
||||||
"""
|
"""
|
||||||
assert isinstance(mark, Mark), mark
|
assert isinstance(mark, Mark), mark
|
||||||
# always reassign name to avoid updating pytestmark
|
# Always reassign name to avoid updating pytestmark in a reference that
|
||||||
# in a reference that was only borrowed
|
# was only borrowed.
|
||||||
obj.pytestmark = get_unpacked_marks(obj) + [mark]
|
obj.pytestmark = get_unpacked_marks(obj) + [mark]
|
||||||
|
|
||||||
|
|
||||||
class MarkGenerator:
|
class MarkGenerator:
|
||||||
""" Factory for :class:`MarkDecorator` objects - exposed as
|
"""Factory for :class:`MarkDecorator` objects - exposed as
|
||||||
a ``pytest.mark`` singleton instance. Example::
|
a ``pytest.mark`` singleton instance.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.slowtest
|
@pytest.mark.slowtest
|
||||||
def test_function():
|
def test_function():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
will set a 'slowtest' :class:`MarkInfo` object
|
applies a 'slowtest' :class:`Mark` on ``test_function``.
|
||||||
on the ``test_function`` object. """
|
"""
|
||||||
|
|
||||||
_config = None
|
_config = None
|
||||||
_markers = set() # type: Set[str]
|
_markers = set() # type: Set[str]
|
||||||
|
|
Loading…
Reference in New Issue