From 96d85be0e3d1d0f7fa42ce0cf41cdcce884fa8e0 Mon Sep 17 00:00:00 2001 From: Pradyun Gedam Date: Wed, 1 Feb 2023 09:04:29 +0000 Subject: [PATCH] Improve error for local version label with unsupported operators (#675) Co-authored-by: Pradyun Gedam --- src/packaging/_parser.py | 6 ++++++ src/packaging/_tokenizer.py | 1 + tests/test_requirements.py | 20 ++++++++++++++++++++ 3 files changed, 27 insertions(+) diff --git a/src/packaging/_parser.py b/src/packaging/_parser.py index 9b436e27..5a18b758 100644 --- a/src/packaging/_parser.py +++ b/src/packaging/_parser.py @@ -233,6 +233,12 @@ def _parse_version_many(tokenizer: Tokenizer) -> str: span_start=span_start, span_end=tokenizer.position + 1, ) + if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): + tokenizer.raise_syntax_error( + "Local version label can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position, + ) tokenizer.consume("WS") if not tokenizer.check("COMMA"): break diff --git a/src/packaging/_tokenizer.py b/src/packaging/_tokenizer.py index 1c1a961b..dd0d648d 100644 --- a/src/packaging/_tokenizer.py +++ b/src/packaging/_tokenizer.py @@ -79,6 +79,7 @@ def __str__(self) -> str: "URL": r"[^ \t]+", "IDENTIFIER": r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b", "VERSION_PREFIX_TRAIL": r"\.\*", + "VERSION_LOCAL_LABEL_TRAIL": r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*", "WS": r"[ \t]+", "END": r"$", } diff --git a/tests/test_requirements.py b/tests/test_requirements.py index b567b874..4ccc4770 100644 --- a/tests/test_requirements.py +++ b/tests/test_requirements.py @@ -301,6 +301,26 @@ def test_error_when_prefix_match_is_used_incorrectly(self) -> None: " ~~~~~^" ) + @pytest.mark.parametrize("operator", [">=", "<=", ">", "<", "~="]) + def test_error_when_local_version_label_is_used_incorrectly( + self, operator: str + ) -> None: + # GIVEN + to_parse = f"name {operator} 1.0+local.version.label" + op_tilde = len(operator) * "~" + + # WHEN + with pytest.raises(InvalidRequirement) as ctx: + Requirement(to_parse) + + # THEN + assert ctx.exconly() == ( + "packaging.requirements.InvalidRequirement: " + "Local version label can only be used with `==` or `!=` operators\n" + f" name {operator} 1.0+local.version.label\n" + f" {op_tilde}~~~~^" + ) + def test_error_when_bracket_not_closed_correctly(self) -> None: # GIVEN to_parse = "name[bar, baz >= 1.0"