Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/packaging/specifiers.py: 13%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# This file is dual licensed under the terms of the Apache License, Version
2# 2.0, and the BSD License. See the LICENSE file in the root of this repository
3# for complete details.
4"""
5.. testsetup::
7 from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier
8 from packaging.version import Version
9"""
11from __future__ import annotations
13__lazy_modules__ = {f"{__spec__.parent}._ranges", f"{__spec__.parent}.utils"}
15import abc
16import builtins
17import re
18import sys
19import typing
20from typing import (
21 TYPE_CHECKING,
22 Any,
23 Final,
24 TypeVar,
25)
27from ._ranges import (
28 FULL_RANGE,
29 bounds_for_spec,
30 coerce_version,
31 filter_by_ranges,
32 intersect_specifier_bounds,
33 matches_bounds_only,
34 ranges_are_prerelease_only,
35 resolve_prereleases,
36 trim_release,
37)
38from .utils import canonicalize_version
39from .version import Version
41if TYPE_CHECKING:
42 from collections.abc import Callable, Iterable, Iterator, Sequence
43 from typing import TypeGuard
45 from typing_extensions import Self
47 from . import ranges
48 from ._ranges import Interval
51__all__ = [
52 "BaseSpecifier",
53 "InvalidSpecifier",
54 "Specifier",
55 "SpecifierSet",
56]
59def __dir__() -> list[str]:
60 return __all__
63def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]:
64 return (
65 isinstance(spec, tuple)
66 and len(spec) == 2
67 and isinstance(spec[0], str)
68 and isinstance(spec[1], str)
69 )
72def _validate_pre(pre: object, /) -> TypeGuard[bool | None]:
73 return pre is None or isinstance(pre, bool)
76T = TypeVar("T")
77UnparsedVersion = Version | str
78UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)
81# Sentinel for __replace__, typed as Any so it can be the default for any field.
82# mypy is missing sentinel currently
83_UNSET: Any = object() if sys.version_info < (3, 15) else builtins.sentinel("_UNSET") # type: ignore[attr-defined]
86# Operators whose result is just a direct Version comparison, given a parsed
87# item with no local. ``<=``/``==``/``!=`` need that no-local guard because
88# PEP 440 strips locals on those; ``>=`` works regardless.
89_DIRECT_COMPARE_OPS: dict[str, Callable[[Version, Version], bool]] = {
90 ">=": Version.__ge__,
91 "<=": Version.__le__,
92 "==": Version.__eq__,
93 "!=": Version.__ne__,
94}
97def _fast_match(specifier: Specifier, parsed: Version) -> bool | None:
98 """Match ``parsed`` against ``specifier`` without building a range.
100 Handles ``>=``, ``<=``, ``==``, ``!=``, ``<``, ``>`` when the spec is
101 not a wildcard and ``parsed`` has no local. Returns ``None`` when the
102 range path must be used. Pre-release policy is left to the caller.
103 """
104 op_str, ver_str = specifier._spec
105 if ver_str.endswith(".*") or parsed.local is not None:
106 return None
108 direct_compare = _DIRECT_COMPARE_OPS.get(op_str)
109 if direct_compare is not None:
110 return direct_compare(parsed, specifier._require_spec_version(ver_str))
112 if op_str in ("<", ">"):
113 spec_v = specifier._require_spec_version(ver_str)
114 # ``<V``/``>V`` carve out V's family (pre/dev/post); that only
115 # matters when parsed shares V's epoch and trimmed release.
116 # Otherwise a direct cmpkey comparison is correct.
117 if parsed.epoch != spec_v.epoch or trim_release(parsed.release) != trim_release(
118 spec_v.release
119 ):
120 return parsed < spec_v if op_str == "<" else parsed > spec_v
121 return None
123 return None
126class InvalidSpecifier(ValueError):
127 """
128 Raised when attempting to create a :class:`Specifier` with a specifier
129 string that is invalid.
131 >>> Specifier("lolwat")
132 Traceback (most recent call last):
133 ...
134 packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat'
135 """
138class BaseSpecifier(metaclass=abc.ABCMeta):
139 """
140 Abstract base class for :class:`Specifier` and :class:`SpecifierSet`.
141 """
143 __slots__ = ()
144 __match_args__ = ("_str",)
146 @property
147 def _str(self) -> str:
148 """Internal property for match_args"""
149 return str(self)
151 @abc.abstractmethod
152 def __str__(self) -> str:
153 """
154 Returns the str representation of this Specifier-like object. This
155 should be representative of the Specifier itself.
156 """
158 @abc.abstractmethod
159 def __hash__(self) -> int:
160 """
161 Returns a hash value for this Specifier-like object.
162 """
164 @abc.abstractmethod
165 def __eq__(self, other: object) -> bool:
166 """
167 Returns a boolean representing whether or not the two Specifier-like
168 objects are equal.
170 :param other: The other object to check against.
171 """
173 @property
174 @abc.abstractmethod
175 def prereleases(self) -> bool | None:
176 """Whether or not pre-releases as a whole are allowed.
178 This can be set to either ``True`` or ``False`` to explicitly enable or disable
179 prereleases or it can be set to ``None`` (the default) to use default semantics.
180 """
182 @prereleases.setter # noqa: B027
183 def prereleases(self, value: bool) -> None:
184 """Setter for :attr:`prereleases`.
186 :param value: The value to set.
187 """
189 @abc.abstractmethod
190 def contains(self, item: str, prereleases: bool | None = None) -> bool:
191 """
192 Determines if the given item is contained within this specifier.
193 """
195 @typing.overload
196 def filter(
197 self,
198 iterable: Iterable[UnparsedVersionVar],
199 prereleases: bool | None = None,
200 key: None = ...,
201 ) -> Iterator[UnparsedVersionVar]: ...
203 @typing.overload
204 def filter(
205 self,
206 iterable: Iterable[T],
207 prereleases: bool | None = None,
208 key: Callable[[T], UnparsedVersion] = ...,
209 ) -> Iterator[T]: ...
211 @abc.abstractmethod
212 def filter(
213 self,
214 iterable: Iterable[Any],
215 prereleases: bool | None = None,
216 key: Callable[[Any], UnparsedVersion] | None = None,
217 ) -> Iterator[Any]:
218 """
219 Takes an iterable of items and filters them so that only items which
220 are contained within this specifier are allowed in it.
221 """
224class Specifier(BaseSpecifier):
225 """This class abstracts handling of version specifiers.
227 .. tip::
229 It is generally not required to instantiate this manually. You should instead
230 prefer to work with :class:`SpecifierSet` instead, which can parse
231 comma-separated version specifiers (which is what package metadata contains).
233 Instances are safe to serialize with :mod:`pickle`. They use a stable
234 format so the same pickle can be loaded in future packaging releases.
236 .. versionchanged:: 26.2
238 Added a stable pickle format. Pickles created with packaging 26.2+ can
239 be unpickled with future releases. Backward compatibility with pickles
240 from packaging < 26.2 is supported but may be removed in a future
241 release.
242 """
244 __slots__ = (
245 "_canonical_spec_cache",
246 "_prereleases",
247 "_ranges",
248 "_spec",
249 "_spec_version",
250 )
252 _specifier_regex_str = r"""
253 (?:
254 (?:
255 # The identity operators allow for an escape hatch that will
256 # do an exact string match of the version you wish to install.
257 # This will not be parsed by PEP 440 and we cannot determine
258 # any semantic meaning from it. This operator is discouraged
259 # but included entirely as an escape hatch.
260 === # Only match for the identity operator
261 \s*
262 [^\s;)]+ # The arbitrary version can be just about anything,
263 # we match everything except for whitespace, a
264 # semi-colon for marker support, and a closing paren
265 # since versions can be enclosed in them.
266 )
267 |
268 (?:
269 # The (non)equality operators allow for wild card and local
270 # versions to be specified so we have to define these two
271 # operators separately to enable that.
272 (?:==|!=) # Only match for equals and not equals
274 \s*
275 v?
276 (?:[0-9]+!)? # epoch
277 [0-9]+(?:\.[0-9]+)* # release
279 # You cannot use a wild card and a pre-release, post-release, a dev or
280 # local version together so group them with a | and make them optional.
281 (?:
282 \.\* # Wild card syntax of .*
283 |
284 (?a: # pre release
285 [-_\.]?
286 (alpha|beta|preview|pre|a|b|c|rc)
287 [-_\.]?
288 [0-9]*
289 )?
290 (?a: # post release
291 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
292 )?
293 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
294 (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
295 )?
296 )
297 |
298 (?:
299 # The compatible operator requires at least two digits in the
300 # release segment.
301 (?:~=) # Only match for the compatible operator
303 \s*
304 v?
305 (?:[0-9]+!)? # epoch
306 [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *)
307 (?a: # pre release
308 [-_\.]?
309 (alpha|beta|preview|pre|a|b|c|rc)
310 [-_\.]?
311 [0-9]*
312 )?
313 (?a: # post release
314 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
315 )?
316 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
317 )
318 |
319 (?:
320 # All other operators only allow a sub set of what the
321 # (non)equality operators do. Specifically they do not allow
322 # local versions to be specified nor do they allow the prefix
323 # matching wild cards.
324 (?:<=|>=|<|>)
326 \s*
327 v?
328 (?:[0-9]+!)? # epoch
329 [0-9]+(?:\.[0-9]+)* # release
330 (?a: # pre release
331 [-_\.]?
332 (alpha|beta|preview|pre|a|b|c|rc)
333 [-_\.]?
334 [0-9]*
335 )?
336 (?a: # post release
337 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
338 )?
339 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
340 )
341 )
342 """
344 _regex = re.compile(
345 r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE
346 )
348 # Legacy unused attribute, kept for backward compatibility
349 _operators: Final = {
350 "~=": "compatible",
351 "==": "equal",
352 "!=": "not_equal",
353 "<=": "less_than_equal",
354 ">=": "greater_than_equal",
355 "<": "less_than",
356 ">": "greater_than",
357 "===": "arbitrary",
358 }
360 def __init__(self, spec: str = "", prereleases: bool | None = None) -> None:
361 """Initialize a Specifier instance.
363 :param spec:
364 The string representation of a specifier which will be parsed and
365 normalized before use.
366 :param prereleases:
367 This tells the specifier if it should accept prerelease versions if
368 applicable or not. The default of ``None`` will autodetect it from the
369 given specifiers.
370 :raises InvalidSpecifier:
371 If the given specifier is invalid (i.e. bad syntax).
372 """
373 if not self._regex.fullmatch(spec):
374 raise InvalidSpecifier(f"Invalid specifier: {spec!r}")
376 spec = spec.strip()
377 if spec.startswith("==="):
378 operator, version = spec[:3], spec[3:].strip()
379 elif spec.startswith(("~=", "==", "!=", "<=", ">=")):
380 operator, version = spec[:2], spec[2:].strip()
381 else:
382 operator, version = spec[:1], spec[1:].strip()
384 self._spec: tuple[str, str] = (operator, version)
386 # Store whether or not this Specifier should accept prereleases
387 self._prereleases = prereleases
389 # Specifier version cache
390 self._spec_version: tuple[str, Version] | None = None
392 # Version range cache (populated by _to_ranges)
393 self._ranges: Sequence[Interval] | None = None
395 # Canonical (operator, version) cache (populated by _canonical_spec)
396 self._canonical_spec_cache: tuple[str, str] | None = None
398 def __replace__(
399 self,
400 *,
401 operator: str = _UNSET,
402 version: str = _UNSET,
403 prereleases: bool | None = _UNSET,
404 ) -> Self:
405 """__replace__(*, operator=..., version=..., prereleases=...)
407 Return a specifier with the given parts replaced. If no part changes,
408 return ``self``.
410 :param str operator:
411 Replacement comparison operator. Omit to retain the current operator.
412 :param str version:
413 Replacement version string. Omit to retain the current version.
414 :param prereleases:
415 Replacement prerelease override. Omit to retain the current override;
416 ``None`` restores automatic detection.
417 :type prereleases: bool | None
419 .. versionadded:: 26.4
420 """
421 new_operator = self.operator if operator is _UNSET else operator
422 new_version = self.version if version is _UNSET else version
423 new_prereleases = self._prereleases if prereleases is _UNSET else prereleases
424 if (
425 new_operator == self.operator
426 and new_version == self.version
427 and new_prereleases == self._prereleases
428 ):
429 return self
431 return self.__class__(
432 f"{new_operator}{new_version}", prereleases=new_prereleases
433 )
435 def _get_spec_version(self, version: str) -> Version | None:
436 """One element cache, as only one spec Version is needed per Specifier."""
437 if self._spec_version is not None and self._spec_version[0] == version:
438 return self._spec_version[1]
440 version_specifier = coerce_version(version)
441 if version_specifier is None:
442 return None
444 self._spec_version = (version, version_specifier)
445 return version_specifier
447 def _require_spec_version(self, version: str) -> Version:
448 """Get spec version, asserting it's valid (not for === operator).
450 This method should only be called for operators where version
451 strings are guaranteed to be valid PEP 440 versions (not ===).
452 """
453 spec_version = self._get_spec_version(version)
454 assert spec_version is not None
455 return spec_version
457 def _to_ranges(self) -> Sequence[Interval]:
458 """Convert this specifier to sorted, non-overlapping version ranges.
460 Each standard operator maps to one or two ranges. ``===`` is
461 modeled as full range (actual check done separately). Cached.
462 """
463 if self._ranges is not None:
464 return self._ranges
466 op = self.operator
467 ver_str = self.version
469 if op == "===":
470 result: Sequence[Interval] = FULL_RANGE
471 else:
472 version = self._require_spec_version(ver_str.removesuffix(".*"))
473 result = bounds_for_spec(op, ver_str, version)
475 self._ranges = result
476 return result
478 @property
479 def prereleases(self) -> bool | None:
480 # If there is an explicit prereleases set for this, then we'll just
481 # blindly use that.
482 if self._prereleases is not None:
483 return self._prereleases
485 # Only the "!=" operator does not imply prereleases when
486 # the version in the specifier is a prerelease.
487 operator, version_str = self._spec
488 if operator == "!=":
489 return False
491 # The == specifier with trailing .* cannot include prereleases
492 # e.g. "==1.0a1.*" is not valid.
493 if operator == "==" and version_str.endswith(".*"):
494 return False
496 # "===" can have arbitrary string versions, so we cannot parse
497 # those, we take prereleases as unknown (None) for those.
498 version = self._get_spec_version(version_str)
499 if version is None:
500 return None
502 # For all other operators, use the check if spec Version
503 # object implies pre-releases.
504 return version.is_prerelease
506 @prereleases.setter
507 def prereleases(self, value: bool | None) -> None:
508 self._prereleases = value
510 def __getstate__(self) -> tuple[tuple[str, str], bool | None]:
511 # Return state as a 2-item tuple for compactness:
512 # ((operator, version), prereleases)
513 # Cache members are excluded and will be recomputed on demand.
514 return (self._spec, self._prereleases)
516 def __setstate__(self, state: object) -> None:
517 # Always discard cached values - they will be recomputed on demand.
518 self._spec_version = None
519 self._ranges = None
520 self._canonical_spec_cache = None
522 if isinstance(state, tuple):
523 if len(state) == 2:
524 # New format (26.2+): ((operator, version), prereleases)
525 spec, prereleases = state
526 if _validate_spec(spec) and _validate_pre(prereleases):
527 self._spec = spec
528 self._prereleases = prereleases
529 return
530 if len(state) == 2 and isinstance(state[1], dict):
531 # Format (packaging 26.0-26.1): (None, {slot: value}).
532 _, slot_dict = state
533 spec = slot_dict.get("_spec")
534 prereleases = slot_dict.get("_prereleases", "invalid")
535 if _validate_spec(spec) and _validate_pre(prereleases):
536 self._spec = spec
537 self._prereleases = prereleases
538 return
539 if isinstance(state, dict):
540 # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
541 spec = state.get("_spec")
542 prereleases = state.get("_prereleases", "invalid")
543 if _validate_spec(spec) and _validate_pre(prereleases):
544 self._spec = spec
545 self._prereleases = prereleases
546 return
548 raise TypeError(f"Cannot restore Specifier from {state!r}")
550 @property
551 def operator(self) -> str:
552 """The operator of this specifier.
554 >>> Specifier("==1.2.3").operator
555 '=='
556 """
557 return self._spec[0]
559 @property
560 def version(self) -> str:
561 """The version of this specifier.
563 >>> Specifier("==1.2.3").version
564 '1.2.3'
565 """
566 return self._spec[1]
568 def __repr__(self) -> str:
569 """A representation of the Specifier that shows all internal state.
571 >>> Specifier('>=1.0.0')
572 <Specifier('>=1.0.0')>
573 >>> Specifier('>=1.0.0', prereleases=False)
574 <Specifier('>=1.0.0', prereleases=False)>
575 >>> Specifier('>=1.0.0', prereleases=True)
576 <Specifier('>=1.0.0', prereleases=True)>
577 """
578 pre = (
579 f", prereleases={self.prereleases!r}"
580 if self._prereleases is not None
581 else ""
582 )
584 return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
586 def __str__(self) -> str:
587 """A string representation of the Specifier that can be round-tripped.
589 >>> str(Specifier('>=1.0.0'))
590 '>=1.0.0'
591 >>> str(Specifier('>=1.0.0', prereleases=False))
592 '>=1.0.0'
593 """
594 return "{}{}".format(*self._spec)
596 @property
597 def _canonical_spec(self) -> tuple[str, str]:
598 cached = self._canonical_spec_cache
599 if cached is not None:
600 return cached
602 operator, version = self._spec
603 if operator == "===" or version.endswith(".*"):
604 result = self._spec
605 else:
606 canonical = canonicalize_version(
607 self._require_spec_version(version),
608 strip_trailing_zero=(operator != "~="),
609 )
610 # Most versions are already canonical, so reuse the existing tuple rather
611 # than retaining a second copy of a string equal to the one we hold.
612 result = self._spec if canonical == version else (operator, canonical)
614 self._canonical_spec_cache = result
615 return result
617 def __hash__(self) -> int:
618 return hash(self._canonical_spec)
620 def __eq__(self, other: object) -> bool:
621 """Whether or not the two Specifier-like objects are equal.
623 :param other: The other object to check against.
625 The value of :attr:`prereleases` is ignored.
627 >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")
628 True
629 >>> (Specifier("==1.2.3", prereleases=False) ==
630 ... Specifier("==1.2.3", prereleases=True))
631 True
632 >>> Specifier("==1.2.3") == "==1.2.3"
633 True
634 >>> Specifier("==1.2.3") == Specifier("==1.2.4")
635 False
636 >>> Specifier("==1.2.3") == Specifier("~=1.2.3")
637 False
638 """
639 if isinstance(other, str):
640 try:
641 other = self.__class__(str(other))
642 except InvalidSpecifier:
643 return NotImplemented
644 elif not isinstance(other, self.__class__):
645 return NotImplemented
647 return self._canonical_spec == other._canonical_spec
649 def __contains__(self, item: str | Version) -> bool:
650 """Return whether or not the item is contained in this specifier.
652 :param item: The item to check for.
654 This is used for the ``in`` operator and behaves the same as
655 :meth:`contains` with no ``prereleases`` argument passed.
657 >>> "1.2.3" in Specifier(">=1.2.3")
658 True
659 >>> Version("1.2.3") in Specifier(">=1.2.3")
660 True
661 >>> "1.0.0" in Specifier(">=1.2.3")
662 False
663 >>> "1.3.0a1" in Specifier(">=1.2.3")
664 True
665 >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)
666 True
667 """
668 return self.contains(item)
670 def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool:
671 """Return whether or not the item is contained in this specifier.
673 :param item:
674 The item to check for, which can be a version string or a
675 :class:`~packaging.version.Version` instance.
676 :param prereleases:
677 Whether or not to match prereleases with this Specifier. If set to
678 ``None`` (the default), it will follow the recommendation from
679 :pep:`440` and match prereleases, as there are no other versions.
681 >>> Specifier(">=1.2.3").contains("1.2.3")
682 True
683 >>> Specifier(">=1.2.3").contains(Version("1.2.3"))
684 True
685 >>> Specifier(">=1.2.3").contains("1.0.0")
686 False
687 >>> Specifier(">=1.2.3").contains("1.3.0a1")
688 True
689 >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1")
690 False
691 >>> Specifier(">=1.2.3").contains("1.3.0a1")
692 True
694 .. versionchanged:: 26.0
696 With ``prereleases=None``, a prerelease now matches. A single
697 version has no alternatives, so the :pep:`440` rule to accept
698 prereleases when nothing else satisfies the specifier applies.
699 Earlier versions rejected it. An unparsable version now returns
700 ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`.
701 """
702 # ``===`` compares the raw string, so a Version parse here would
703 # be wasted.
704 if self._spec[0] == "===":
705 return bool(list(self.filter([item], prereleases=prereleases)))
707 parsed = coerce_version(item)
708 if parsed is None:
709 # Standard operators never match an unparsable input.
710 return False
712 if prereleases is None:
713 prereleases = resolve_prereleases(self._prereleases, self.prereleases)
715 if prereleases is False and parsed.is_prerelease:
716 return False
718 # ``_fast_match`` answers the simple operators without building a
719 # range; otherwise fall back to the engine's bounds membership.
720 match = _fast_match(self, parsed)
721 if match is not None:
722 return match
724 return matches_bounds_only(self._to_ranges(), parsed)
726 @typing.overload
727 def filter(
728 self,
729 iterable: Iterable[UnparsedVersionVar],
730 prereleases: bool | None = None,
731 key: None = ...,
732 ) -> Iterator[UnparsedVersionVar]: ...
734 @typing.overload
735 def filter(
736 self,
737 iterable: Iterable[T],
738 prereleases: bool | None = None,
739 key: Callable[[T], UnparsedVersion] = ...,
740 ) -> Iterator[T]: ...
742 def filter(
743 self,
744 iterable: Iterable[Any],
745 prereleases: bool | None = None,
746 key: Callable[[Any], UnparsedVersion] | None = None,
747 ) -> Iterator[Any]:
748 """Filter items in the given iterable, that match the specifier.
750 :param iterable:
751 An iterable that can contain version strings and
752 :class:`~packaging.version.Version` instances. The items in the
753 iterable will be filtered according to the specifier.
754 :param prereleases:
755 Whether or not to allow prereleases in the returned iterator. If set to
756 ``None`` (the default), it will follow the recommendation from :pep:`440`
757 and match prereleases if there are no other versions.
758 :param key:
759 A callable that takes a single argument (an item from the iterable) and
760 returns a version string or :class:`~packaging.version.Version`
761 instance to be used for filtering.
763 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
764 ['1.3']
765 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))
766 ['1.2.3', '1.3', <Version('1.4')>]
767 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))
768 ['1.5a1']
769 >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
770 ['1.3', '1.5a1']
771 >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
772 ['1.3', '1.5a1']
773 >>> list(Specifier(">=1.2.3").filter(
774 ... [{"ver": "1.2"}, {"ver": "1.3"}],
775 ... key=lambda x: x["ver"]))
776 [{'ver': '1.3'}]
778 .. versionchanged:: 26.1
780 Added the ``key`` parameter.
781 """
782 if prereleases is None:
783 prereleases = resolve_prereleases(self._prereleases, self.prereleases)
785 if self.operator == "===":
786 spec_lower = self.version.lower()
787 matches = (
788 item
789 for item in iterable
790 if str(item if key is None else key(item)).lower() == spec_lower
791 )
792 return _apply_prereleases_filter(matches, key, prereleases)
794 return filter_by_ranges(self._to_ranges(), iterable, key, prereleases)
797def _apply_prereleases_filter(
798 matches: Iterable[Any],
799 key: Callable[[Any], UnparsedVersion] | None,
800 prereleases: bool | None,
801) -> Iterator[Any]:
802 """Apply ``prereleases=`` handling to an already-matched iterable.
804 ``None`` means PEP 440 default (buffer pre-releases until a final
805 appears); ``True`` yields everything; ``False`` drops pre-releases.
806 """
807 if prereleases is None:
808 return _pep440_filter_prereleases(matches, key)
809 if prereleases:
810 return iter(matches)
811 return (
812 item
813 for item in matches
814 if (parsed := coerce_version(item if key is None else key(item))) is None
815 or not parsed.is_prerelease
816 )
819class SpecifierSet(BaseSpecifier):
820 """This class abstracts handling of a set of version specifiers.
822 It can be passed a single specifier (``>=3.0``), a comma-separated list of
823 specifiers (``>=3.0,!=3.1``), or no specifier at all.
825 Instances are safe to serialize with :mod:`pickle`. They use a stable
826 format so the same pickle can be loaded in future packaging
827 releases.
829 .. versionchanged:: 26.2
831 Added a stable pickle format. Pickles created with
832 packaging 26.2+ can be unpickled with future releases.
833 Backward compatibility with pickles from
834 packaging < 26.2 is supported but may be removed in a future
835 release.
836 """
838 __slots__ = (
839 "_canonicalized",
840 "_has_arbitrary",
841 "_is_unsatisfiable",
842 "_prereleases",
843 "_ranges",
844 "_specs",
845 )
847 def __init__(
848 self,
849 specifiers: str | Iterable[Specifier | str] = "",
850 prereleases: bool | None = None,
851 ) -> None:
852 """Initialize a SpecifierSet instance.
854 :param specifiers:
855 The string representation of a specifier or a comma-separated list of
856 specifiers which will be parsed and normalized before use.
857 May also be an iterable of ``Specifier`` instances, which will be used
858 as is. Plain ``str`` elements are parsed as if passed to
859 :class:`Specifier`, so ``SpecifierSet([">=1.0"])`` equals
860 ``SpecifierSet(">=1.0")``.
861 :param prereleases:
862 This tells the SpecifierSet if it should accept prerelease versions if
863 applicable or not. The default of ``None`` will autodetect it from the
864 given specifiers.
866 :raises InvalidSpecifier:
867 If the given ``specifiers`` are not parseable than this exception will be
868 raised.
869 :raises TypeError:
870 If an element of the iterable form is neither a :class:`Specifier`
871 nor a ``str``.
872 """
874 if isinstance(specifiers, str):
875 # Split on `,` to break each individual specifier into its own item, and
876 # strip each item to remove leading/trailing whitespace.
877 split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
879 self._specs: tuple[Specifier, ...] = tuple(map(Specifier, split_specifiers))
880 # Fast substring check; avoids iterating parsed specs.
881 self._has_arbitrary = "===" in specifiers
882 else:
883 # List comprehension is faster than generator below
884 self._specs = tuple(
885 [s if isinstance(s, Specifier) else Specifier(s) for s in specifiers]
886 )
887 self._has_arbitrary = any(s.operator == "===" for s in self._specs)
889 self._canonicalized = len(self._specs) <= 1
890 self._is_unsatisfiable: bool | None = None
891 self._ranges: Sequence[Interval] | None = None
893 # Store our prereleases value so we can use it later to determine if
894 # we accept prereleases or not.
895 self._prereleases = prereleases
897 def __replace__(
898 self,
899 *,
900 specifiers: str | Iterable[Specifier | str] = _UNSET,
901 prereleases: bool | None = _UNSET,
902 ) -> Self:
903 """__replace__(*, specifiers=..., prereleases=...)
905 Return a specifier set with the given parts replaced. If ``specifiers``
906 is omitted and the prerelease override is unchanged, return ``self``.
908 :param specifiers:
909 Replacement specifiers as a string or an iterable of ``Specifier``
910 and ``str`` instances. Omit to retain the current specifiers.
911 :type specifiers: str | Iterable[Specifier | str]
912 :param prereleases:
913 Replacement prerelease override. Omit to retain the current override;
914 ``None`` restores automatic detection.
915 :type prereleases: bool | None
917 .. versionadded:: 26.4
918 """
919 new_specifiers = self._specs if specifiers is _UNSET else specifiers
920 new_prereleases = self._prereleases if prereleases is _UNSET else prereleases
921 if specifiers is _UNSET and new_prereleases == self._prereleases:
922 return self
924 return self.__class__(new_specifiers, prereleases=new_prereleases)
926 def _canonical_specs(self) -> tuple[Specifier, ...]:
927 """Deduplicate, sort, and cache specs for order-sensitive operations."""
928 if not self._canonicalized:
929 self._specs = tuple(dict.fromkeys(sorted(self._specs, key=str)))
930 self._canonicalized = True
931 return self._specs
933 @property
934 def prereleases(self) -> bool | None:
935 # If we have been given an explicit prerelease modifier, then we'll
936 # pass that through here.
937 if self._prereleases is not None:
938 return self._prereleases
940 # If we don't have any specifiers, and we don't have a forced value,
941 # then we'll just return None since we don't know if this should have
942 # pre-releases or not.
943 if not self._specs:
944 return None
946 # Otherwise we'll see if any of the given specifiers accept
947 # prereleases, if any of them do we'll return True, otherwise False.
948 if any(s.prereleases for s in self._specs):
949 return True
951 return None
953 @prereleases.setter
954 def prereleases(self, value: bool | None) -> None:
955 self._prereleases = value
956 self._is_unsatisfiable = None
958 def __getstate__(self) -> tuple[tuple[Specifier, ...], bool | None]:
959 # Return state as a 2-item tuple for compactness:
960 # (specs, prereleases)
961 # Cache members are excluded and will be recomputed on demand.
962 return (self._specs, self._prereleases)
964 def __setstate__(self, state: object) -> None:
965 # Always discard cached values - they will be recomputed on demand.
966 self._ranges = None
967 self._is_unsatisfiable = None
969 if isinstance(state, tuple):
970 if len(state) == 2:
971 # New format (26.2+): (specs, prereleases)
972 specs, prereleases = state
973 if (
974 isinstance(specs, tuple)
975 and all(isinstance(s, Specifier) for s in specs)
976 and _validate_pre(prereleases)
977 ):
978 self._specs = specs
979 self._prereleases = prereleases
980 self._canonicalized = len(specs) <= 1
981 self._has_arbitrary = any("===" in str(s) for s in specs)
982 return
983 if len(state) == 2 and isinstance(state[1], dict):
984 # Format (packaging 26.0-26.1): (None, {slot: value}).
985 _, slot_dict = state
986 specs = slot_dict.get("_specs", ())
987 prereleases = slot_dict.get("_prereleases")
988 # Convert frozenset to tuple (26.0 stored as frozenset)
989 if isinstance(specs, frozenset):
990 specs = tuple(sorted(specs, key=str))
991 if (
992 isinstance(specs, tuple)
993 and all(isinstance(s, Specifier) for s in specs)
994 and _validate_pre(prereleases)
995 ):
996 self._specs = specs
997 self._prereleases = prereleases
998 self._canonicalized = len(self._specs) <= 1
999 self._has_arbitrary = any("===" in str(s) for s in self._specs)
1000 return
1001 if isinstance(state, dict):
1002 # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
1003 specs = state.get("_specs", ())
1004 prereleases = state.get("_prereleases")
1005 # Convert frozenset to tuple (26.0 stored as frozenset)
1006 if isinstance(specs, frozenset):
1007 specs = tuple(sorted(specs, key=str))
1008 if (
1009 isinstance(specs, tuple)
1010 and all(isinstance(s, Specifier) for s in specs)
1011 and _validate_pre(prereleases)
1012 ):
1013 self._specs = specs
1014 self._prereleases = prereleases
1015 self._canonicalized = len(self._specs) <= 1
1016 self._has_arbitrary = any("===" in str(s) for s in self._specs)
1017 return
1019 raise TypeError(f"Cannot restore SpecifierSet from {state!r}")
1021 def __repr__(self) -> str:
1022 """A representation of the specifier set that shows all internal state.
1024 Note that the ordering of the individual specifiers within the set may not
1025 match the input string.
1027 >>> SpecifierSet('>=1.0.0,!=2.0.0')
1028 <SpecifierSet('!=2.0.0,>=1.0.0')>
1029 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)
1030 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)>
1031 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)
1032 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)>
1033 """
1034 pre = (
1035 f", prereleases={self.prereleases!r}"
1036 if self._prereleases is not None
1037 else ""
1038 )
1040 return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
1042 def __str__(self) -> str:
1043 """A string representation of the specifier set that can be round-tripped.
1045 Note that the ordering of the individual specifiers within the set may not
1046 match the input string.
1048 >>> str(SpecifierSet(">=1.0.0,!=1.0.1"))
1049 '!=1.0.1,>=1.0.0'
1050 >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False))
1051 '!=1.0.1,>=1.0.0'
1052 """
1053 return ",".join(str(s) for s in self._canonical_specs())
1055 def __hash__(self) -> int:
1056 return hash(self._canonical_specs())
1058 def __and__(self, other: SpecifierSet | str) -> SpecifierSet:
1059 """Return a SpecifierSet which is a combination of the two sets.
1061 :param other: The other object to combine with.
1063 >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1'
1064 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
1065 >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1')
1066 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
1067 """
1068 if isinstance(other, str):
1069 other = SpecifierSet(other)
1070 elif not isinstance(other, SpecifierSet):
1071 return NotImplemented
1073 specifier = SpecifierSet()
1074 specifier._specs = self._specs + other._specs
1075 specifier._canonicalized = len(specifier._specs) <= 1
1076 specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary
1078 # Combine prerelease settings: use common or non-None value
1079 if self._prereleases is None or self._prereleases == other._prereleases:
1080 specifier._prereleases = other._prereleases
1081 elif other._prereleases is None:
1082 specifier._prereleases = self._prereleases
1083 else:
1084 raise ValueError(
1085 "Cannot combine SpecifierSets with True and False prerelease overrides."
1086 )
1088 return specifier
1090 def __eq__(self, other: object) -> bool:
1091 """Whether or not the two SpecifierSet-like objects are equal.
1093 :param other: The other object to check against.
1095 The value of :attr:`prereleases` is ignored.
1097 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1")
1098 True
1099 >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) ==
1100 ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True))
1101 True
1102 >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1"
1103 True
1104 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0")
1105 False
1106 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2")
1107 False
1108 """
1109 if isinstance(other, (str, Specifier)):
1110 other = SpecifierSet(str(other))
1111 elif not isinstance(other, SpecifierSet):
1112 return NotImplemented
1114 return self._canonical_specs() == other._canonical_specs()
1116 def __len__(self) -> int:
1117 """Returns the number of specifiers in this specifier set."""
1118 return len(self._specs)
1120 def __iter__(self) -> Iterator[Specifier]:
1121 """
1122 Returns an iterator over all the underlying :class:`Specifier` instances
1123 in this specifier set.
1125 >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str)
1126 [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>]
1127 """
1128 return iter(self._specs)
1130 def _get_ranges(self) -> Sequence[Interval]:
1131 """Intersect all specifiers into a single sequence of version ranges.
1133 Empty when unsatisfiable. Callers must ensure ``self._specs``
1134 is non-empty.
1135 """
1136 if self._ranges is not None:
1137 return self._ranges
1139 self._ranges = intersect_specifier_bounds(s._to_ranges() for s in self._specs)
1140 return self._ranges
1142 def is_unsatisfiable(self) -> bool:
1143 """Check whether this specifier set can never be satisfied.
1145 Returns True if no version can satisfy all specifiers simultaneously.
1147 >>> SpecifierSet(">=2.0,<1.0").is_unsatisfiable()
1148 True
1149 >>> SpecifierSet(">=1.0,<2.0").is_unsatisfiable()
1150 False
1151 >>> SpecifierSet("").is_unsatisfiable()
1152 False
1153 >>> SpecifierSet("==1.0,!=1.0").is_unsatisfiable()
1154 True
1156 .. versionadded:: 26.1
1157 """
1158 cached = self._is_unsatisfiable
1159 if cached is not None:
1160 return cached
1162 if not self._specs:
1163 self._is_unsatisfiable = False
1164 return False
1166 result = not self._get_ranges()
1168 if not result:
1169 result = self._check_arbitrary_unsatisfiable()
1171 if not result and self.prereleases is False:
1172 result = ranges_are_prerelease_only(self._get_ranges())
1174 self._is_unsatisfiable = result
1175 return result
1177 def _check_arbitrary_unsatisfiable(self) -> bool:
1178 """Check === (arbitrary equality) specs for unsatisfiability.
1180 === uses case-insensitive string comparison, so the only candidate
1181 that can match ``===V`` is the literal string V. This method
1182 checks whether that candidate is excluded by other specifiers.
1183 """
1184 arbitrary = [s for s in self._specs if s.operator == "==="]
1185 if not arbitrary:
1186 return False
1188 # Multiple === must agree on the same string (case-insensitive).
1189 first = arbitrary[0].version.lower()
1190 if any(s.version.lower() != first for s in arbitrary[1:]):
1191 return True
1193 # The sole candidate is the === version string. Check whether
1194 # it can satisfy every standard spec.
1195 candidate = coerce_version(arbitrary[0].version)
1197 # With prereleases=False, a prerelease candidate is excluded
1198 # by contains() before the === string check even runs.
1199 if (
1200 self.prereleases is False
1201 and candidate is not None
1202 and candidate.is_prerelease
1203 ):
1204 return True
1206 standard = [s for s in self._specs if s.operator != "==="]
1207 if not standard:
1208 return False
1210 if candidate is None:
1211 # Unparsable string cannot satisfy any standard spec.
1212 return True
1214 return not all(s.contains(candidate) for s in standard)
1216 def to_range(self) -> ranges.VersionRange:
1217 """Return the :class:`~packaging.ranges.VersionRange` this set accepts.
1219 An empty set yields the full range; an unsatisfiable set yields the
1220 empty range. ``===`` specifiers contribute literal-string admission.
1222 >>> SpecifierSet(">=1.0,<2.0").to_range()
1223 <VersionRange '[1.0, 2.0.dev0)'>
1225 .. versionadded:: 26.3
1226 """
1227 from .ranges import VersionRange # noqa: PLC0415
1229 return VersionRange._from_specifier_set(self)
1231 def _check_relation_operand(self, other: object) -> None:
1232 if not isinstance(other, SpecifierSet):
1233 raise TypeError("expected a SpecifierSet")
1234 if self._has_arbitrary or other._has_arbitrary:
1235 raise ValueError("set relations do not support === specifiers")
1237 def is_subset(self, other: SpecifierSet) -> bool:
1238 """Return whether every version matching this set also matches other.
1240 :raises ValueError:
1241 If either set uses ``===`` specifiers, or the two sets were
1242 given different ``prereleases`` arguments (unset on one side
1243 counts as different).
1244 :raises TypeError:
1245 If other is not a :class:`SpecifierSet`.
1247 >>> SpecifierSet(">=3.12,<3.13").is_subset(SpecifierSet(">=3.12"))
1248 True
1249 >>> SpecifierSet(">=3.12").is_subset(SpecifierSet(">=3.12,<3.13"))
1250 False
1252 .. versionadded:: 26.3
1253 """
1254 self._check_relation_operand(other)
1255 return self.to_range().is_subset(other.to_range())
1257 def is_superset(self, other: SpecifierSet) -> bool:
1258 """Return whether every version matching other also matches this set.
1260 :raises ValueError:
1261 If either set uses ``===`` specifiers, or the two sets were
1262 given different ``prereleases`` arguments (unset on one side
1263 counts as different).
1264 :raises TypeError:
1265 If other is not a :class:`SpecifierSet`.
1267 >>> SpecifierSet(">=3.12").is_superset(SpecifierSet(">=3.12,<3.13"))
1268 True
1270 .. versionadded:: 26.3
1271 """
1272 self._check_relation_operand(other)
1273 return self.to_range().is_superset(other.to_range())
1275 def is_disjoint(self, other: SpecifierSet) -> bool:
1276 """Return whether this set and other share no matching versions.
1278 :raises ValueError:
1279 If either set uses ``===`` specifiers, or the two sets were
1280 given different ``prereleases`` arguments (unset on one side
1281 counts as different).
1282 :raises TypeError:
1283 If other is not a :class:`SpecifierSet`.
1285 >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.12"))
1286 True
1287 >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.11"))
1288 False
1290 .. versionadded:: 26.3
1291 """
1292 self._check_relation_operand(other)
1293 return self.to_range().is_disjoint(other.to_range())
1295 def __contains__(self, item: UnparsedVersion) -> bool:
1296 """Return whether or not the item is contained in this specifier.
1298 :param item: The item to check for.
1300 This is used for the ``in`` operator and behaves the same as
1301 :meth:`contains` with no ``prereleases`` argument passed.
1303 >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1")
1304 True
1305 >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1")
1306 True
1307 >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")
1308 False
1309 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")
1310 True
1311 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)
1312 True
1313 """
1314 return self.contains(item)
1316 def contains(
1317 self,
1318 item: UnparsedVersion,
1319 prereleases: bool | None = None,
1320 installed: bool | None = None,
1321 ) -> bool:
1322 """Return whether or not the item is contained in this SpecifierSet.
1324 :param item:
1325 The item to check for, which can be a version string or a
1326 :class:`~packaging.version.Version` instance.
1327 :param prereleases:
1328 Whether or not to match prereleases with this SpecifierSet. If set to
1329 ``None`` (the default), it will follow the recommendation from :pep:`440`
1330 and match prereleases, as there are no other versions.
1331 :param installed:
1332 Whether or not the item is installed. If set to ``True``, it will
1333 accept prerelease versions even if the specifier does not allow them.
1335 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")
1336 True
1337 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3"))
1338 True
1339 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")
1340 False
1341 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")
1342 True
1343 >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1")
1344 False
1345 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
1346 True
1348 .. versionchanged:: 26.0
1350 With ``prereleases=None``, a prerelease now matches. A single
1351 version has no alternatives, so the :pep:`440` rule to accept
1352 prereleases when nothing else satisfies the specifiers applies.
1353 Earlier versions rejected it. An unparsable version now returns
1354 ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`.
1355 """
1356 version = coerce_version(item)
1358 if version is not None and installed and version.is_prerelease:
1359 prereleases = True
1361 # When item is a string and === is involved, keep it as-is
1362 # so the comparison isn't done against the normalized form.
1363 if version is None or (self._has_arbitrary and not isinstance(item, Version)):
1364 check_item = item
1365 else:
1366 check_item = version
1368 # Fast path: a parseable, local-free version against a rangelike set.
1369 # A local on ``version`` needs PEP 440 stripping that the range path
1370 # applies.
1371 if (
1372 version is not None
1373 and not self._has_arbitrary
1374 and version.local is None
1375 and self._specs
1376 ):
1377 if version.is_prerelease and (
1378 prereleases is False
1379 or (prereleases is None and self._prereleases is False)
1380 ):
1381 return False
1383 bounds = self._ranges
1384 if bounds is None:
1385 # Per-spec ``_fast_match`` answers a set of simple specifiers
1386 # without folding anything. If a spec needs the range path,
1387 # fold the intersected bounds once and cache them so repeated
1388 # checks on the same set stay cheap.
1389 for spec in self._specs:
1390 match = _fast_match(spec, version)
1391 if match is None:
1392 break
1393 if not match:
1394 return False
1395 else:
1396 return True
1398 bounds = self._ranges = self._get_ranges()
1400 return matches_bounds_only(bounds, version)
1402 return bool(list(self.filter([check_item], prereleases=prereleases)))
1404 @typing.overload
1405 def filter(
1406 self,
1407 iterable: Iterable[UnparsedVersionVar],
1408 prereleases: bool | None = None,
1409 key: None = ...,
1410 ) -> Iterator[UnparsedVersionVar]: ...
1412 @typing.overload
1413 def filter(
1414 self,
1415 iterable: Iterable[T],
1416 prereleases: bool | None = None,
1417 key: Callable[[T], UnparsedVersion] = ...,
1418 ) -> Iterator[T]: ...
1420 def filter(
1421 self,
1422 iterable: Iterable[Any],
1423 prereleases: bool | None = None,
1424 key: Callable[[Any], UnparsedVersion] | None = None,
1425 ) -> Iterator[Any]:
1426 """Filter items in the given iterable, that match the specifiers in this set.
1428 :param iterable:
1429 An iterable that can contain version strings and
1430 :class:`~packaging.version.Version` instances. The items in the
1431 iterable will be filtered according to the specifier.
1432 :param prereleases:
1433 Whether or not to allow prereleases in the returned iterator. If set to
1434 ``None`` (the default), it will follow the recommendation from :pep:`440`
1435 and match prereleases if there are no other versions.
1436 :param key:
1437 A callable that takes a single argument (an item from the iterable) and
1438 returns a version string or :class:`~packaging.version.Version`
1439 instance to be used for filtering.
1441 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
1442 ['1.3']
1443 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))
1444 ['1.3', <Version('1.4')>]
1445 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))
1446 ['1.5a1']
1447 >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
1448 ['1.3', '1.5a1']
1449 >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
1450 ['1.3', '1.5a1']
1451 >>> list(SpecifierSet(">=1.2.3").filter(
1452 ... [{"ver": "1.2"}, {"ver": "1.3"}],
1453 ... key=lambda x: x["ver"]))
1454 [{'ver': '1.3'}]
1456 An "empty" SpecifierSet will filter items based on the presence of prerelease
1457 versions in the set.
1459 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"]))
1460 ['1.3']
1461 >>> list(SpecifierSet("").filter(["1.5a1"]))
1462 ['1.5a1']
1463 >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"]))
1464 ['1.3', '1.5a1']
1465 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))
1466 ['1.3', '1.5a1']
1468 .. versionchanged:: 26.0
1470 Prerelease filtering now follows the PEP 440 recommendation of
1471 yielding prereleases only when no final release is present.
1473 .. versionchanged:: 26.1
1475 Added the ``key`` parameter.
1476 """
1477 # Determine if we're forcing a prerelease or not, if we're not forcing
1478 # one for this particular filter call, then we'll use whatever the
1479 # SpecifierSet thinks for whether or not we should support prereleases.
1480 if prereleases is None and self.prereleases is not None:
1481 prereleases = self.prereleases
1483 if self._specs:
1484 if self._has_arbitrary:
1485 # Slow path for ===
1486 specs = self._specs
1487 matches = (
1488 item
1489 for item in iterable
1490 if all(
1491 s.contains(item if key is None else key(item), prereleases=True)
1492 for s in specs
1493 )
1494 )
1495 return _apply_prereleases_filter(matches, key, prereleases)
1497 ranges = self._ranges
1498 if ranges is None:
1499 ranges = self._get_ranges()
1500 return filter_by_ranges(ranges, iterable, key, prereleases)
1502 # Empty SpecifierSet.
1503 return _apply_prereleases_filter(iterable, key, prereleases)
1506def _pep440_filter_prereleases(
1507 iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None
1508) -> Iterator[Any]:
1509 """Filter per PEP 440: exclude prereleases unless no finals exist."""
1510 # Two lists used:
1511 # * all_nonfinal to preserve order if no finals exist
1512 # * arbitrary_strings for streaming when first final found
1513 all_nonfinal: list[Any] = []
1514 arbitrary_strings: list[Any] = []
1516 found_final = False
1517 for item in iterable:
1518 parsed = coerce_version(item if key is None else key(item))
1520 if parsed is None:
1521 # Arbitrary strings are always included as it is not
1522 # possible to determine if they are prereleases,
1523 # and they have already passed all specifiers.
1524 if found_final:
1525 yield item
1526 else:
1527 arbitrary_strings.append(item)
1528 all_nonfinal.append(item)
1529 continue
1531 if not parsed.is_prerelease:
1532 # Final release found - flush arbitrary strings, then yield
1533 if not found_final:
1534 yield from arbitrary_strings
1535 found_final = True
1536 yield item
1537 continue
1539 # Prerelease - buffer if no finals yet, otherwise skip
1540 if not found_final:
1541 all_nonfinal.append(item)
1543 # No finals found - yield all buffered items
1544 if not found_final:
1545 yield from all_nonfinal