Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 9 additions & 14 deletions src/packaging/specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,12 @@ def _public_version(version: Version) -> Version:
return version.__replace__(local=None)


def _base_version(version: Version) -> Version:
if (
version.pre is None
and version.post is None
and version.dev is None
and version.local is None
):
return version
return version.__replace__(pre=None, post=None, dev=None, local=None)
def _post_base(version: Version) -> Version:
"""The version that *version* is a post-release of.

1.0.post1 -> 1.0, 1.0a1.post0 -> 1.0a1, 1.0.post0.dev1 -> 1.0.
"""
return version.__replace__(post=None, dev=None, local=None)


def _earliest_prerelease(version: Version) -> Version:
Expand Down Expand Up @@ -594,14 +591,12 @@ def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
if not prospective > spec:
return False

# This special case is here so that, unless the specifier itself
# includes is a post-release version, that we do not accept
# post-release versions for the version mentioned in the specifier
# (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0).
# The spec says: ">V MUST NOT allow a post-release of the specified
# version unless the specified version is itself a post-release."
if (
not spec.is_postrelease
and prospective.is_postrelease
and _base_version(prospective) == _base_version(spec)
and _post_base(prospective) == spec
):
return False

Expand Down
54 changes: 54 additions & 0 deletions tests/test_specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,43 @@ def test_specifier_prereleases_detection(
("<=2.0.dev1", "1.0a1", None, None, True),
("<2.0", "2.0a1", None, None, False),
("<2.0a2", "2.0a1", None, None, True),
# >V.devN: post-releases of V.devN itself are excluded
# (V.devN can't have post-releases in PEP 440, so nothing
# to exclude; these just confirm ordering still works)
(">1.0.dev1", "1.0.dev0", None, None, False),
(">1.0.dev1", "1.0.dev2", None, None, True),
# >V.devN: post-releases of the base release are NOT
# post-releases of V.devN, so they are accepted
(">1.0.dev1", "1.0.post0", None, None, True),
(">1.0.dev1", "1.0.post1", None, None, True),
(">1.0.dev0", "1.0.post0", None, None, True),
# >V.preN: post-releases of the base release are NOT
# post-releases of V.preN, so they are accepted
(">1.0a1", "1.0.post0", None, None, True),
(">1.0b1", "1.0.post0", None, None, True),
(">1.0rc1", "1.0.post0", None, None, True),
# >V.preN: post-releases of the pre-release itself
# ARE excluded
(">1.0a1", "1.0a1.post0", None, None, False),
(">1.0b2", "1.0b2.post0", None, None, False),
(">1.0rc1", "1.0rc1.post0", None, None, False),
# >V.preN: post of a different pre is not a post-release
# of V.preN either
(">1.0a1", "1.0a2.post0", None, None, True),
(">1.0b1", "1.0b2.post0", None, None, True),
# >V.devN: non-post-release versions above V.devN
(">1.0.dev1", "1.0", None, None, True),
(">1.0.dev1", "1.0a1", None, None, True),
(">1.0.dev1", "1.1", None, None, True),
# >V (final): post-releases of V are still excluded
(">1.0", "1.0.post0", None, None, False),
(">1.0", "1.0.post1", None, None, False),
# >V (final): post-releases of a different base are fine
(">1.0", "2.0.post0", None, None, True),
(">1.0", "0.9.post0", None, None, False),
# >V.devN: locals and different bases
(">1.0.dev1", "1.1.post0", None, None, True),
(">1.0.dev1", "0.9.post0", None, None, False),
# <V.postN: pre-releases of V.postN itself are excluded
("<1.0.post1", "1.0.post1.dev0", None, None, False),
("<1.0.post0", "1.0.post0.dev0", None, None, False),
Expand Down Expand Up @@ -1962,6 +1999,23 @@ def test_filter_exclusionary_bridges(
(">=1!0,!=1!1.*,!=1!2.*,<1!3", True, "0!5.0", False),
(">=1!0,!=1!1.*,!=1!2.*,<1!3", False, "1!0.5", True),
(">=1!0,!=1!1.*,!=1!2.*,<1!3", False, "0!5.0", False),
# >V.devN combined with other specifiers: post-releases of
# the base release are accepted (they are not post-releases
# of V.devN).
(">1.0.dev1,==1.0.post0", None, "1.0.post0", True),
(">1.0.dev1,==1.0.post1", None, "1.0.post1", True),
(">1.0a1,==1.0.post0", None, "1.0.post0", True),
(">1.0.dev1,<=2.0", None, "1.0.post0", True),
(">1.0.dev1,<=2.0", None, "1.0", True),
# >V.preN: post of the pre-release itself is still excluded
(">1.0a1,<=2.0", True, "1.0a1.post0", False),
# With an upper bound that includes post-releases
(">1.0.dev1,<=1.0.post1", None, "1.0.post0", True),
(">1.0.dev1,<=1.0.post1", None, "1.0.post1", True),
(">1.0.dev1,<=1.0.post1", None, "1.0", True),
# != can remove some versions but post-releases still match
(">1.0.dev1,!=1.0,<=2.0", None, "1.0.post0", True),
(">1.0.dev1,!=1.0,!=1.0.post0,<=2.0", None, "1.0.post1", True),
# <V.postN combined with other specifiers: pre-releases of
# the base release are accepted (they are not pre-releases
# of V.postN).
Expand Down
Loading