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
13import abc
14import re
15import typing
16from typing import (
17 TYPE_CHECKING,
18 Any,
19 Final,
20 TypeVar,
21)
23from ._ranges import (
24 FULL_RANGE,
25 bounds_for_spec,
26 coerce_version,
27 filter_by_ranges,
28 intersect_specifier_bounds,
29 matches_bounds_only,
30 ranges_are_prerelease_only,
31 resolve_prereleases,
32 trim_release,
33)
34from .utils import canonicalize_version
35from .version import Version
37if TYPE_CHECKING:
38 from collections.abc import Callable, Iterable, Iterator, Sequence
39 from typing import TypeGuard
41 from . import ranges
42 from ._ranges import Interval
45__all__ = [
46 "BaseSpecifier",
47 "InvalidSpecifier",
48 "Specifier",
49 "SpecifierSet",
50]
53def __dir__() -> list[str]:
54 return __all__
57def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]:
58 return (
59 isinstance(spec, tuple)
60 and len(spec) == 2
61 and isinstance(spec[0], str)
62 and isinstance(spec[1], str)
63 )
66def _validate_pre(pre: object, /) -> TypeGuard[bool | None]:
67 return pre is None or isinstance(pre, bool)
70T = TypeVar("T")
71UnparsedVersion = Version | str
72UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)
75# Operators whose result is just a direct Version comparison, given a parsed
76# item with no local. ``<=``/``==``/``!=`` need that no-local guard because
77# PEP 440 strips locals on those; ``>=`` works regardless.
78_DIRECT_COMPARE_OPS: dict[str, Callable[[Version, Version], bool]] = {
79 ">=": Version.__ge__,
80 "<=": Version.__le__,
81 "==": Version.__eq__,
82 "!=": Version.__ne__,
83}
86def _fast_match(specifier: Specifier, parsed: Version) -> bool | None:
87 """Match ``parsed`` against ``specifier`` without building a range.
89 Handles ``>=``, ``<=``, ``==``, ``!=``, ``<``, ``>`` when the spec is
90 not a wildcard and ``parsed`` has no local. Returns ``None`` when the
91 range path must be used. Pre-release policy is left to the caller.
92 """
93 op_str, ver_str = specifier._spec
94 if ver_str.endswith(".*") or parsed.local is not None:
95 return None
97 direct_compare = _DIRECT_COMPARE_OPS.get(op_str)
98 if direct_compare is not None:
99 return direct_compare(parsed, specifier._require_spec_version(ver_str))
101 if op_str in ("<", ">"):
102 spec_v = specifier._require_spec_version(ver_str)
103 # ``<V``/``>V`` carve out V's family (pre/dev/post); that only
104 # matters when parsed shares V's epoch and trimmed release.
105 # Otherwise a direct cmpkey comparison is correct.
106 if parsed.epoch != spec_v.epoch or trim_release(parsed.release) != trim_release(
107 spec_v.release
108 ):
109 return parsed < spec_v if op_str == "<" else parsed > spec_v
110 return None
112 return None
115class InvalidSpecifier(ValueError):
116 """
117 Raised when attempting to create a :class:`Specifier` with a specifier
118 string that is invalid.
120 >>> Specifier("lolwat")
121 Traceback (most recent call last):
122 ...
123 packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat'
124 """
127class BaseSpecifier(metaclass=abc.ABCMeta):
128 """
129 Abstract base class for :class:`Specifier` and :class:`SpecifierSet`.
130 """
132 __slots__ = ()
133 __match_args__ = ("_str",)
135 @property
136 def _str(self) -> str:
137 """Internal property for match_args"""
138 return str(self)
140 @abc.abstractmethod
141 def __str__(self) -> str:
142 """
143 Returns the str representation of this Specifier-like object. This
144 should be representative of the Specifier itself.
145 """
147 @abc.abstractmethod
148 def __hash__(self) -> int:
149 """
150 Returns a hash value for this Specifier-like object.
151 """
153 @abc.abstractmethod
154 def __eq__(self, other: object) -> bool:
155 """
156 Returns a boolean representing whether or not the two Specifier-like
157 objects are equal.
159 :param other: The other object to check against.
160 """
162 @property
163 @abc.abstractmethod
164 def prereleases(self) -> bool | None:
165 """Whether or not pre-releases as a whole are allowed.
167 This can be set to either ``True`` or ``False`` to explicitly enable or disable
168 prereleases or it can be set to ``None`` (the default) to use default semantics.
169 """
171 @prereleases.setter # noqa: B027
172 def prereleases(self, value: bool) -> None:
173 """Setter for :attr:`prereleases`.
175 :param value: The value to set.
176 """
178 @abc.abstractmethod
179 def contains(self, item: str, prereleases: bool | None = None) -> bool:
180 """
181 Determines if the given item is contained within this specifier.
182 """
184 @typing.overload
185 def filter(
186 self,
187 iterable: Iterable[UnparsedVersionVar],
188 prereleases: bool | None = None,
189 key: None = ...,
190 ) -> Iterator[UnparsedVersionVar]: ...
192 @typing.overload
193 def filter(
194 self,
195 iterable: Iterable[T],
196 prereleases: bool | None = None,
197 key: Callable[[T], UnparsedVersion] = ...,
198 ) -> Iterator[T]: ...
200 @abc.abstractmethod
201 def filter(
202 self,
203 iterable: Iterable[Any],
204 prereleases: bool | None = None,
205 key: Callable[[Any], UnparsedVersion] | None = None,
206 ) -> Iterator[Any]:
207 """
208 Takes an iterable of items and filters them so that only items which
209 are contained within this specifier are allowed in it.
210 """
213class Specifier(BaseSpecifier):
214 """This class abstracts handling of version specifiers.
216 .. tip::
218 It is generally not required to instantiate this manually. You should instead
219 prefer to work with :class:`SpecifierSet` instead, which can parse
220 comma-separated version specifiers (which is what package metadata contains).
222 Instances are safe to serialize with :mod:`pickle`. They use a stable
223 format so the same pickle can be loaded in future packaging releases.
225 .. versionchanged:: 26.2
227 Added a stable pickle format. Pickles created with packaging 26.2+ can
228 be unpickled with future releases. Backward compatibility with pickles
229 from packaging < 26.2 is supported but may be removed in a future
230 release.
231 """
233 __slots__ = (
234 "_prereleases",
235 "_ranges",
236 "_spec",
237 "_spec_version",
238 )
240 _specifier_regex_str = r"""
241 (?:
242 (?:
243 # The identity operators allow for an escape hatch that will
244 # do an exact string match of the version you wish to install.
245 # This will not be parsed by PEP 440 and we cannot determine
246 # any semantic meaning from it. This operator is discouraged
247 # but included entirely as an escape hatch.
248 === # Only match for the identity operator
249 \s*
250 [^\s;)]* # The arbitrary version can be just about anything,
251 # we match everything except for whitespace, a
252 # semi-colon for marker support, and a closing paren
253 # since versions can be enclosed in them.
254 )
255 |
256 (?:
257 # The (non)equality operators allow for wild card and local
258 # versions to be specified so we have to define these two
259 # operators separately to enable that.
260 (?:==|!=) # Only match for equals and not equals
262 \s*
263 v?
264 (?:[0-9]+!)? # epoch
265 [0-9]+(?:\.[0-9]+)* # release
267 # You cannot use a wild card and a pre-release, post-release, a dev or
268 # local version together so group them with a | and make them optional.
269 (?:
270 \.\* # Wild card syntax of .*
271 |
272 (?a: # pre release
273 [-_\.]?
274 (alpha|beta|preview|pre|a|b|c|rc)
275 [-_\.]?
276 [0-9]*
277 )?
278 (?a: # post release
279 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
280 )?
281 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
282 (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
283 )?
284 )
285 |
286 (?:
287 # The compatible operator requires at least two digits in the
288 # release segment.
289 (?:~=) # Only match for the compatible operator
291 \s*
292 v?
293 (?:[0-9]+!)? # epoch
294 [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *)
295 (?a: # pre release
296 [-_\.]?
297 (alpha|beta|preview|pre|a|b|c|rc)
298 [-_\.]?
299 [0-9]*
300 )?
301 (?a: # post release
302 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
303 )?
304 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
305 )
306 |
307 (?:
308 # All other operators only allow a sub set of what the
309 # (non)equality operators do. Specifically they do not allow
310 # local versions to be specified nor do they allow the prefix
311 # matching wild cards.
312 (?:<=|>=|<|>)
314 \s*
315 v?
316 (?:[0-9]+!)? # epoch
317 [0-9]+(?:\.[0-9]+)* # release
318 (?a: # pre release
319 [-_\.]?
320 (alpha|beta|preview|pre|a|b|c|rc)
321 [-_\.]?
322 [0-9]*
323 )?
324 (?a: # post release
325 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
326 )?
327 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
328 )
329 )
330 """
332 _regex = re.compile(
333 r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE
334 )
336 # Legacy unused attribute, kept for backward compatibility
337 _operators: Final = {
338 "~=": "compatible",
339 "==": "equal",
340 "!=": "not_equal",
341 "<=": "less_than_equal",
342 ">=": "greater_than_equal",
343 "<": "less_than",
344 ">": "greater_than",
345 "===": "arbitrary",
346 }
348 def __init__(self, spec: str = "", prereleases: bool | None = None) -> None:
349 """Initialize a Specifier instance.
351 :param spec:
352 The string representation of a specifier which will be parsed and
353 normalized before use.
354 :param prereleases:
355 This tells the specifier if it should accept prerelease versions if
356 applicable or not. The default of ``None`` will autodetect it from the
357 given specifiers.
358 :raises InvalidSpecifier:
359 If the given specifier is invalid (i.e. bad syntax).
360 """
361 if not self._regex.fullmatch(spec):
362 raise InvalidSpecifier(f"Invalid specifier: {spec!r}")
364 spec = spec.strip()
365 if spec.startswith("==="):
366 operator, version = spec[:3], spec[3:].strip()
367 elif spec.startswith(("~=", "==", "!=", "<=", ">=")):
368 operator, version = spec[:2], spec[2:].strip()
369 else:
370 operator, version = spec[:1], spec[1:].strip()
372 self._spec: tuple[str, str] = (operator, version)
374 # Store whether or not this Specifier should accept prereleases
375 self._prereleases = prereleases
377 # Specifier version cache
378 self._spec_version: tuple[str, Version] | None = None
380 # Version range cache (populated by _to_ranges)
381 self._ranges: Sequence[Interval] | None = None
383 def _get_spec_version(self, version: str) -> Version | None:
384 """One element cache, as only one spec Version is needed per Specifier."""
385 if self._spec_version is not None and self._spec_version[0] == version:
386 return self._spec_version[1]
388 version_specifier = coerce_version(version)
389 if version_specifier is None:
390 return None
392 self._spec_version = (version, version_specifier)
393 return version_specifier
395 def _require_spec_version(self, version: str) -> Version:
396 """Get spec version, asserting it's valid (not for === operator).
398 This method should only be called for operators where version
399 strings are guaranteed to be valid PEP 440 versions (not ===).
400 """
401 spec_version = self._get_spec_version(version)
402 assert spec_version is not None
403 return spec_version
405 def _to_ranges(self) -> Sequence[Interval]:
406 """Convert this specifier to sorted, non-overlapping version ranges.
408 Each standard operator maps to one or two ranges. ``===`` is
409 modeled as full range (actual check done separately). Cached.
410 """
411 if self._ranges is not None:
412 return self._ranges
414 op = self.operator
415 ver_str = self.version
417 if op == "===":
418 result: Sequence[Interval] = FULL_RANGE
419 else:
420 version = self._require_spec_version(ver_str.removesuffix(".*"))
421 result = bounds_for_spec(op, ver_str, version)
423 self._ranges = result
424 return result
426 @property
427 def prereleases(self) -> bool | None:
428 # If there is an explicit prereleases set for this, then we'll just
429 # blindly use that.
430 if self._prereleases is not None:
431 return self._prereleases
433 # Only the "!=" operator does not imply prereleases when
434 # the version in the specifier is a prerelease.
435 operator, version_str = self._spec
436 if operator == "!=":
437 return False
439 # The == specifier with trailing .* cannot include prereleases
440 # e.g. "==1.0a1.*" is not valid.
441 if operator == "==" and version_str.endswith(".*"):
442 return False
444 # "===" can have arbitrary string versions, so we cannot parse
445 # those, we take prereleases as unknown (None) for those.
446 version = self._get_spec_version(version_str)
447 if version is None:
448 return None
450 # For all other operators, use the check if spec Version
451 # object implies pre-releases.
452 return version.is_prerelease
454 @prereleases.setter
455 def prereleases(self, value: bool | None) -> None:
456 self._prereleases = value
458 def __getstate__(self) -> tuple[tuple[str, str], bool | None]:
459 # Return state as a 2-item tuple for compactness:
460 # ((operator, version), prereleases)
461 # Cache members are excluded and will be recomputed on demand.
462 return (self._spec, self._prereleases)
464 def __setstate__(self, state: object) -> None:
465 # Always discard cached values - they will be recomputed on demand.
466 self._spec_version = None
467 self._ranges = None
469 if isinstance(state, tuple):
470 if len(state) == 2:
471 # New format (26.2+): ((operator, version), prereleases)
472 spec, prereleases = state
473 if _validate_spec(spec) and _validate_pre(prereleases):
474 self._spec = spec
475 self._prereleases = prereleases
476 return
477 if len(state) == 2 and isinstance(state[1], dict):
478 # Format (packaging 26.0-26.1): (None, {slot: value}).
479 _, slot_dict = state
480 spec = slot_dict.get("_spec")
481 prereleases = slot_dict.get("_prereleases", "invalid")
482 if _validate_spec(spec) and _validate_pre(prereleases):
483 self._spec = spec
484 self._prereleases = prereleases
485 return
486 if isinstance(state, dict):
487 # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
488 spec = state.get("_spec")
489 prereleases = state.get("_prereleases", "invalid")
490 if _validate_spec(spec) and _validate_pre(prereleases):
491 self._spec = spec
492 self._prereleases = prereleases
493 return
495 raise TypeError(f"Cannot restore Specifier from {state!r}")
497 @property
498 def operator(self) -> str:
499 """The operator of this specifier.
501 >>> Specifier("==1.2.3").operator
502 '=='
503 """
504 return self._spec[0]
506 @property
507 def version(self) -> str:
508 """The version of this specifier.
510 >>> Specifier("==1.2.3").version
511 '1.2.3'
512 """
513 return self._spec[1]
515 def __repr__(self) -> str:
516 """A representation of the Specifier that shows all internal state.
518 >>> Specifier('>=1.0.0')
519 <Specifier('>=1.0.0')>
520 >>> Specifier('>=1.0.0', prereleases=False)
521 <Specifier('>=1.0.0', prereleases=False)>
522 >>> Specifier('>=1.0.0', prereleases=True)
523 <Specifier('>=1.0.0', prereleases=True)>
524 """
525 pre = (
526 f", prereleases={self.prereleases!r}"
527 if self._prereleases is not None
528 else ""
529 )
531 return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
533 def __str__(self) -> str:
534 """A string representation of the Specifier that can be round-tripped.
536 >>> str(Specifier('>=1.0.0'))
537 '>=1.0.0'
538 >>> str(Specifier('>=1.0.0', prereleases=False))
539 '>=1.0.0'
540 """
541 return "{}{}".format(*self._spec)
543 @property
544 def _canonical_spec(self) -> tuple[str, str]:
545 operator, version = self._spec
546 if operator == "===" or version.endswith(".*"):
547 return operator, version
549 spec_version = self._require_spec_version(version)
551 canonical_version = canonicalize_version(
552 spec_version, strip_trailing_zero=(operator != "~=")
553 )
555 return operator, canonical_version
557 def __hash__(self) -> int:
558 return hash(self._canonical_spec)
560 def __eq__(self, other: object) -> bool:
561 """Whether or not the two Specifier-like objects are equal.
563 :param other: The other object to check against.
565 The value of :attr:`prereleases` is ignored.
567 >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")
568 True
569 >>> (Specifier("==1.2.3", prereleases=False) ==
570 ... Specifier("==1.2.3", prereleases=True))
571 True
572 >>> Specifier("==1.2.3") == "==1.2.3"
573 True
574 >>> Specifier("==1.2.3") == Specifier("==1.2.4")
575 False
576 >>> Specifier("==1.2.3") == Specifier("~=1.2.3")
577 False
578 """
579 if isinstance(other, str):
580 try:
581 other = self.__class__(str(other))
582 except InvalidSpecifier:
583 return NotImplemented
584 elif not isinstance(other, self.__class__):
585 return NotImplemented
587 return self._canonical_spec == other._canonical_spec
589 def __contains__(self, item: str | Version) -> bool:
590 """Return whether or not the item is contained in this specifier.
592 :param item: The item to check for.
594 This is used for the ``in`` operator and behaves the same as
595 :meth:`contains` with no ``prereleases`` argument passed.
597 >>> "1.2.3" in Specifier(">=1.2.3")
598 True
599 >>> Version("1.2.3") in Specifier(">=1.2.3")
600 True
601 >>> "1.0.0" in Specifier(">=1.2.3")
602 False
603 >>> "1.3.0a1" in Specifier(">=1.2.3")
604 True
605 >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)
606 True
607 """
608 return self.contains(item)
610 def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool:
611 """Return whether or not the item is contained in this specifier.
613 :param item:
614 The item to check for, which can be a version string or a
615 :class:`~packaging.version.Version` instance.
616 :param prereleases:
617 Whether or not to match prereleases with this Specifier. If set to
618 ``None`` (the default), it will follow the recommendation from
619 :pep:`440` and match prereleases, as there are no other versions.
621 >>> Specifier(">=1.2.3").contains("1.2.3")
622 True
623 >>> Specifier(">=1.2.3").contains(Version("1.2.3"))
624 True
625 >>> Specifier(">=1.2.3").contains("1.0.0")
626 False
627 >>> Specifier(">=1.2.3").contains("1.3.0a1")
628 True
629 >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1")
630 False
631 >>> Specifier(">=1.2.3").contains("1.3.0a1")
632 True
634 .. versionchanged:: 26.0
636 With ``prereleases=None``, a prerelease now matches. A single
637 version has no alternatives, so the :pep:`440` rule to accept
638 prereleases when nothing else satisfies the specifier applies.
639 Earlier versions rejected it. An unparsable version now returns
640 ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`.
641 """
642 # ``===`` compares the raw string, so a Version parse here would
643 # be wasted.
644 if self._spec[0] == "===":
645 return bool(list(self.filter([item], prereleases=prereleases)))
647 parsed = coerce_version(item)
648 if parsed is None:
649 # Standard operators never match an unparsable input.
650 return False
652 if prereleases is None:
653 prereleases = resolve_prereleases(self._prereleases, self.prereleases)
655 if prereleases is False and parsed.is_prerelease:
656 return False
658 # ``_fast_match`` answers the simple operators without building a
659 # range; otherwise fall back to the engine's bounds membership.
660 match = _fast_match(self, parsed)
661 if match is not None:
662 return match
664 return matches_bounds_only(self._to_ranges(), parsed)
666 @typing.overload
667 def filter(
668 self,
669 iterable: Iterable[UnparsedVersionVar],
670 prereleases: bool | None = None,
671 key: None = ...,
672 ) -> Iterator[UnparsedVersionVar]: ...
674 @typing.overload
675 def filter(
676 self,
677 iterable: Iterable[T],
678 prereleases: bool | None = None,
679 key: Callable[[T], UnparsedVersion] = ...,
680 ) -> Iterator[T]: ...
682 def filter(
683 self,
684 iterable: Iterable[Any],
685 prereleases: bool | None = None,
686 key: Callable[[Any], UnparsedVersion] | None = None,
687 ) -> Iterator[Any]:
688 """Filter items in the given iterable, that match the specifier.
690 :param iterable:
691 An iterable that can contain version strings and
692 :class:`~packaging.version.Version` instances. The items in the
693 iterable will be filtered according to the specifier.
694 :param prereleases:
695 Whether or not to allow prereleases in the returned iterator. If set to
696 ``None`` (the default), it will follow the recommendation from :pep:`440`
697 and match prereleases if there are no other versions.
698 :param key:
699 A callable that takes a single argument (an item from the iterable) and
700 returns a version string or :class:`~packaging.version.Version`
701 instance to be used for filtering.
703 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
704 ['1.3']
705 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))
706 ['1.2.3', '1.3', <Version('1.4')>]
707 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))
708 ['1.5a1']
709 >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
710 ['1.3', '1.5a1']
711 >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
712 ['1.3', '1.5a1']
713 >>> list(Specifier(">=1.2.3").filter(
714 ... [{"ver": "1.2"}, {"ver": "1.3"}],
715 ... key=lambda x: x["ver"]))
716 [{'ver': '1.3'}]
718 .. versionchanged:: 26.1
720 Added the ``key`` parameter.
721 """
722 if prereleases is None:
723 prereleases = resolve_prereleases(self._prereleases, self.prereleases)
725 if self.operator == "===":
726 spec_lower = self.version.lower()
727 matches = (
728 item
729 for item in iterable
730 if str(item if key is None else key(item)).lower() == spec_lower
731 )
732 return _apply_prereleases_filter(matches, key, prereleases)
734 return filter_by_ranges(self._to_ranges(), iterable, key, prereleases)
737def _apply_prereleases_filter(
738 matches: Iterable[Any],
739 key: Callable[[Any], UnparsedVersion] | None,
740 prereleases: bool | None,
741) -> Iterator[Any]:
742 """Apply ``prereleases=`` handling to an already-matched iterable.
744 ``None`` means PEP 440 default (buffer pre-releases until a final
745 appears); ``True`` yields everything; ``False`` drops pre-releases.
746 """
747 if prereleases is None:
748 return _pep440_filter_prereleases(matches, key)
749 if prereleases:
750 return iter(matches)
751 return (
752 item
753 for item in matches
754 if (parsed := coerce_version(item if key is None else key(item))) is None
755 or not parsed.is_prerelease
756 )
759class SpecifierSet(BaseSpecifier):
760 """This class abstracts handling of a set of version specifiers.
762 It can be passed a single specifier (``>=3.0``), a comma-separated list of
763 specifiers (``>=3.0,!=3.1``), or no specifier at all.
765 Instances are safe to serialize with :mod:`pickle`. They use a stable
766 format so the same pickle can be loaded in future packaging
767 releases.
769 .. versionchanged:: 26.2
771 Added a stable pickle format. Pickles created with
772 packaging 26.2+ can be unpickled with future releases.
773 Backward compatibility with pickles from
774 packaging < 26.2 is supported but may be removed in a future
775 release.
776 """
778 __slots__ = (
779 "_canonicalized",
780 "_has_arbitrary",
781 "_is_unsatisfiable",
782 "_prereleases",
783 "_ranges",
784 "_specs",
785 )
787 def __init__(
788 self,
789 specifiers: str | Iterable[Specifier] = "",
790 prereleases: bool | None = None,
791 ) -> None:
792 """Initialize a SpecifierSet instance.
794 :param specifiers:
795 The string representation of a specifier or a comma-separated list of
796 specifiers which will be parsed and normalized before use.
797 May also be an iterable of ``Specifier`` instances, which will be used
798 as is.
799 :param prereleases:
800 This tells the SpecifierSet if it should accept prerelease versions if
801 applicable or not. The default of ``None`` will autodetect it from the
802 given specifiers.
804 :raises InvalidSpecifier:
805 If the given ``specifiers`` are not parseable than this exception will be
806 raised.
807 """
809 if isinstance(specifiers, str):
810 # Split on `,` to break each individual specifier into its own item, and
811 # strip each item to remove leading/trailing whitespace.
812 split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
814 self._specs: tuple[Specifier, ...] = tuple(map(Specifier, split_specifiers))
815 # Fast substring check; avoids iterating parsed specs.
816 self._has_arbitrary = "===" in specifiers
817 else:
818 self._specs = tuple(specifiers)
819 # Substring check works for both Specifier objects and plain
820 # strings (setuptools passes lists of strings).
821 self._has_arbitrary = any("===" in str(s) for s in self._specs)
823 self._canonicalized = len(self._specs) <= 1
824 self._is_unsatisfiable: bool | None = None
825 self._ranges: Sequence[Interval] | None = None
827 # Store our prereleases value so we can use it later to determine if
828 # we accept prereleases or not.
829 self._prereleases = prereleases
831 def _canonical_specs(self) -> tuple[Specifier, ...]:
832 """Deduplicate, sort, and cache specs for order-sensitive operations."""
833 if not self._canonicalized:
834 self._specs = tuple(dict.fromkeys(sorted(self._specs, key=str)))
835 self._canonicalized = True
836 return self._specs
838 @property
839 def prereleases(self) -> bool | None:
840 # If we have been given an explicit prerelease modifier, then we'll
841 # pass that through here.
842 if self._prereleases is not None:
843 return self._prereleases
845 # If we don't have any specifiers, and we don't have a forced value,
846 # then we'll just return None since we don't know if this should have
847 # pre-releases or not.
848 if not self._specs:
849 return None
851 # Otherwise we'll see if any of the given specifiers accept
852 # prereleases, if any of them do we'll return True, otherwise False.
853 if any(s.prereleases for s in self._specs):
854 return True
856 return None
858 @prereleases.setter
859 def prereleases(self, value: bool | None) -> None:
860 self._prereleases = value
861 self._is_unsatisfiable = None
863 def __getstate__(self) -> tuple[tuple[Specifier, ...], bool | None]:
864 # Return state as a 2-item tuple for compactness:
865 # (specs, prereleases)
866 # Cache members are excluded and will be recomputed on demand.
867 return (self._specs, self._prereleases)
869 def __setstate__(self, state: object) -> None:
870 # Always discard cached values - they will be recomputed on demand.
871 self._ranges = None
872 self._is_unsatisfiable = None
874 if isinstance(state, tuple):
875 if len(state) == 2:
876 # New format (26.2+): (specs, prereleases)
877 specs, prereleases = state
878 if (
879 isinstance(specs, tuple)
880 and all(isinstance(s, Specifier) for s in specs)
881 and _validate_pre(prereleases)
882 ):
883 self._specs = specs
884 self._prereleases = prereleases
885 self._canonicalized = len(specs) <= 1
886 self._has_arbitrary = any("===" in str(s) for s in specs)
887 return
888 if len(state) == 2 and isinstance(state[1], dict):
889 # Format (packaging 26.0-26.1): (None, {slot: value}).
890 _, slot_dict = state
891 specs = slot_dict.get("_specs", ())
892 prereleases = slot_dict.get("_prereleases")
893 # Convert frozenset to tuple (26.0 stored as frozenset)
894 if isinstance(specs, frozenset):
895 specs = tuple(sorted(specs, key=str))
896 if (
897 isinstance(specs, tuple)
898 and all(isinstance(s, Specifier) for s in specs)
899 and _validate_pre(prereleases)
900 ):
901 self._specs = specs
902 self._prereleases = prereleases
903 self._canonicalized = len(self._specs) <= 1
904 self._has_arbitrary = any("===" in str(s) for s in self._specs)
905 return
906 if isinstance(state, dict):
907 # Old format (packaging <= 25.x, no __slots__): state is a plain dict.
908 specs = state.get("_specs", ())
909 prereleases = state.get("_prereleases")
910 # Convert frozenset to tuple (26.0 stored as frozenset)
911 if isinstance(specs, frozenset):
912 specs = tuple(sorted(specs, key=str))
913 if (
914 isinstance(specs, tuple)
915 and all(isinstance(s, Specifier) for s in specs)
916 and _validate_pre(prereleases)
917 ):
918 self._specs = specs
919 self._prereleases = prereleases
920 self._canonicalized = len(self._specs) <= 1
921 self._has_arbitrary = any("===" in str(s) for s in self._specs)
922 return
924 raise TypeError(f"Cannot restore SpecifierSet from {state!r}")
926 def __repr__(self) -> str:
927 """A representation of the specifier set that shows all internal state.
929 Note that the ordering of the individual specifiers within the set may not
930 match the input string.
932 >>> SpecifierSet('>=1.0.0,!=2.0.0')
933 <SpecifierSet('!=2.0.0,>=1.0.0')>
934 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)
935 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)>
936 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)
937 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)>
938 """
939 pre = (
940 f", prereleases={self.prereleases!r}"
941 if self._prereleases is not None
942 else ""
943 )
945 return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
947 def __str__(self) -> str:
948 """A string representation of the specifier set that can be round-tripped.
950 Note that the ordering of the individual specifiers within the set may not
951 match the input string.
953 >>> str(SpecifierSet(">=1.0.0,!=1.0.1"))
954 '!=1.0.1,>=1.0.0'
955 >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False))
956 '!=1.0.1,>=1.0.0'
957 """
958 return ",".join(str(s) for s in self._canonical_specs())
960 def __hash__(self) -> int:
961 return hash(self._canonical_specs())
963 def __and__(self, other: SpecifierSet | str) -> SpecifierSet:
964 """Return a SpecifierSet which is a combination of the two sets.
966 :param other: The other object to combine with.
968 >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1'
969 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
970 >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1')
971 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
972 """
973 if isinstance(other, str):
974 other = SpecifierSet(other)
975 elif not isinstance(other, SpecifierSet):
976 return NotImplemented
978 specifier = SpecifierSet()
979 specifier._specs = self._specs + other._specs
980 specifier._canonicalized = len(specifier._specs) <= 1
981 specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary
983 # Combine prerelease settings: use common or non-None value
984 if self._prereleases is None or self._prereleases == other._prereleases:
985 specifier._prereleases = other._prereleases
986 elif other._prereleases is None:
987 specifier._prereleases = self._prereleases
988 else:
989 raise ValueError(
990 "Cannot combine SpecifierSets with True and False prerelease overrides."
991 )
993 return specifier
995 def __eq__(self, other: object) -> bool:
996 """Whether or not the two SpecifierSet-like objects are equal.
998 :param other: The other object to check against.
1000 The value of :attr:`prereleases` is ignored.
1002 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1")
1003 True
1004 >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) ==
1005 ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True))
1006 True
1007 >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1"
1008 True
1009 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0")
1010 False
1011 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2")
1012 False
1013 """
1014 if isinstance(other, (str, Specifier)):
1015 other = SpecifierSet(str(other))
1016 elif not isinstance(other, SpecifierSet):
1017 return NotImplemented
1019 return self._canonical_specs() == other._canonical_specs()
1021 def __len__(self) -> int:
1022 """Returns the number of specifiers in this specifier set."""
1023 return len(self._specs)
1025 def __iter__(self) -> Iterator[Specifier]:
1026 """
1027 Returns an iterator over all the underlying :class:`Specifier` instances
1028 in this specifier set.
1030 >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str)
1031 [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>]
1032 """
1033 return iter(self._specs)
1035 def _get_ranges(self) -> Sequence[Interval]:
1036 """Intersect all specifiers into a single sequence of version ranges.
1038 Empty when unsatisfiable. Callers must ensure ``self._specs``
1039 is non-empty.
1040 """
1041 if self._ranges is not None:
1042 return self._ranges
1044 self._ranges = intersect_specifier_bounds(s._to_ranges() for s in self._specs)
1045 return self._ranges
1047 def is_unsatisfiable(self) -> bool:
1048 """Check whether this specifier set can never be satisfied.
1050 Returns True if no version can satisfy all specifiers simultaneously.
1052 >>> SpecifierSet(">=2.0,<1.0").is_unsatisfiable()
1053 True
1054 >>> SpecifierSet(">=1.0,<2.0").is_unsatisfiable()
1055 False
1056 >>> SpecifierSet("").is_unsatisfiable()
1057 False
1058 >>> SpecifierSet("==1.0,!=1.0").is_unsatisfiable()
1059 True
1061 .. versionadded:: 26.1
1062 """
1063 cached = self._is_unsatisfiable
1064 if cached is not None:
1065 return cached
1067 if not self._specs:
1068 self._is_unsatisfiable = False
1069 return False
1071 result = not self._get_ranges()
1073 if not result:
1074 result = self._check_arbitrary_unsatisfiable()
1076 if not result and self.prereleases is False:
1077 result = ranges_are_prerelease_only(self._get_ranges())
1079 self._is_unsatisfiable = result
1080 return result
1082 def _check_arbitrary_unsatisfiable(self) -> bool:
1083 """Check === (arbitrary equality) specs for unsatisfiability.
1085 === uses case-insensitive string comparison, so the only candidate
1086 that can match ``===V`` is the literal string V. This method
1087 checks whether that candidate is excluded by other specifiers.
1088 """
1089 arbitrary = [s for s in self._specs if s.operator == "==="]
1090 if not arbitrary:
1091 return False
1093 # Multiple === must agree on the same string (case-insensitive).
1094 first = arbitrary[0].version.lower()
1095 if any(s.version.lower() != first for s in arbitrary[1:]):
1096 return True
1098 # The sole candidate is the === version string. Check whether
1099 # it can satisfy every standard spec.
1100 candidate = coerce_version(arbitrary[0].version)
1102 # With prereleases=False, a prerelease candidate is excluded
1103 # by contains() before the === string check even runs.
1104 if (
1105 self.prereleases is False
1106 and candidate is not None
1107 and candidate.is_prerelease
1108 ):
1109 return True
1111 standard = [s for s in self._specs if s.operator != "==="]
1112 if not standard:
1113 return False
1115 if candidate is None:
1116 # Unparsable string cannot satisfy any standard spec.
1117 return True
1119 return not all(s.contains(candidate) for s in standard)
1121 def to_range(self) -> ranges.VersionRange:
1122 """Return the :class:`~packaging.ranges.VersionRange` this set accepts.
1124 An empty set yields the full range; an unsatisfiable set yields the
1125 empty range. ``===`` specifiers contribute literal-string admission.
1127 >>> SpecifierSet(">=1.0,<2.0").to_range()
1128 <VersionRange '[1.0, 2.0.dev0)'>
1130 .. versionadded:: 26.3
1131 """
1132 from .ranges import VersionRange # noqa: PLC0415
1134 return VersionRange._from_specifier_set(self)
1136 def _check_relation_operand(self, other: object) -> None:
1137 if not isinstance(other, SpecifierSet):
1138 raise TypeError("expected a SpecifierSet")
1139 if self._has_arbitrary or other._has_arbitrary:
1140 raise ValueError("set relations do not support === specifiers")
1142 def is_subset(self, other: SpecifierSet) -> bool:
1143 """Return whether every version matching this set also matches other.
1145 :raises ValueError:
1146 If either set uses ``===`` specifiers, or the two sets were
1147 given different ``prereleases`` arguments (unset on one side
1148 counts as different).
1149 :raises TypeError:
1150 If other is not a :class:`SpecifierSet`.
1152 >>> SpecifierSet(">=3.12,<3.13").is_subset(SpecifierSet(">=3.12"))
1153 True
1154 >>> SpecifierSet(">=3.12").is_subset(SpecifierSet(">=3.12,<3.13"))
1155 False
1157 .. versionadded:: 26.3
1158 """
1159 self._check_relation_operand(other)
1160 return self.to_range().is_subset(other.to_range())
1162 def is_superset(self, other: SpecifierSet) -> bool:
1163 """Return whether every version matching other also matches this set.
1165 :raises ValueError:
1166 If either set uses ``===`` specifiers, or the two sets were
1167 given different ``prereleases`` arguments (unset on one side
1168 counts as different).
1169 :raises TypeError:
1170 If other is not a :class:`SpecifierSet`.
1172 >>> SpecifierSet(">=3.12").is_superset(SpecifierSet(">=3.12,<3.13"))
1173 True
1175 .. versionadded:: 26.3
1176 """
1177 self._check_relation_operand(other)
1178 return self.to_range().is_superset(other.to_range())
1180 def is_disjoint(self, other: SpecifierSet) -> bool:
1181 """Return whether this set and other share no matching versions.
1183 :raises ValueError:
1184 If either set uses ``===`` specifiers, or the two sets were
1185 given different ``prereleases`` arguments (unset on one side
1186 counts as different).
1187 :raises TypeError:
1188 If other is not a :class:`SpecifierSet`.
1190 >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.12"))
1191 True
1192 >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.11"))
1193 False
1195 .. versionadded:: 26.3
1196 """
1197 self._check_relation_operand(other)
1198 return self.to_range().is_disjoint(other.to_range())
1200 def __contains__(self, item: UnparsedVersion) -> bool:
1201 """Return whether or not the item is contained in this specifier.
1203 :param item: The item to check for.
1205 This is used for the ``in`` operator and behaves the same as
1206 :meth:`contains` with no ``prereleases`` argument passed.
1208 >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1")
1209 True
1210 >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1")
1211 True
1212 >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")
1213 False
1214 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")
1215 True
1216 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)
1217 True
1218 """
1219 return self.contains(item)
1221 def contains(
1222 self,
1223 item: UnparsedVersion,
1224 prereleases: bool | None = None,
1225 installed: bool | None = None,
1226 ) -> bool:
1227 """Return whether or not the item is contained in this SpecifierSet.
1229 :param item:
1230 The item to check for, which can be a version string or a
1231 :class:`~packaging.version.Version` instance.
1232 :param prereleases:
1233 Whether or not to match prereleases with this SpecifierSet. If set to
1234 ``None`` (the default), it will follow the recommendation from :pep:`440`
1235 and match prereleases, as there are no other versions.
1236 :param installed:
1237 Whether or not the item is installed. If set to ``True``, it will
1238 accept prerelease versions even if the specifier does not allow them.
1240 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")
1241 True
1242 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3"))
1243 True
1244 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")
1245 False
1246 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")
1247 True
1248 >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1")
1249 False
1250 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
1251 True
1253 .. versionchanged:: 26.0
1255 With ``prereleases=None``, a prerelease now matches. A single
1256 version has no alternatives, so the :pep:`440` rule to accept
1257 prereleases when nothing else satisfies the specifiers applies.
1258 Earlier versions rejected it. An unparsable version now returns
1259 ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`.
1260 """
1261 version = coerce_version(item)
1263 if version is not None and installed and version.is_prerelease:
1264 prereleases = True
1266 # When item is a string and === is involved, keep it as-is
1267 # so the comparison isn't done against the normalized form.
1268 if version is None or (self._has_arbitrary and not isinstance(item, Version)):
1269 check_item = item
1270 else:
1271 check_item = version
1273 # Fast path: a parseable, local-free version against a rangelike set.
1274 # A local on ``version`` needs PEP 440 stripping that the range path
1275 # applies.
1276 if (
1277 version is not None
1278 and not self._has_arbitrary
1279 and version.local is None
1280 and self._specs
1281 ):
1282 if version.is_prerelease and (
1283 prereleases is False
1284 or (prereleases is None and self._prereleases is False)
1285 ):
1286 return False
1288 bounds = self._ranges
1289 if bounds is None:
1290 # Per-spec ``_fast_match`` answers a set of simple specifiers
1291 # without folding anything. If a spec needs the range path,
1292 # fold the intersected bounds once and cache them so repeated
1293 # checks on the same set stay cheap.
1294 for spec in self._specs:
1295 match = _fast_match(spec, version)
1296 if match is None:
1297 break
1298 if not match:
1299 return False
1300 else:
1301 return True
1303 bounds = self._ranges = self._get_ranges()
1305 return matches_bounds_only(bounds, version)
1307 return bool(list(self.filter([check_item], prereleases=prereleases)))
1309 @typing.overload
1310 def filter(
1311 self,
1312 iterable: Iterable[UnparsedVersionVar],
1313 prereleases: bool | None = None,
1314 key: None = ...,
1315 ) -> Iterator[UnparsedVersionVar]: ...
1317 @typing.overload
1318 def filter(
1319 self,
1320 iterable: Iterable[T],
1321 prereleases: bool | None = None,
1322 key: Callable[[T], UnparsedVersion] = ...,
1323 ) -> Iterator[T]: ...
1325 def filter(
1326 self,
1327 iterable: Iterable[Any],
1328 prereleases: bool | None = None,
1329 key: Callable[[Any], UnparsedVersion] | None = None,
1330 ) -> Iterator[Any]:
1331 """Filter items in the given iterable, that match the specifiers in this set.
1333 :param iterable:
1334 An iterable that can contain version strings and
1335 :class:`~packaging.version.Version` instances. The items in the
1336 iterable will be filtered according to the specifier.
1337 :param prereleases:
1338 Whether or not to allow prereleases in the returned iterator. If set to
1339 ``None`` (the default), it will follow the recommendation from :pep:`440`
1340 and match prereleases if there are no other versions.
1341 :param key:
1342 A callable that takes a single argument (an item from the iterable) and
1343 returns a version string or :class:`~packaging.version.Version`
1344 instance to be used for filtering.
1346 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
1347 ['1.3']
1348 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))
1349 ['1.3', <Version('1.4')>]
1350 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))
1351 ['1.5a1']
1352 >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
1353 ['1.3', '1.5a1']
1354 >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
1355 ['1.3', '1.5a1']
1356 >>> list(SpecifierSet(">=1.2.3").filter(
1357 ... [{"ver": "1.2"}, {"ver": "1.3"}],
1358 ... key=lambda x: x["ver"]))
1359 [{'ver': '1.3'}]
1361 An "empty" SpecifierSet will filter items based on the presence of prerelease
1362 versions in the set.
1364 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"]))
1365 ['1.3']
1366 >>> list(SpecifierSet("").filter(["1.5a1"]))
1367 ['1.5a1']
1368 >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"]))
1369 ['1.3', '1.5a1']
1370 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))
1371 ['1.3', '1.5a1']
1373 .. versionchanged:: 26.0
1375 Prerelease filtering now follows the PEP 440 recommendation of
1376 yielding prereleases only when no final release is present.
1378 .. versionchanged:: 26.1
1380 Added the ``key`` parameter.
1381 """
1382 # Determine if we're forcing a prerelease or not, if we're not forcing
1383 # one for this particular filter call, then we'll use whatever the
1384 # SpecifierSet thinks for whether or not we should support prereleases.
1385 if prereleases is None and self.prereleases is not None:
1386 prereleases = self.prereleases
1388 if self._specs:
1389 if self._has_arbitrary:
1390 # Slow path for ===
1391 specs = self._specs
1392 matches = (
1393 item
1394 for item in iterable
1395 if all(
1396 s.contains(item if key is None else key(item), prereleases=True)
1397 for s in specs
1398 )
1399 )
1400 return _apply_prereleases_filter(matches, key, prereleases)
1402 ranges = self._ranges
1403 if ranges is None:
1404 ranges = self._get_ranges()
1405 return filter_by_ranges(ranges, iterable, key, prereleases)
1407 # Empty SpecifierSet.
1408 return _apply_prereleases_filter(iterable, key, prereleases)
1411def _pep440_filter_prereleases(
1412 iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None
1413) -> Iterator[Any]:
1414 """Filter per PEP 440: exclude prereleases unless no finals exist."""
1415 # Two lists used:
1416 # * all_nonfinal to preserve order if no finals exist
1417 # * arbitrary_strings for streaming when first final found
1418 all_nonfinal: list[Any] = []
1419 arbitrary_strings: list[Any] = []
1421 found_final = False
1422 for item in iterable:
1423 parsed = coerce_version(item if key is None else key(item))
1425 if parsed is None:
1426 # Arbitrary strings are always included as it is not
1427 # possible to determine if they are prereleases,
1428 # and they have already passed all specifiers.
1429 if found_final:
1430 yield item
1431 else:
1432 arbitrary_strings.append(item)
1433 all_nonfinal.append(item)
1434 continue
1436 if not parsed.is_prerelease:
1437 # Final release found - flush arbitrary strings, then yield
1438 if not found_final:
1439 yield from arbitrary_strings
1440 found_final = True
1441 yield item
1442 continue
1444 # Prerelease - buffer if no finals yet, otherwise skip
1445 if not found_final:
1446 all_nonfinal.append(item)
1448 # No finals found - yield all buffered items
1449 if not found_final:
1450 yield from all_nonfinal