Skip to content

Commit 48d948a

Browse files
committed
Merge branch 'main' into fix/greater-than-dev-postrelease-exclusion
# Conflicts: # tests/test_specifiers.py
2 parents 35f46a5 + 7740476 commit 48d948a

2 files changed

Lines changed: 58 additions & 14 deletions

File tree

src/packaging/specifiers.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,12 @@ def _post_base(version: Version) -> Version:
6060
return version.__replace__(post=None, dev=None, local=None)
6161

6262

63-
def _base_version(version: Version) -> Version:
64-
if (
65-
version.pre is None
66-
and version.post is None
67-
and version.dev is None
68-
and version.local is None
69-
):
70-
return version
71-
return version.__replace__(pre=None, post=None, dev=None, local=None)
63+
def _earliest_prerelease(version: Version) -> Version:
64+
"""Earliest pre-release of *version*.
65+
66+
1.2 -> 1.2.dev0, 1.2.post1 -> 1.2.post1.dev0.
67+
"""
68+
return version.__replace__(dev=0, local=None)
7269

7370

7471
class InvalidSpecifier(ValueError):
@@ -569,14 +566,12 @@ def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
569566
if not prospective < spec:
570567
return False
571568

572-
# This special case is here so that, unless the specifier itself
573-
# includes is a pre-release version, that we do not accept pre-release
574-
# versions for the version mentioned in the specifier (e.g. <3.1 should
575-
# not match 3.1.dev0, but should match 3.0.dev0).
569+
# The spec says: "<V MUST NOT allow a pre-release of the specified
570+
# version unless the specified version is itself a pre-release."
576571
if (
577572
not spec.is_prerelease
578573
and prospective.is_prerelease
579-
and _base_version(prospective) == _base_version(spec)
574+
and prospective >= _earliest_prerelease(spec)
580575
):
581576
return False
582577

tests/test_specifiers.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,35 @@ def test_specifier_prereleases_detection(
817817
# >V.devN: locals and different bases
818818
(">1.0.dev1", "1.1.post0", None, None, True),
819819
(">1.0.dev1", "0.9.post0", None, None, False),
820+
# <V.postN: pre-releases of V.postN itself are excluded
821+
("<1.0.post1", "1.0.post1.dev0", None, None, False),
822+
("<1.0.post0", "1.0.post0.dev0", None, None, False),
823+
# <V.postN: pre-releases of the base release are NOT
824+
# pre-releases of V.postN, so they are accepted
825+
("<1.0.post1", "1.0.dev0", None, None, True),
826+
("<1.0.post1", "1.0a1", None, None, True),
827+
("<1.0.post1", "1.0rc1", None, None, True),
828+
("<1.0.post0", "1.0.dev0", None, None, True),
829+
("<1.0.post0", "1.0a1", None, None, True),
830+
("<1.0.post0", "1.0b1", None, None, True),
831+
("<1.0.post0", "1.0rc2", None, None, True),
832+
# <V.postN: dev of a different post is not a pre-release
833+
# of V.postN either
834+
("<1.0.post1", "1.0.post0.dev0", None, None, True),
835+
("<1.0.post2", "1.0.post1.dev0", None, None, True),
836+
# <V.postN: non-pre-release versions below V.postN
837+
("<1.0.post1", "1.0", None, None, True),
838+
("<1.0.post1", "1.0.post0", None, None, True),
839+
("<1.0.post1", "0.9", None, None, True),
840+
("<1.0.post0", "1.0", None, None, True),
841+
# <V.postN: higher post numbers
842+
("<1.0.post10", "1.0.dev0", None, None, True),
843+
("<1.0.post10", "1.0.post9.dev0", None, None, True),
844+
("<1.0.post10", "1.0.post9", None, None, True),
845+
# <V.postN: locals and different bases
846+
("<1.0.post1", "1.0+local", None, None, True),
847+
("<1.0.post1", "1.0.post0+local", None, None, True),
848+
("<1.0.post1", "0.9.dev0", None, None, True),
820849
("<=2.0", "1.0.dev1", False, None, False),
821850
("<=2.0a1", "1.0.dev1", False, None, False),
822851
("<=2.0", "1.0.dev1", None, False, False),
@@ -1987,6 +2016,26 @@ def test_filter_exclusionary_bridges(
19872016
# != can remove some versions but post-releases still match
19882017
(">1.0.dev1,!=1.0,<=2.0", None, "1.0.post0", True),
19892018
(">1.0.dev1,!=1.0,!=1.0.post0,<=2.0", None, "1.0.post1", True),
2019+
# <V.postN combined with other specifiers: pre-releases of
2020+
# the base release are accepted (they are not pre-releases
2021+
# of V.postN).
2022+
("==1.0.dev0,<1.0.post1", None, "1.0.dev0", True),
2023+
("==1.0a1,<1.0.post0", None, "1.0a1", True),
2024+
("==1.0.post0.dev0,<1.0.post1", None, "1.0.post0.dev0", True),
2025+
(">=1.0,<1.0.post1", None, "1.0", True),
2026+
(">=1.0,<1.0.post1", None, "1.0.post0", True),
2027+
# 1.0.dev0 < 1.0, so it fails >=1.0 regardless of <
2028+
(">=1.0,<1.0.post1", True, "1.0.dev0", False),
2029+
# With a lower bound that includes pre-releases
2030+
(">=1.0.dev0,<1.0.post1", True, "1.0.dev0", True),
2031+
(">=1.0.dev0,<1.0.post1", True, "1.0.a1", True),
2032+
(">=1.0.dev0,<1.0.post1", True, "1.0.post0.dev0", True),
2033+
# != can remove non-pre-releases but pre-releases still match
2034+
(">=1.0.dev0,<1.0.post1,!=1.0,!=1.0.post0", True, "1.0.dev0", True),
2035+
(">=1.0.dev0,<1.0.post1,!=1.0,!=1.0.post0", True, "1.0.post0.dev0", True),
2036+
# Post-release survivors still match
2037+
(">=1.0.dev0,<1.0.post2,!=1.0,!=1.0.post0", True, "1.0.post1", True),
2038+
(">=1.0.dev0,<1.0.post2,!=1.0", True, "1.0.post0", True),
19902039
],
19912040
)
19922041
def test_contains_exclusionary_bridges(

0 commit comments

Comments
 (0)