Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions changelog/8983.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The test selection options ``pytest -k`` and ``pytest -m`` now support matching names containing backslash (`\\`) characters.
Backslashes are treated literally, not as escape characters (the values being matched against are already escaped).
4 changes: 2 additions & 2 deletions src/_pytest/mark/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
expr: and_expr ('or' and_expr)*
and_expr: not_expr ('and' not_expr)*
not_expr: 'not' not_expr | '(' expr ')' | ident
ident: (\w|:|\+|-|\.|\[|\])+
ident: (\w|:|\+|-|\.|\[|\]|\\)+

The semantics are:

Expand Down Expand Up @@ -88,7 +88,7 @@ def lex(self, input: str) -> Iterator[Token]:
yield Token(TokenType.RPAREN, ")", pos)
pos += 1
else:
match = re.match(r"(:?\w|:|\+|-|\.|\[|\])+", input[pos:])
match = re.match(r"(:?\w|:|\+|-|\.|\[|\]|\\)+", input[pos:])
if match:
value = match.group(0)
if value == "or":
Expand Down
16 changes: 15 additions & 1 deletion testing/test_mark_expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,20 @@ def test_syntax_oddeties(expr: str, expected: bool) -> None:
assert evaluate(expr, matcher) is expected


def test_backslash_not_treated_specially() -> None:
r"""When generating nodeids, if the source name contains special characters
like a newline, they are escaped into two characters like \n. Therefore, a
user will never need to insert a literal newline, only \n (two chars). So
mark expressions themselves do not support escaping, instead they treat
backslashes as regular identifier characters."""
matcher = {r"\nfoo\n"}.__contains__

assert evaluate(r"\nfoo\n", matcher)
assert not evaluate(r"foo", matcher)
with pytest.raises(ParseError):
evaluate("\nfoo\n", matcher)


@pytest.mark.parametrize(
("expr", "column", "message"),
(
Expand Down Expand Up @@ -129,6 +143,7 @@ def test_syntax_errors(expr: str, column: int, message: str) -> None:
":::",
"a:::c",
"a+-b",
r"\nhe\\l\lo\n\t\rbye",
"אבגד",
"aaאבגדcc",
"a[bcd]",
Expand Down Expand Up @@ -156,7 +171,6 @@ def test_valid_idents(ident: str) -> None:
"ident",
(
"/",
"\\",
"^",
"*",
"=",
Expand Down