From df0b2e9eb64cac3732678e252f1035e49818d780 Mon Sep 17 00:00:00 2001 From: Frost Ming Date: Thu, 13 Jul 2023 15:13:17 +0800 Subject: [PATCH] fix: tolerate local version in specifiers (#2102) --- news/2102.bugfix.md | 1 + src/pdm/core.py | 2 +- src/pdm/models/specifiers.py | 18 ++++++++++++------ tests/models/test_candidates.py | 1 + tests/models/test_requirements.py | 6 ++++++ tests/models/test_specifiers.py | 4 ++++ 6 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 news/2102.bugfix.md diff --git a/news/2102.bugfix.md b/news/2102.bugfix.md new file mode 100644 index 0000000000..7aec233ba4 --- /dev/null +++ b/news/2102.bugfix.md @@ -0,0 +1 @@ +Tolerate and actually ignore the local versions in version specifiers. diff --git a/src/pdm/core.py b/src/pdm/core.py index 83eceef8e4..594f3564f2 100644 --- a/src/pdm/core.py +++ b/src/pdm/core.py @@ -83,7 +83,7 @@ def init_parser(self) -> None: ignore_python_option.add_to_parser(self.parser) pep582_option.add_to_parser(self.parser) - self.subparsers = self.parser.add_subparsers(parser_class=ArgumentParser, metavar="__root__") + self.subparsers = self.parser.add_subparsers(parser_class=ArgumentParser, dest="fooo") for _, name, _ in pkgutil.iter_modules(COMMANDS_MODULE_PATH): module = importlib.import_module(f"pdm.cli.commands.{name}", __name__) try: diff --git a/src/pdm/models/specifiers.py b/src/pdm/models/specifiers.py index cbe3cc6d4d..39f9ccfcf9 100644 --- a/src/pdm/models/specifiers.py +++ b/src/pdm/models/specifiers.py @@ -36,16 +36,22 @@ def fix_legacy_specifier(specifier: str) -> str: """Since packaging 22.0, legacy specifiers like '>=4.*' are no longer supported. We try to normalize them to the new format. """ + from pdm.utils import deprecation_warning def fix_wildcard(match: Match[str]) -> str: operator, _, version = match.groups() - if ".*" not in version or operator in ("==", "!="): + if operator in ("==", "!="): return match.group(0) - version = version.replace(".*", ".0") - if operator in ("<", "<="): # <4.* and <=4.* are equivalent to <4.0 - operator = "<" - elif operator in (">", ">="): # >4.* and >=4.* are equivalent to >=4.0 - operator = ">=" + if ".*" in version: + deprecation_warning(".* suffix can only be used with `==` or `!=` operators", stacklevel=4) + version = version.replace(".*", ".0") + if operator in ("<", "<="): # <4.* and <=4.* are equivalent to <4.0 + operator = "<" + elif operator in (">", ">="): # >4.* and >=4.* are equivalent to >=4.0 + operator = ">=" + elif "+" in version: # Drop the local version + deprecation_warning("Local version label can only be used with `==` or `!=` operators", stacklevel=4) + version = version.split("+")[0] return f"{operator}{version}" return _legacy_specifier_re.sub(fix_wildcard, specifier) diff --git a/tests/models/test_candidates.py b/tests/models/test_candidates.py index b57bd330e6..08b0d751bd 100644 --- a/tests/models/test_candidates.py +++ b/tests/models/test_candidates.py @@ -289,6 +289,7 @@ def test_legacy_pep345_tag_link(project): assert candidate.requires_python == ">=3,<4" +@pytest.mark.filterwarnings("ignore::FutureWarning") def test_ignore_invalid_py_version(project): project.project_config["pypi.url"] = "https://my.pypi.org/simple" req = parse_requirement("wheel") diff --git a/tests/models/test_requirements.py b/tests/models/test_requirements.py index a1ecc6225e..687a4ca236 100644 --- a/tests/models/test_requirements.py +++ b/tests/models/test_requirements.py @@ -61,9 +61,15 @@ "foo<5.0,>=4.0", marks=pytest.mark.skipif(not PACKAGING_22, reason="packaging 22+ required"), ), + pytest.param( + "foo>=3.0+g1234; python_version>='3.6'", + 'foo>=3.0; python_version >= "3.6"', + marks=pytest.mark.skipif(not PACKAGING_22, reason="packaging 22+ required"), + ), ] +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize("req, result", REQUIREMENTS) def test_convert_req_dict_to_req_line(req, result): r = parse_requirement(req) diff --git a/tests/models/test_specifiers.py b/tests/models/test_specifiers.py index a0bfb0f321..2034c752f9 100644 --- a/tests/models/test_specifiers.py +++ b/tests/models/test_specifiers.py @@ -3,6 +3,7 @@ from pdm.models.specifiers import PySpecSet +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize( "original,normalized", [ @@ -23,6 +24,8 @@ (">3.4.*", ">=3.4"), ("<=3.4.*", "<3.4"), ("<3.4.*", "<3.4"), + (">=3.0+g1234", ">=3.0"), + ("<3.0+g1234", "<3.0"), ("<3.10.0a6", "<3.10.0a6"), ("<3.10.2a3", "<3.10.2a3"), ], @@ -79,6 +82,7 @@ def test_impossible_pyspec(): assert str(spec_copy) == "impossible" +@pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.parametrize( "left,right", [