Skip to content

Commit 888eb14

Browse files
authored
Improve the message around expecting a semicolon (#833)
This makes it clearer that another character can be used. Co-authored-by: Pradyun Gedam <[email protected]>
1 parent d08417c commit 888eb14

File tree

2 files changed

+33
-11
lines changed

2 files changed

+33
-11
lines changed

src/packaging/_parser.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ def _parse_requirement_details(
111111
return (url, specifier, marker)
112112

113113
marker = _parse_requirement_marker(
114-
tokenizer, span_start=url_start, after="URL and whitespace"
114+
tokenizer,
115+
span_start=url_start,
116+
expected="semicolon (after URL and whitespace)",
115117
)
116118
else:
117119
specifier_start = tokenizer.position
@@ -124,27 +126,28 @@ def _parse_requirement_details(
124126
marker = _parse_requirement_marker(
125127
tokenizer,
126128
span_start=specifier_start,
127-
after=(
128-
"version specifier"
129+
expected=(
130+
"comma (within version specifier), semicolon (after version specifier)"
129131
if specifier
130-
else "name and no valid version specifier"
132+
else "semicolon (after name with no version specifier)"
131133
),
132134
)
133135

134136
return (url, specifier, marker)
135137

136138

137139
def _parse_requirement_marker(
138-
tokenizer: Tokenizer, *, span_start: int, after: str
140+
tokenizer: Tokenizer, *, span_start: int, expected: str
139141
) -> MarkerList:
140142
"""
141143
requirement_marker = SEMICOLON marker WS?
142144
"""
143145

144146
if not tokenizer.check("SEMICOLON"):
145147
tokenizer.raise_syntax_error(
146-
f"Expected end or semicolon (after {after})",
148+
f"Expected {expected} or end",
147149
span_start=span_start,
150+
span_end=None,
148151
)
149152
tokenizer.read()
150153

tests/test_requirements.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ def test_error_no_space_after_url(self) -> None:
373373
# THEN
374374
assert ctx.exconly() == (
375375
"packaging.requirements.InvalidRequirement: "
376-
"Expected end or semicolon (after URL and whitespace)\n"
376+
"Expected semicolon (after URL and whitespace) or end\n"
377377
" name @ https://example.com/; extra == 'example'\n"
378378
" ~~~~~~~~~~~~~~~~~~~~~~^"
379379
)
@@ -521,7 +521,8 @@ def test_error_on_legacy_version_outside_triple_equals(self) -> None:
521521
# THEN
522522
assert ctx.exconly() == (
523523
"packaging.requirements.InvalidRequirement: "
524-
"Expected end or semicolon (after version specifier)\n"
524+
"Expected comma (within version specifier), "
525+
"semicolon (after version specifier) or end\n"
525526
" name==1.0.org1\n"
526527
" ~~~~~^"
527528
)
@@ -537,7 +538,7 @@ def test_error_on_missing_version_after_op(self) -> None:
537538
# THEN
538539
assert ctx.exconly() == (
539540
"packaging.requirements.InvalidRequirement: "
540-
"Expected end or semicolon (after name and no valid version specifier)\n"
541+
"Expected semicolon (after name with no version specifier) or end\n"
541542
" name==\n"
542543
" ^"
543544
)
@@ -553,7 +554,7 @@ def test_error_on_missing_op_after_name(self) -> None:
553554
# THEN
554555
assert ctx.exconly() == (
555556
"packaging.requirements.InvalidRequirement: "
556-
"Expected end or semicolon (after name and no valid version specifier)\n"
557+
"Expected semicolon (after name with no version specifier) or end\n"
557558
" name 1.0\n"
558559
" ^"
559560
)
@@ -569,11 +570,29 @@ def test_error_on_random_char_after_specifier(self) -> None:
569570
# THEN
570571
assert ctx.exconly() == (
571572
"packaging.requirements.InvalidRequirement: "
572-
"Expected end or semicolon (after version specifier)\n"
573+
"Expected comma (within version specifier), "
574+
"semicolon (after version specifier) or end\n"
573575
" name >= 1.0 #\n"
574576
" ~~~~~~~^"
575577
)
576578

579+
def test_error_on_missing_comma_in_specifier(self) -> None:
580+
# GIVEN
581+
to_parse = "name >= 1.0 <= 2.0"
582+
583+
# WHEN
584+
with pytest.raises(InvalidRequirement) as ctx:
585+
Requirement(to_parse)
586+
587+
# THEN
588+
assert ctx.exconly() == (
589+
"packaging.requirements.InvalidRequirement: "
590+
"Expected comma (within version specifier), "
591+
"semicolon (after version specifier) or end\n"
592+
" name >= 1.0 <= 2.0\n"
593+
" ~~~~~~~^"
594+
)
595+
577596

578597
class TestRequirementBehaviour:
579598
def test_types_with_nothing(self) -> None:

0 commit comments

Comments
 (0)