Skip to content

Commit 586873c

Browse files
committed
Non-PEP440 versions shouldn't prevent prerelease versions in empty specifier
1 parent 8fc075b commit 586873c

File tree

2 files changed

+35
-14
lines changed

2 files changed

+35
-14
lines changed

src/packaging/specifiers.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -994,14 +994,22 @@ def filter(
994994

995995
# Finally if prereleases is None, apply PEP 440 logic:
996996
# exclude prereleases unless there are no final releases that matched.
997-
filtered: list[UnparsedVersionVar] = []
997+
filtered_items: list[UnparsedVersionVar] = []
998998
found_prereleases: list[UnparsedVersionVar] = []
999+
found_final_release = False
9991000

10001001
for item in iterable:
10011002
parsed_version = _coerce_version(item)
1002-
if parsed_version is not None and parsed_version.is_prerelease:
1003+
# Arbitrary strings are always included as it is not
1004+
# possible to determine if they are prereleases,
1005+
# and they have already passed all specifiers.
1006+
if parsed_version is None:
1007+
filtered_items.append(item)
1008+
found_prereleases.append(item)
1009+
elif parsed_version.is_prerelease:
10031010
found_prereleases.append(item)
10041011
else:
1005-
filtered.append(item)
1012+
filtered_items.append(item)
1013+
found_final_release = True
10061014

1007-
return iter(filtered if filtered else found_prereleases)
1015+
return iter(filtered_items if found_final_release else found_prereleases)

tests/test_specifiers.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -688,13 +688,7 @@ def test_specifiers_prereleases(
688688
# Test != with invalid versions (should not pass as versions are not valid)
689689
("!=1.0", None, None, ["invalid", "foobar"], []),
690690
("!=1.0", None, None, ["1.0", "invalid", "2.0"], ["2.0"]),
691-
(
692-
"!=2.0.*",
693-
None,
694-
None,
695-
["invalid", "foobar", "2.0"],
696-
[]
697-
),
691+
("!=2.0.*", None, None, ["invalid", "foobar", "2.0"], []),
698692
("!=2.0.*", None, None, ["1.0", "invalid", "2.0.0"], ["1.0"]),
699693
# Test that !== ignores prereleases parameter for non-PEP 440 versions
700694
("!=1.0", None, True, ["invalid", "foobar"], []),
@@ -803,15 +797,34 @@ def test_empty_specifier(self, version):
803797
assert spec.contains(parse(version))
804798

805799
@pytest.mark.parametrize(
806-
"prereleases",
807-
[None, False, True],
800+
("prereleases", "versions", "expected"),
801+
[
802+
# single arbitrary string
803+
(None, ["foobar"], ["foobar"]),
804+
(False, ["foobar"], ["foobar"]),
805+
(True, ["foobar"], ["foobar"]),
806+
# arbitrary string with a stable version present
807+
(None, ["foobar", "1.0"], ["foobar", "1.0"]),
808+
(False, ["foobar", "1.0"], ["foobar", "1.0"]),
809+
(True, ["foobar", "1.0"], ["foobar", "1.0"]),
810+
# arbitrary string with a prerelease only
811+
(None, ["foobar", "1.0a1"], ["foobar", "1.0a1"]),
812+
(False, ["foobar", "1.0a1"], ["foobar"]),
813+
(True, ["foobar", "1.0a1"], ["foobar", "1.0a1"]),
814+
],
808815
)
809-
def test_empty_specifier_arbitrary_string(self, prereleases):
816+
def test_empty_specifier_arbitrary_string(self, prereleases, versions, expected):
810817
"""Test empty SpecifierSet accepts arbitrary strings."""
811818

812819
spec = SpecifierSet("", prereleases=prereleases)
820+
821+
# basic behavior preserved
813822
assert spec.contains("foobar")
814823

824+
# check filter behavior (no override of prereleases passed to filter)
825+
kwargs = {}
826+
assert list(spec.filter(versions, **kwargs)) == expected
827+
815828
def test_create_from_specifiers(self):
816829
spec_strs = [">=1.0", "!=1.1", "!=1.2", "<2.0"]
817830
specs = [Specifier(s) for s in spec_strs]

0 commit comments

Comments
 (0)