Skip to content

Commit 1e7eb20

Browse files
committed
perf(expression): improve string lexing & error messages
1 parent f489739 commit 1e7eb20

File tree

2 files changed

+30
-21
lines changed

2 files changed

+30
-21
lines changed

src/_pytest/mark/expression.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,18 @@ def lex(self, input: str) -> Iterator[Token]:
9999
elif input[pos] == ",":
100100
yield Token(TokenType.COMMA, ",", pos)
101101
pos += 1
102-
elif (quote_char := input[pos]) == "'" or input[pos] == '"':
103-
quote_position = input[pos + 1 :].find(quote_char)
104-
if quote_position == -1:
102+
elif (quote_char := input[pos]) in ("'", '"'):
103+
end_quote_pos = input.find(quote_char, pos + 1)
104+
if end_quote_pos == -1:
105105
raise ParseError(
106106
pos + 1,
107107
f'closing quote "{quote_char}" is missing',
108108
)
109-
value = input[pos : pos + 2 + quote_position]
110-
if "\\" in value:
109+
value = input[pos : end_quote_pos + 1]
110+
if (backslash_pos := input.find("\\")) != -1:
111111
raise ParseError(
112-
pos + 1,
113-
"escaping not supported in marker expression",
112+
backslash_pos + 1,
113+
r'escaping with "\" not supported in marker expression',
114114
)
115115
yield Token(TokenType.STRING, value, pos)
116116
pos += len(value)
@@ -218,10 +218,15 @@ def not_expr(s: Scanner) -> ast.expr:
218218

219219
def single_kwarg(s: Scanner) -> ast.keyword:
220220
keyword_name = s.accept(TokenType.IDENT, reject=True)
221-
if not keyword_name.value.isidentifier() or keyword.iskeyword(keyword_name.value):
221+
if not keyword_name.value.isidentifier():
222+
raise ParseError(
223+
keyword_name.pos + 1,
224+
f"not a valid python identifier {keyword_name.value}",
225+
)
226+
if keyword.iskeyword(keyword_name.value):
222227
raise ParseError(
223228
keyword_name.pos + 1,
224-
f'unexpected character/s "{keyword_name.value}"',
229+
f"unexpected reserved python keyword `{keyword_name.value}`",
225230
)
226231
s.accept(TokenType.EQUAL, reject=True)
227232

testing/test_mark_expression.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -207,25 +207,29 @@ def test_invalid_idents(ident: str) -> None:
207207
@pytest.mark.parametrize(
208208
"expr, expected_error_msg",
209209
(
210-
("mark(1=2)", 'unexpected character/s "1"'),
211-
("mark(/=2)", 'unexpected character/s "/"'),
212-
("mark(True=False)", 'unexpected character/s "True"'),
213-
("mark(def=False)", 'unexpected character/s "def"'),
214-
("mark(class=False)", 'unexpected character/s "class"'),
215-
("mark(if=False)", 'unexpected character/s "if"'),
216-
("mark(else=False)", 'unexpected character/s "else"'),
217-
("mark(1)", 'unexpected character/s "1"'),
218-
("mark(var:=False", 'unexpected character/s "var:"'),
219-
("mark(valid=False, def=1)", 'unexpected character/s "def"'),
210+
("mark(True=False)", "unexpected reserved python keyword `True`"),
211+
("mark(def=False)", "unexpected reserved python keyword `def`"),
212+
("mark(class=False)", "unexpected reserved python keyword `class`"),
213+
("mark(if=False)", "unexpected reserved python keyword `if`"),
214+
("mark(else=False)", "unexpected reserved python keyword `else`"),
215+
("mark(valid=False, def=1)", "unexpected reserved python keyword `def`"),
216+
("mark(1)", "not a valid python identifier 1"),
217+
("mark(var:=False", "not a valid python identifier var:"),
218+
("mark(1=2)", "not a valid python identifier 1"),
219+
("mark(/=2)", "not a valid python identifier /"),
220220
("mark(var==", "expected identifier; got ="),
221+
("mark(var)", "expected =; got right parenthesis"),
221222
("mark(var=none)", 'unexpected character/s "none"'),
222223
("mark(var=1.1)", 'unexpected character/s "1.1"'),
223-
("mark(var)", "expected =; got right parenthesis"),
224224
("mark(var=')", """closing quote "'" is missing"""),
225225
('mark(var=")', 'closing quote """ is missing'),
226226
("""mark(var="')""", 'closing quote """ is missing'),
227227
("""mark(var='")""", """closing quote "'" is missing"""),
228-
(r"mark(var='\hugo')", "escaping not supported in marker expression"),
228+
(
229+
r"mark(var='\hugo')",
230+
r'escaping with "\\" not supported in marker expression',
231+
),
232+
("mark(empty_list=[])", r'unexpected character/s "\[\]"'),
229233
),
230234
)
231235
def test_invalid_kwarg_name_or_value( # TODO: move to `test_syntax_errors` ?

0 commit comments

Comments
 (0)