This commit is contained in:
Max Berkowitz 2024-06-20 10:47:51 +02:00 committed by GitHub
commit f91f6f470c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 24 additions and 10 deletions

View File

@ -271,6 +271,7 @@ Matt Bachmann
Matt Duck
Matt Williams
Matthias Hafner
Max Berkowitz
Maxim Filipenko
Maximilian Cosmo Sitter
mbyt

View File

@ -0,0 +1 @@
Emit a warning when unregistered marks are used with the command line option -m.

View File

@ -236,7 +236,7 @@ def deselect_by_mark(items: "List[Item]", config: Config) -> None:
if not matchexpr:
return
expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'")
expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'", True)
remaining: List[Item] = []
deselected: List[Item] = []
for item in items:
@ -249,9 +249,9 @@ def deselect_by_mark(items: "List[Item]", config: Config) -> None:
items[:] = remaining
def _parse_expression(expr: str, exc_message: str) -> Expression:
def _parse_expression(expr: str, exc_message: str, mark: bool = False) -> Expression:
try:
return Expression.compile(expr)
return Expression.compile(expr, mark)
except ParseError as e:
raise UsageError(f"{exc_message}: {expr}: {e}") from None

View File

@ -27,6 +27,8 @@ from typing import NoReturn
from typing import Optional
from typing import Sequence
from .structures import MARK_GEN
__all__ = [
"Expression",
@ -197,12 +199,19 @@ class Expression:
self.code = code
@classmethod
def compile(self, input: str) -> "Expression":
def compile(self, input: str, mark: bool = False) -> "Expression":
"""Compile a match expression.
:param input: The input expression - one line.
"""
astexpr = expression(Scanner(input))
if mark:
for node in ast.walk(astexpr):
# if the node is an identifier, i.e. a mark name
if isinstance(node, ast.Name):
MARK_GEN.verify_mark(node.id[len(IDENT_PREFIX) :])
code: types.CodeType = compile(
astexpr,
filename="<pytest match expression>",

View File

@ -520,11 +520,7 @@ class MarkGenerator:
self._config: Optional[Config] = None
self._markers: Set[str] = set()
def __getattr__(self, name: str) -> MarkDecorator:
"""Generate a new :class:`MarkDecorator` with the given name."""
if name[0] == "_":
raise AttributeError("Marker name must NOT start with underscore")
def verify_mark(self, name: str) -> None:
if self._config is not None:
# We store a set of markers as a performance optimisation - if a mark
# name is in the set we definitely know it, but a mark may be known and
@ -556,9 +552,16 @@ class MarkGenerator:
"custom marks to avoid this warning - for details, see "
"https://docs.pytest.org/en/stable/how-to/mark.html",
PytestUnknownMarkWarning,
2,
3,
)
def __getattr__(self, name: str) -> MarkDecorator:
"""Generate a new :class:`MarkDecorator` with the given name."""
if name[0] == "_":
raise AttributeError("Marker name must NOT start with underscore")
self.verify_mark(name)
return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True)