Skip to content

Type annotate pytest.mark.{skip,skipif,xfail,parametrize} #7379

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 90 additions & 10 deletions src/_pytest/mark/structures.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,19 @@ def get_empty_parameterset_mark(
) -> "MarkDecorator":
from ..nodes import Collector

fs, lineno = getfslineno(func)
reason = "got empty parameter set %r, function %s at %s:%d" % (
argnames,
func.__name__,
fs,
lineno,
)

requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION)
if requested_mark in ("", None, "skip"):
mark = MARK_GEN.skip
mark = MARK_GEN.skip(reason=reason)
elif requested_mark == "xfail":
mark = MARK_GEN.xfail(run=False)
mark = MARK_GEN.xfail(reason=reason, run=False)
elif requested_mark == "fail_at_collect":
f_name = func.__name__
_, lineno = getfslineno(func)
Expand All @@ -59,14 +67,7 @@ def get_empty_parameterset_mark(
)
else:
raise LookupError(requested_mark)
fs, lineno = getfslineno(func)
reason = "got empty parameter set %r, function %s at %s:%d" % (
argnames,
func.__name__,
fs,
lineno,
)
return mark(reason=reason)
return mark


class ParameterSet(
Expand Down Expand Up @@ -379,6 +380,76 @@ def store_mark(obj, mark: Mark) -> None:
obj.pytestmark = get_unpacked_marks(obj) + [mark]


# Typing for builtin pytest marks. This is cheating; it gives builtin marks
# special privilege, and breaks modularity. But practicality beats purity...
if TYPE_CHECKING:
from _pytest.fixtures import _Scope

class _SkipMarkDecorator(MarkDecorator):
@overload # type: ignore[override,misc]
def __call__(self, arg: _Markable) -> _Markable:
raise NotImplementedError()

@overload # noqa: F811
def __call__(self, reason: str = ...) -> "MarkDecorator": # noqa: F811
raise NotImplementedError()

class _SkipifMarkDecorator(MarkDecorator):
def __call__( # type: ignore[override]
self,
condition: Union[str, bool] = ...,
*conditions: Union[str, bool],
reason: str = ...
) -> MarkDecorator:
raise NotImplementedError()

class _XfailMarkDecorator(MarkDecorator):
@overload # type: ignore[override,misc]
def __call__(self, arg: _Markable) -> _Markable:
raise NotImplementedError()

@overload # noqa: F811
def __call__( # noqa: F811
self,
condition: Union[str, bool] = ...,
*conditions: Union[str, bool],
reason: str = ...,
run: bool = ...,
raises: Union[BaseException, Tuple[BaseException, ...]] = ...,
strict: bool = ...
) -> MarkDecorator:
raise NotImplementedError()

class _ParametrizeMarkDecorator(MarkDecorator):
def __call__( # type: ignore[override]
self,
argnames: Union[str, List[str], Tuple[str, ...]],
argvalues: Iterable[Union[ParameterSet, Sequence[object], object]],
*,
indirect: Union[bool, Sequence[str]] = ...,
ids: Optional[
Union[
Iterable[Union[None, str, float, int, bool]],
Callable[[object], Optional[object]],
]
] = ...,
scope: Optional[_Scope] = ...
) -> MarkDecorator:
raise NotImplementedError()

class _UsefixturesMarkDecorator(MarkDecorator):
def __call__( # type: ignore[override]
self, *fixtures: str
) -> MarkDecorator:
raise NotImplementedError()

class _FilterwarningsMarkDecorator(MarkDecorator):
def __call__( # type: ignore[override]
self, *filters: str
) -> MarkDecorator:
raise NotImplementedError()


class MarkGenerator:
"""Factory for :class:`MarkDecorator` objects - exposed as
a ``pytest.mark`` singleton instance.
Expand All @@ -397,6 +468,15 @@ def test_function():
_config = None # type: Optional[Config]
_markers = set() # type: Set[str]

# See TYPE_CHECKING above.
if TYPE_CHECKING:
skip = None # type: _SkipMarkDecorator
skipif = None # type: _SkipifMarkDecorator
xfail = None # type: _XfailMarkDecorator
parametrize = None # type: _ParametrizeMarkDecorator
usefixtures = None # type: _UsefixturesMarkDecorator
filterwarnings = None # type: _FilterwarningsMarkDecorator

def __getattr__(self, name: str) -> MarkDecorator:
if name[0] == "_":
raise AttributeError("Marker name must NOT start with underscore")
Expand Down