summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRan Benita <ran@unusedvar.com>2020-12-19 22:19:51 +0200
committerRan Benita <ran@unusedvar.com>2020-12-20 15:58:49 +0200
commit2ec372df8b987207efc4ad0f33c2f82df5c9e2e5 (patch)
tree3fe6281b0551a9a703e309d54efe4810bd94c4ee
parent89dcfbf293802b6efacc5aea2893f9fec03787bc (diff)
downloadpytest-2ec372df8b987207efc4ad0f33c2f82df5c9e2e5.tar.gz
mark: export pytest.Mark 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.
-rw-r--r--changelog/7469.deprecation.rst5
-rw-r--r--changelog/7469.feature.rst10
-rw-r--r--doc/en/reference.rst4
-rw-r--r--src/_pytest/mark/structures.py39
-rw-r--r--src/pytest/__init__.py2
5 files changed, 48 insertions, 12 deletions
diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst
new file mode 100644
index 000000000..79419dace
--- /dev/null
+++ b/changelog/7469.deprecation.rst
@@ -0,0 +1,5 @@
+Directly constructing the following classes is now deprecated:
+
+- ``_pytest.mark.structures.Mark``
+
+These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0.
diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst
new file mode 100644
index 000000000..f4bc52414
--- /dev/null
+++ b/changelog/7469.feature.rst
@@ -0,0 +1,10 @@
+The types of objects used in pytest's API are now exported so they may be used in type annotations.
+
+The newly-exported types are:
+
+- ``pytest.Mark`` for :class:`marks <pytest.Mark>`.
+
+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.
+
+Subclassing them is also not supported. This is not currently enforced at runtime, but is detected by type-checkers such as mypy.
diff --git a/doc/en/reference.rst b/doc/en/reference.rst
index 8aa95ca64..3fc62ee72 100644
--- a/doc/en/reference.rst
+++ b/doc/en/reference.rst
@@ -239,7 +239,7 @@ For example:
def test_function():
...
-Will create and attach a :class:`Mark <_pytest.mark.structures.Mark>` object to the collected
+Will create and attach a :class:`Mark <pytest.Mark>` object to the collected
:class:`Item <pytest.Item>`, which can then be accessed by fixtures or hooks with
:meth:`Node.iter_markers <_pytest.nodes.Node.iter_markers>`. The ``mark`` object will have the following attributes:
@@ -863,7 +863,7 @@ MarkGenerator
Mark
~~~~
-.. autoclass:: _pytest.mark.structures.Mark
+.. autoclass:: pytest.Mark()
:members:
diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py
index 6c126cf4a..29b958687 100644
--- a/src/_pytest/mark/structures.py
+++ b/src/_pytest/mark/structures.py
@@ -28,6 +28,7 @@ from ..compat import final
from ..compat import NOTSET
from ..compat import NotSetType
from _pytest.config import Config
+from _pytest.deprecated import check_ispytest
from _pytest.outcomes import fail
from _pytest.warning_types import PytestUnknownMarkWarning
@@ -200,21 +201,38 @@ class ParameterSet(
@final
-@attr.s(frozen=True)
+@attr.s(frozen=True, init=False, auto_attribs=True)
class Mark:
#: Name of the mark.
- name = attr.ib(type=str)
+ name: str
#: Positional arguments of the mark decorator.
- args = attr.ib(type=Tuple[Any, ...])
+ args: Tuple[Any, ...]
#: Keyword arguments of the mark decorator.
- kwargs = attr.ib(type=Mapping[str, Any])
+ kwargs: Mapping[str, Any]
#: Source Mark for ids with parametrize Marks.
- _param_ids_from = attr.ib(type=Optional["Mark"], default=None, repr=False)
+ _param_ids_from: Optional["Mark"] = attr.ib(default=None, repr=False)
#: Resolved/generated ids with parametrize Marks.
- _param_ids_generated = attr.ib(
- type=Optional[Sequence[str]], default=None, repr=False
- )
+ _param_ids_generated: Optional[Sequence[str]] = attr.ib(default=None, repr=False)
+
+ def __init__(
+ self,
+ name: str,
+ args: Tuple[Any, ...],
+ kwargs: Mapping[str, Any],
+ param_ids_from: Optional["Mark"] = None,
+ param_ids_generated: Optional[Sequence[str]] = None,
+ *,
+ _ispytest: bool = False,
+ ) -> None:
+ """:meta private:"""
+ check_ispytest(_ispytest)
+ # Weirdness to bypass frozen=True.
+ object.__setattr__(self, "name", name)
+ object.__setattr__(self, "args", args)
+ object.__setattr__(self, "kwargs", kwargs)
+ object.__setattr__(self, "_param_ids_from", param_ids_from)
+ object.__setattr__(self, "_param_ids_generated", param_ids_generated)
def _has_param_ids(self) -> bool:
return "ids" in self.kwargs or len(self.args) >= 4
@@ -243,6 +261,7 @@ class Mark:
self.args + other.args,
dict(self.kwargs, **other.kwargs),
param_ids_from=param_ids_from,
+ _ispytest=True,
)
@@ -320,7 +339,7 @@ class MarkDecorator:
:rtype: MarkDecorator
"""
- mark = Mark(self.name, args, kwargs)
+ mark = Mark(self.name, args, kwargs, _ispytest=True)
return self.__class__(self.mark.combined_with(mark))
# Type ignored because the overloads overlap with an incompatible
@@ -515,7 +534,7 @@ class MarkGenerator:
2,
)
- return MarkDecorator(Mark(name, (), {}))
+ return MarkDecorator(Mark(name, (), {}, _ispytest=True))
MARK_GEN = MarkGenerator()
diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py
index 70177f950..4b194e0c8 100644
--- a/src/pytest/__init__.py
+++ b/src/pytest/__init__.py
@@ -21,6 +21,7 @@ from _pytest.fixtures import yield_fixture
from _pytest.freeze_support import freeze_includes
from _pytest.logging import LogCaptureFixture
from _pytest.main import Session
+from _pytest.mark import Mark
from _pytest.mark import MARK_GEN as mark
from _pytest.mark import param
from _pytest.monkeypatch import MonkeyPatch
@@ -89,6 +90,7 @@ __all__ = [
"LogCaptureFixture",
"main",
"mark",
+ "Mark",
"Module",
"MonkeyPatch",
"Package",