Skip to content

Commit 25b1f44

Browse files
authored
Fix pep440 edge case in SpecifierSet.filter (#942)
1 parent 1b66ec2 commit 25b1f44

File tree

2 files changed

+391
-25
lines changed

2 files changed

+391
-25
lines changed

src/packaging/specifiers.py

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -949,30 +949,38 @@ def filter(
949949
# filter method for each one, this will act as a logical AND amongst
950950
# each specifier.
951951
if self._specs:
952+
# When prereleases is None, we need to let all versions through
953+
# the individual filters, then decide about prereleases at the end
954+
# based on whether any non-prereleases matched ALL specs.
952955
for spec in self._specs:
953-
iterable = spec.filter(iterable, prereleases=prereleases)
954-
return iter(iterable)
955-
# If we do not have any specifiers, then we need to have a rough filter
956-
# which will filter out any pre-releases, unless there are no final
957-
# releases.
956+
iterable = spec.filter(
957+
iterable, prereleases=True if prereleases is None else prereleases
958+
)
959+
960+
if prereleases is not None:
961+
# If we have a forced prereleases value,
962+
# we can immediately return he iterator.
963+
return iter(iterable)
958964
else:
959-
filtered: list[UnparsedVersionVar] = []
960-
found_prereleases: list[UnparsedVersionVar] = []
961-
962-
for item in iterable:
963-
parsed_version = _coerce_version(item)
964-
965-
# Store any item which is a pre-release for later unless we've
966-
# already found a final version or we are accepting prereleases
967-
if parsed_version.is_prerelease and not prereleases:
968-
if not filtered:
969-
found_prereleases.append(item)
970-
else:
971-
filtered.append(item)
972-
973-
# If we've found no items except for pre-releases, then we'll go
974-
# ahead and use the pre-releases
975-
if not filtered and found_prereleases and prereleases is None:
976-
return iter(found_prereleases)
977-
978-
return iter(filtered)
965+
# Handle empty SpecifierSet cases where prereleases is not None.
966+
if prereleases is True:
967+
return iter(iterable)
968+
969+
if prereleases is False:
970+
return (
971+
item for item in iterable if not _coerce_version(item).is_prerelease
972+
)
973+
974+
# Finally if prereleases is None, apply PEP 440 logic:
975+
# exclude prereleases unless there are no final releases that matched.
976+
filtered: list[UnparsedVersionVar] = []
977+
found_prereleases: list[UnparsedVersionVar] = []
978+
979+
for item in iterable:
980+
parsed_version = _coerce_version(item)
981+
if parsed_version.is_prerelease:
982+
found_prereleases.append(item)
983+
else:
984+
filtered.append(item)
985+
986+
return iter(filtered if filtered else found_prereleases)

0 commit comments

Comments
 (0)