Skip to content

Commit 5da370e

Browse files
committed
Sort Requires-Python version
1 parent 3c5a189 commit 5da370e

File tree

4 files changed

+18
-7
lines changed

4 files changed

+18
-7
lines changed

news/13367.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Requires-Python error message should not sort versions numerically, not alphabetically

src/pip/_internal/index/package_finder.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from pip._vendor.packaging import specifiers
1919
from pip._vendor.packaging.tags import Tag
2020
from pip._vendor.packaging.utils import canonicalize_name
21-
from pip._vendor.packaging.version import InvalidVersion, _BaseVersion
21+
from pip._vendor.packaging.version import InvalidVersion, Version, _BaseVersion
2222
from pip._vendor.packaging.version import parse as parse_version
2323

2424
from pip._internal.exceptions import (
@@ -248,7 +248,17 @@ def evaluate_link(self, link: Link) -> tuple[LinkType, str]:
248248
ignore_requires_python=self._ignore_requires_python,
249249
)
250250
if not supports_python:
251-
reason = f"{version} Requires-Python {link.requires_python}"
251+
rp_specs = specifiers.SpecifierSet(link.requires_python)
252+
rp_versions = []
253+
for rp_spec in rp_specs:
254+
rp_version = rp_spec.version
255+
if rp_spec.operator in ["==", "!="] and rp_version.endswith(".*"):
256+
rp_version = rp_version[:-2]
257+
rp_versions.append(Version(rp_version))
258+
sorted_requires_python = ",".join(
259+
[str(s) for _, s in sorted(zip(rp_versions, rp_specs))]
260+
)
261+
reason = f"{version} Requires-Python {sorted_requires_python}"
252262
return (LinkType.requires_python_mismatch, reason)
253263

254264
logger.debug("Found link %s, version: %s", link, version)

src/pip/_vendor/packaging/specifiers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,10 @@ def prereleases(self) -> bool:
256256
# operators, and if they are if they are including an explicit
257257
# prerelease.
258258
operator, version = self._spec
259-
if operator in ["==", ">=", "<=", "~=", "===", ">", "<"]:
260-
# The == specifier can include a trailing .*, if it does we
259+
if operator in ["==", "!=", ">=", "<=", "~=", "===", ">", "<"]:
260+
# The == and != specifier can include a trailing .*, if it does we
261261
# want to remove before parsing.
262-
if operator == "==" and version.endswith(".*"):
262+
if operator in ["==", "!="] and version.endswith(".*"):
263263
version = version[:-2]
264264

265265
# Parse the version, and if it is a pre-release than this

tests/unit/test_index.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class TestLinkEvaluator:
133133
False,
134134
(
135135
LinkType.requires_python_mismatch,
136-
"1.12 Requires-Python == 3.6.5",
136+
"1.12 Requires-Python ==3.6.5,!=3.13.*",
137137
),
138138
id="requires-python-mismatch",
139139
),
@@ -162,7 +162,7 @@ def test_evaluate_link(
162162
)
163163
link = Link(
164164
"https://example.com/#egg=twine-1.12",
165-
requires_python="== 3.6.5",
165+
requires_python="!= 3.13.*, == 3.6.5",
166166
)
167167
actual = evaluator.evaluate_link(link)
168168
assert actual == expected

0 commit comments

Comments
 (0)