codekingpro/portable-devtools
115k
1# This file is dual licensed under the terms of the Apache License, Version2# 2.0, and the BSD License. See the LICENSE file in the root of this repository3# for complete details.4"""5.. testsetup::6 7 from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier8 from packaging.version import Version9"""10 11from __future__ import annotations12 13import abc14import enum15import functools16import itertools17import re18import sys19import typing20from typing import (21 TYPE_CHECKING,22 Any,23 Callable,24 Final,25 Iterable,26 Iterator,27 Sequence,28 TypeVar,29 Union,30)31 32from .utils import canonicalize_version33from .version import InvalidVersion, Version34 35if sys.version_info >= (3, 10):36 from typing import TypeGuard # pragma: no cover37elif TYPE_CHECKING:38 from typing_extensions import TypeGuard39 40__all__ = [41 "BaseSpecifier",42 "InvalidSpecifier",43 "Specifier",44 "SpecifierSet",45]46 47 48def __dir__() -> list[str]:49 return __all__50 51 52def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]:53 return (54 isinstance(spec, tuple)55 and len(spec) == 256 and isinstance(spec[0], str)57 and isinstance(spec[1], str)58 )59 60 61def _validate_pre(pre: object, /) -> TypeGuard[bool | None]:62 return pre is None or isinstance(pre, bool)63 64 65T = TypeVar("T")66UnparsedVersion = Union[Version, str]67UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)68CallableOperator = Callable[[Version, str], bool]69 70# The smallest possible PEP 440 version. No valid version is less than this.71_MIN_VERSION: Final[Version] = Version("0.dev0")72 73 74def _trim_release(release: tuple[int, ...]) -> tuple[int, ...]:75 """Strip trailing zeros from a release tuple for normalized comparison."""76 end = len(release)77 while end > 1 and release[end - 1] == 0:78 end -= 179 return release if end == len(release) else release[:end]80 81 82class _BoundaryKind(enum.Enum):83 """Where a boundary marker sits in the version ordering."""84 85 AFTER_LOCALS = enum.auto() # after V+local, before V.post086 AFTER_POSTS = enum.auto() # after V.postN, before next release87 88 89@functools.total_ordering90class _BoundaryVersion:91 """A point on the version line between two real PEP 440 versions.92 93 Some specifier semantics imply boundaries between real versions:94 ``<=1.0`` includes ``1.0+local`` and ``>1.0`` excludes95 ``1.0.post0``. No real :class:`Version` falls on those boundaries,96 so this class creates values that sort between the real versions97 on either side.98 99 Two kinds exist, shown relative to a base version V::100 101 V < V+local < AFTER_LOCALS(V) < V.post0 < AFTER_POSTS(V)102 103 ``AFTER_LOCALS`` sits after V and every V+local, but before104 V.post0. Upper bound of ``<=V``, ``==V``, ``!=V``.105 106 ``AFTER_POSTS`` sits after every V.postN, but before the next107 release segment. Lower bound of ``>V`` (final or pre-release V)108 to exclude post-releases per PEP 440.109 """110 111 __slots__ = ("_kind", "_trimmed_release", "version")112 113 def __init__(self, version: Version, kind: _BoundaryKind) -> None:114 self.version = version115 self._kind = kind116 self._trimmed_release = _trim_release(version.release)117 118 def _is_family(self, other: Version) -> bool:119 """Is ``other`` a version that this boundary sorts above?"""120 v = self.version121 if not (122 other.epoch == v.epoch123 and _trim_release(other.release) == self._trimmed_release124 and other.pre == v.pre125 ):126 return False127 if self._kind == _BoundaryKind.AFTER_LOCALS:128 # Local family: exact same public version (any local label).129 return other.post == v.post and other.dev == v.dev130 # Post family: same base + any post-release (or identical).131 return other.dev == v.dev or other.post is not None132 133 def __eq__(self, other: object) -> bool:134 if isinstance(other, _BoundaryVersion):135 return self.version == other.version and self._kind == other._kind136 return NotImplemented137 138 def __lt__(self, other: _BoundaryVersion | Version) -> bool:139 if isinstance(other, _BoundaryVersion):140 if self.version != other.version:141 return self.version < other.version142 return self._kind.value < other._kind.value143 return not self._is_family(other) and self.version < other144 145 def __hash__(self) -> int:146 return hash((self.version, self._kind))147 148 def __repr__(self) -> str:149 return f"{self.__class__.__name__}({self.version!r}, {self._kind.name})"150 151 152@functools.total_ordering153class _LowerBound:154 """Lower bound of a version range.155 156 A version *v* of ``None`` means unbounded below (-inf).157 At equal versions, ``[v`` sorts before ``(v`` because an inclusive158 bound starts earlier.159 """160 161 __slots__ = ("inclusive", "version")162 163 def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:164 self.version = version165 self.inclusive = inclusive166 167 def __eq__(self, other: object) -> bool:168 if not isinstance(other, _LowerBound):169 return NotImplemented # pragma: no cover170 return self.version == other.version and self.inclusive == other.inclusive171 172 def __lt__(self, other: _LowerBound) -> bool:173 if not isinstance(other, _LowerBound): # pragma: no cover174 return NotImplemented175 # -inf < anything (except -inf).176 if self.version is None:177 return other.version is not None178 if other.version is None:179 return False180 if self.version != other.version:181 return self.version < other.version182 # [v < (v: inclusive starts earlier.183 return self.inclusive and not other.inclusive184 185 def __hash__(self) -> int:186 return hash((self.version, self.inclusive))187 188 def __repr__(self) -> str:189 bracket = "[" if self.inclusive else "("190 return f"<{self.__class__.__name__} {bracket}{self.version!r}>"191 192 193@functools.total_ordering194class _UpperBound:195 """Upper bound of a version range.196 197 A version *v* of ``None`` means unbounded above (+inf).198 At equal versions, ``v)`` sorts before ``v]`` because an exclusive199 bound ends earlier.200 """201 202 __slots__ = ("inclusive", "version")203 204 def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:205 self.version = version206 self.inclusive = inclusive207 208 def __eq__(self, other: object) -> bool:209 if not isinstance(other, _UpperBound):210 return NotImplemented # pragma: no cover211 return self.version == other.version and self.inclusive == other.inclusive212 213 def __lt__(self, other: _UpperBound) -> bool:214 if not isinstance(other, _UpperBound): # pragma: no cover215 return NotImplemented216 # Nothing < +inf (except +inf itself).217 if self.version is None:218 return False219 if other.version is None:220 return True221 if self.version != other.version:222 return self.version < other.version223 # v) < v]: exclusive ends earlier.224 return not self.inclusive and other.inclusive225 226 def __hash__(self) -> int:227 return hash((self.version, self.inclusive))228 229 def __repr__(self) -> str:230 bracket = "]" if self.inclusive else ")"231 return f"<{self.__class__.__name__} {self.version!r}{bracket}>"232 233 234if typing.TYPE_CHECKING:235 _VersionOrBoundary = Union[Version, _BoundaryVersion, None]236 237 #: A single contiguous version range, represented as a238 #: (lower bound, upper bound) pair.239 _VersionRange = tuple[_LowerBound, _UpperBound]240 241_NEG_INF = _LowerBound(None, False)242_POS_INF = _UpperBound(None, False)243_FULL_RANGE: tuple[_VersionRange] = ((_NEG_INF, _POS_INF),)244 245 246def _range_is_empty(lower: _LowerBound, upper: _UpperBound) -> bool:247 """True when the range defined by *lower* and *upper* contains no versions."""248 if lower.version is None or upper.version is None:249 return False250 if lower.version == upper.version:251 return not (lower.inclusive and upper.inclusive)252 return lower.version > upper.version253 254 255def _intersect_ranges(256 left: Sequence[_VersionRange],257 right: Sequence[_VersionRange],258) -> list[_VersionRange]:259 """Intersect two sorted, non-overlapping range lists (two-pointer merge)."""260 result: list[_VersionRange] = []261 left_index = right_index = 0262 while left_index < len(left) and right_index < len(right):263 left_lower, left_upper = left[left_index]264 right_lower, right_upper = right[right_index]265 266 lower = max(left_lower, right_lower)267 upper = min(left_upper, right_upper)268 269 if not _range_is_empty(lower, upper):270 result.append((lower, upper))271 272 # Advance whichever side has the smaller upper bound.273 if left_upper < right_upper:274 left_index += 1275 else:276 right_index += 1277 278 return result279 280 281def _next_prefix_dev0(version: Version) -> Version:282 """Smallest version in the next prefix: 1.2 -> 1.3.dev0."""283 release = (*version.release[:-1], version.release[-1] + 1)284 return Version.from_parts(epoch=version.epoch, release=release, dev=0)285 286 287def _base_dev0(version: Version) -> Version:288 """The .dev0 of a version's base release: 1.2 -> 1.2.dev0."""289 return Version.from_parts(epoch=version.epoch, release=version.release, dev=0)290 291 292def _coerce_version(version: UnparsedVersion) -> Version | None:293 if not isinstance(version, Version):294 try:295 version = Version(version)296 except InvalidVersion:297 return None298 return version299 300 301def _public_version(version: Version) -> Version:302 if version.local is None:303 return version304 return version.__replace__(local=None)305 306 307def _post_base(version: Version) -> Version:308 """The version that *version* is a post-release of.309 310 1.0.post1 -> 1.0, 1.0a1.post0 -> 1.0a1, 1.0.post0.dev1 -> 1.0.311 """312 return version.__replace__(post=None, dev=None, local=None)313 314 315def _earliest_prerelease(version: Version) -> Version:316 """Earliest pre-release of *version*.317 318 1.2 -> 1.2.dev0, 1.2.post1 -> 1.2.post1.dev0.319 """320 return version.__replace__(dev=0, local=None)321 322 323def _nearest_non_prerelease(324 v: _VersionOrBoundary,325) -> Version | None:326 """Smallest non-pre-release version at or above *v*, or None."""327 if v is None:328 return None329 if isinstance(v, _BoundaryVersion):330 inner = v.version331 if inner.is_prerelease:332 # AFTER_LOCALS(1.0a1) -> nearest non-pre is 1.0333 return inner.__replace__(pre=None, dev=None, local=None)334 # AFTER_LOCALS(1.0) -> nearest non-pre is 1.0.post0335 # AFTER_LOCALS(1.0.post0) -> nearest non-pre is 1.0.post1336 k = (inner.post + 1) if inner.post is not None else 0337 return inner.__replace__(post=k, local=None)338 if not v.is_prerelease:339 return v340 # Strip pre/dev to get the final or post-release form.341 return v.__replace__(pre=None, dev=None, local=None)342 343 344class InvalidSpecifier(ValueError):345 """346 Raised when attempting to create a :class:`Specifier` with a specifier347 string that is invalid.348 349 >>> Specifier("lolwat")350 Traceback (most recent call last):351 ...352 packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat'353 """354 355 356class BaseSpecifier(metaclass=abc.ABCMeta):357 __slots__ = ()358 __match_args__ = ("_str",)359 360 @property361 def _str(self) -> str:362 """Internal property for match_args"""363 return str(self)364 365 @abc.abstractmethod366 def __str__(self) -> str:367 """368 Returns the str representation of this Specifier-like object. This369 should be representative of the Specifier itself.370 """371 372 @abc.abstractmethod373 def __hash__(self) -> int:374 """375 Returns a hash value for this Specifier-like object.376 """377 378 @abc.abstractmethod379 def __eq__(self, other: object) -> bool:380 """381 Returns a boolean representing whether or not the two Specifier-like382 objects are equal.383 384 :param other: The other object to check against.385 """386 387 @property388 @abc.abstractmethod389 def prereleases(self) -> bool | None:390 """Whether or not pre-releases as a whole are allowed.391 392 This can be set to either ``True`` or ``False`` to explicitly enable or disable393 prereleases or it can be set to ``None`` (the default) to use default semantics.394 """395 396 @prereleases.setter # noqa: B027397 def prereleases(self, value: bool) -> None:398 """Setter for :attr:`prereleases`.399 400 :param value: The value to set.401 """402 403 @abc.abstractmethod404 def contains(self, item: str, prereleases: bool | None = None) -> bool:405 """406 Determines if the given item is contained within this specifier.407 """408 409 @typing.overload410 def filter(411 self,412 iterable: Iterable[UnparsedVersionVar],413 prereleases: bool | None = None,414 key: None = ...,415 ) -> Iterator[UnparsedVersionVar]: ...416 417 @typing.overload418 def filter(419 self,420 iterable: Iterable[T],421 prereleases: bool | None = None,422 key: Callable[[T], UnparsedVersion] = ...,423 ) -> Iterator[T]: ...424 425 @abc.abstractmethod426 def filter(427 self,428 iterable: Iterable[Any],429 prereleases: bool | None = None,430 key: Callable[[Any], UnparsedVersion] | None = None,431 ) -> Iterator[Any]:432 """433 Takes an iterable of items and filters them so that only items which434 are contained within this specifier are allowed in it.435 """436 437 438class Specifier(BaseSpecifier):439 """This class abstracts handling of version specifiers.440 441 .. tip::442 443 It is generally not required to instantiate this manually. You should instead444 prefer to work with :class:`SpecifierSet` instead, which can parse445 comma-separated version specifiers (which is what package metadata contains).446 447 Instances are safe to serialize with :mod:`pickle`. They use a stable448 format so the same pickle can be loaded in future packaging releases.449 450 .. versionchanged:: 26.2451 452 Added a stable pickle format. Pickles created with packaging 26.2+ can453 be unpickled with future releases. Backward compatibility with pickles454 from packaging < 26.2 is supported but may be removed in a future455 release.456 """457 458 __slots__ = (459 "_prereleases",460 "_ranges",461 "_spec",462 "_spec_version",463 "_wildcard_split",464 )465 466 _specifier_regex_str = r"""467 (?:468 (?:469 # The identity operators allow for an escape hatch that will470 # do an exact string match of the version you wish to install.471 # This will not be parsed by PEP 440 and we cannot determine472 # any semantic meaning from it. This operator is discouraged473 # but included entirely as an escape hatch.474 === # Only match for the identity operator475 \s*476 [^\s;)]* # The arbitrary version can be just about anything,477 # we match everything except for whitespace, a478 # semi-colon for marker support, and a closing paren479 # since versions can be enclosed in them.480 )481 |482 (?:483 # The (non)equality operators allow for wild card and local484 # versions to be specified so we have to define these two485 # operators separately to enable that.486 (?:==|!=) # Only match for equals and not equals487 488 \s*489 v?490 (?:[0-9]+!)? # epoch491 [0-9]+(?:\.[0-9]+)* # release492 493 # You cannot use a wild card and a pre-release, post-release, a dev or494 # local version together so group them with a | and make them optional.495 (?:496 \.\* # Wild card syntax of .*497 |498 (?a: # pre release499 [-_\.]?500 (alpha|beta|preview|pre|a|b|c|rc)501 [-_\.]?502 [0-9]*503 )?504 (?a: # post release505 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)506 )?507 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release508 (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local509 )?510 )511 |512 (?:513 # The compatible operator requires at least two digits in the514 # release segment.515 (?:~=) # Only match for the compatible operator516 517 \s*518 v?519 (?:[0-9]+!)? # epoch520 [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *)521 (?: # pre release522 [-_\.]?523 (alpha|beta|preview|pre|a|b|c|rc)524 [-_\.]?525 [0-9]*526 )?527 (?: # post release528 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)529 )?530 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release531 )532 |533 (?:534 # All other operators only allow a sub set of what the535 # (non)equality operators do. Specifically they do not allow536 # local versions to be specified nor do they allow the prefix537 # matching wild cards.538 (?:<=|>=|<|>)539 540 \s*541 v?542 (?:[0-9]+!)? # epoch543 [0-9]+(?:\.[0-9]+)* # release544 (?a: # pre release545 [-_\.]?546 (alpha|beta|preview|pre|a|b|c|rc)547 [-_\.]?548 [0-9]*549 )?550 (?a: # post release551 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)552 )?553 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release554 )555 )556 """557 558 _regex = re.compile(559 r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE560 )561 562 _operators: Final = {563 "~=": "compatible",564 "==": "equal",565 "!=": "not_equal",566 "<=": "less_than_equal",567 ">=": "greater_than_equal",568 "<": "less_than",569 ">": "greater_than",570 "===": "arbitrary",571 }572 573 def __init__(self, spec: str = "", prereleases: bool | None = None) -> None:574 """Initialize a Specifier instance.575 576 :param spec:577 The string representation of a specifier which will be parsed and578 normalized before use.579 :param prereleases:580 This tells the specifier if it should accept prerelease versions if581 applicable or not. The default of ``None`` will autodetect it from the582 given specifiers.583 :raises InvalidSpecifier:584 If the given specifier is invalid (i.e. bad syntax).585 """586 if not self._regex.fullmatch(spec):587 raise InvalidSpecifier(f"Invalid specifier: {spec!r}")588 589 spec = spec.strip()590 if spec.startswith("==="):591 operator, version = spec[:3], spec[3:].strip()592 elif spec.startswith(("~=", "==", "!=", "<=", ">=")):593 operator, version = spec[:2], spec[2:].strip()594 else:595 operator, version = spec[:1], spec[1:].strip()596 597 self._spec: tuple[str, str] = (operator, version)598 599 # Store whether or not this Specifier should accept prereleases600 self._prereleases = prereleases601 602 # Specifier version cache603 self._spec_version: tuple[str, Version] | None = None604 605 # Populated on first wildcard (==X.*) comparison606 self._wildcard_split: tuple[list[str], int] | None = None607 608 # Version range cache (populated by _to_ranges)609 self._ranges: Sequence[_VersionRange] | None = None610 611 def _get_spec_version(self, version: str) -> Version | None:612 """One element cache, as only one spec Version is needed per Specifier."""613 if self._spec_version is not None and self._spec_version[0] == version:614 return self._spec_version[1]615 616 version_specifier = _coerce_version(version)617 if version_specifier is None:618 return None619 620 self._spec_version = (version, version_specifier)621 return version_specifier622 623 def _require_spec_version(self, version: str) -> Version:624 """Get spec version, asserting it's valid (not for === operator).625 626 This method should only be called for operators where version627 strings are guaranteed to be valid PEP 440 versions (not ===).628 """629 spec_version = self._get_spec_version(version)630 assert spec_version is not None631 return spec_version632 633 def _to_ranges(self) -> Sequence[_VersionRange]:634 """Convert this specifier to sorted, non-overlapping version ranges.635 636 Each standard operator maps to one or two ranges. ``===`` is637 modeled as full range (actual check done separately). Cached.638 """639 if self._ranges is not None:640 return self._ranges641 642 op = self.operator643 ver_str = self.version644 645 if op == "===":646 self._ranges = _FULL_RANGE647 return _FULL_RANGE648 649 if ver_str.endswith(".*"):650 result = self._wildcard_ranges(op, ver_str)651 else:652 result = self._standard_ranges(op, ver_str)653 654 self._ranges = result655 return result656 657 def _wildcard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]:658 # ==1.2.* -> [1.2.dev0, 1.3.dev0); !=1.2.* -> complement.659 base = self._require_spec_version(ver_str[:-2])660 lower = _base_dev0(base)661 upper = _next_prefix_dev0(base)662 if op == "==":663 return [(_LowerBound(lower, True), _UpperBound(upper, False))]664 # !=665 return [666 (_NEG_INF, _UpperBound(lower, False)),667 (_LowerBound(upper, True), _POS_INF),668 ]669 670 def _standard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]:671 v = self._require_spec_version(ver_str)672 673 if op == ">=":674 return [(_LowerBound(v, True), _POS_INF)]675 676 if op == "<=":677 return [678 (679 _NEG_INF,680 _UpperBound(_BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS), True),681 )682 ]683 684 if op == ">":685 if v.dev is not None:686 # >V.devN: dev versions have no post-releases, so the687 # next real version is V.dev(N+1).688 lower_ver = v.__replace__(dev=v.dev + 1, local=None)689 return [(_LowerBound(lower_ver, True), _POS_INF)]690 if v.post is not None:691 # >V.postN: next real version is V.post(N+1).dev0.692 lower_ver = v.__replace__(post=v.post + 1, dev=0, local=None)693 return [(_LowerBound(lower_ver, True), _POS_INF)]694 # >V (final or pre-release): skip V+local and all V.postN.695 return [696 (697 _LowerBound(_BoundaryVersion(v, _BoundaryKind.AFTER_POSTS), False),698 _POS_INF,699 )700 ]701 702 if op == "<":703 # <V excludes prereleases of V when V is not a prerelease.704 # V.dev0 is the earliest prerelease of V (final, post, etc.).705 bound = v if v.is_prerelease else v.__replace__(dev=0, local=None)706 if bound <= _MIN_VERSION:707 return []708 return [(_NEG_INF, _UpperBound(bound, False))]709 710 # ==, !=: local versions of V match when spec has no local segment.711 has_local = "+" in ver_str712 after_locals = _BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS)713 upper = v if has_local else after_locals714 715 if op == "==":716 return [(_LowerBound(v, True), _UpperBound(upper, True))]717 718 if op == "!=":719 return [720 (_NEG_INF, _UpperBound(v, False)),721 (_LowerBound(upper, False), _POS_INF),722 ]723 724 if op == "~=":725 prefix = v.__replace__(release=v.release[:-1])726 return [727 (_LowerBound(v, True), _UpperBound(_next_prefix_dev0(prefix), False))728 ]729 730 raise ValueError(f"Unknown operator: {op!r}") # pragma: no cover731 732 @property733 def prereleases(self) -> bool | None:734 # If there is an explicit prereleases set for this, then we'll just735 # blindly use that.736 if self._prereleases is not None:737 return self._prereleases738 739 # Only the "!=" operator does not imply prereleases when740 # the version in the specifier is a prerelease.741 operator, version_str = self._spec742 if operator == "!=":743 return False744 745 # The == specifier with trailing .* cannot include prereleases746 # e.g. "==1.0a1.*" is not valid.747 if operator == "==" and version_str.endswith(".*"):748 return False749 750 # "===" can have arbitrary string versions, so we cannot parse751 # those, we take prereleases as unknown (None) for those.752 version = self._get_spec_version(version_str)753 if version is None:754 return None755 756 # For all other operators, use the check if spec Version757 # object implies pre-releases.758 return version.is_prerelease759 760 @prereleases.setter761 def prereleases(self, value: bool | None) -> None:762 self._prereleases = value763 764 def __getstate__(self) -> tuple[tuple[str, str], bool | None]:765 # Return state as a 2-item tuple for compactness:766 # ((operator, version), prereleases)767 # Cache members are excluded and will be recomputed on demand.768 return (self._spec, self._prereleases)769 770 def __setstate__(self, state: object) -> None:771 # Always discard cached values - they will be recomputed on demand.772 self._spec_version = None773 self._wildcard_split = None774 self._ranges = None775 776 if isinstance(state, tuple):777 if len(state) == 2:778 # New format (26.2+): ((operator, version), prereleases)779 spec, prereleases = state780 if _validate_spec(spec) and _validate_pre(prereleases):781 self._spec = spec782 self._prereleases = prereleases783 return784 if len(state) == 2 and isinstance(state[1], dict):785 # Format (packaging 26.0-26.1): (None, {slot: value}).786 _, slot_dict = state787 spec = slot_dict.get("_spec")788 prereleases = slot_dict.get("_prereleases", "invalid")789 if _validate_spec(spec) and _validate_pre(prereleases):790 self._spec = spec791 self._prereleases = prereleases792 return793 if isinstance(state, dict):794 # Old format (packaging <= 25.x, no __slots__): state is a plain dict.795 spec = state.get("_spec")796 prereleases = state.get("_prereleases", "invalid")797 if _validate_spec(spec) and _validate_pre(prereleases):798 self._spec = spec799 self._prereleases = prereleases800 return801 802 raise TypeError(f"Cannot restore Specifier from {state!r}")803 804 @property805 def operator(self) -> str:806 """The operator of this specifier.807 808 >>> Specifier("==1.2.3").operator809 '=='810 """811 return self._spec[0]812 813 @property814 def version(self) -> str:815 """The version of this specifier.816 817 >>> Specifier("==1.2.3").version818 '1.2.3'819 """820 return self._spec[1]821 822 def __repr__(self) -> str:823 """A representation of the Specifier that shows all internal state.824 825 >>> Specifier('>=1.0.0')826 <Specifier('>=1.0.0')>827 >>> Specifier('>=1.0.0', prereleases=False)828 <Specifier('>=1.0.0', prereleases=False)>829 >>> Specifier('>=1.0.0', prereleases=True)830 <Specifier('>=1.0.0', prereleases=True)>831 """832 pre = (833 f", prereleases={self.prereleases!r}"834 if self._prereleases is not None835 else ""836 )837 838 return f"<{self.__class__.__name__}({str(self)!r}{pre})>"839 840 def __str__(self) -> str:841 """A string representation of the Specifier that can be round-tripped.842 843 >>> str(Specifier('>=1.0.0'))844 '>=1.0.0'845 >>> str(Specifier('>=1.0.0', prereleases=False))846 '>=1.0.0'847 """848 return "{}{}".format(*self._spec)849 850 @property851 def _canonical_spec(self) -> tuple[str, str]:852 operator, version = self._spec853 if operator == "===" or version.endswith(".*"):854 return operator, version855 856 spec_version = self._require_spec_version(version)857 858 canonical_version = canonicalize_version(859 spec_version, strip_trailing_zero=(operator != "~=")860 )861 862 return operator, canonical_version863 864 def __hash__(self) -> int:865 return hash(self._canonical_spec)866 867 def __eq__(self, other: object) -> bool:868 """Whether or not the two Specifier-like objects are equal.869 870 :param other: The other object to check against.871 872 The value of :attr:`prereleases` is ignored.873 874 >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")875 True876 >>> (Specifier("==1.2.3", prereleases=False) ==877 ... Specifier("==1.2.3", prereleases=True))878 True879 >>> Specifier("==1.2.3") == "==1.2.3"880 True881 >>> Specifier("==1.2.3") == Specifier("==1.2.4")882 False883 >>> Specifier("==1.2.3") == Specifier("~=1.2.3")884 False885 """886 if isinstance(other, str):887 try:888 other = self.__class__(str(other))889 except InvalidSpecifier:890 return NotImplemented891 elif not isinstance(other, self.__class__):892 return NotImplemented893 894 return self._canonical_spec == other._canonical_spec895 896 def _get_operator(self, op: str) -> CallableOperator:897 operator_callable: CallableOperator = getattr(898 self, f"_compare_{self._operators[op]}"899 )900 return operator_callable901 902 def _compare_compatible(self, prospective: Version, spec: str) -> bool:903 # Compatible releases have an equivalent combination of >= and ==. That904 # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to905 # implement this in terms of the other specifiers instead of906 # implementing it ourselves. The only thing we need to do is construct907 # the other specifiers.908 909 # We want everything but the last item in the version, but we want to910 # ignore suffix segments.911 prefix = _version_join(912 list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]913 )914 915 # Add the prefix notation to the end of our string916 prefix += ".*"917 918 return (self._compare_greater_than_equal(prospective, spec)) and (919 self._compare_equal(prospective, prefix)920 )921 922 def _get_wildcard_split(self, spec: str) -> tuple[list[str], int]:923 """Cached split of a wildcard spec into components and numeric length.924 925 >>> Specifier("==1.*")._get_wildcard_split("1.*")926 (['0', '1'], 2)927 >>> Specifier("==3.10.*")._get_wildcard_split("3.10.*")928 (['0', '3', '10'], 3)929 """930 wildcard_split = self._wildcard_split931 if wildcard_split is None:932 normalized = canonicalize_version(spec[:-2], strip_trailing_zero=False)933 split_spec = _version_split(normalized)934 wildcard_split = (split_spec, _numeric_prefix_len(split_spec))935 self._wildcard_split = wildcard_split936 return wildcard_split937 938 def _compare_equal(self, prospective: Version, spec: str) -> bool:939 # We need special logic to handle prefix matching940 if spec.endswith(".*"):941 split_spec, spec_numeric_len = self._get_wildcard_split(spec)942 943 # In the case of prefix matching we want to ignore local segment.944 normalized_prospective = canonicalize_version(945 _public_version(prospective), strip_trailing_zero=False946 )947 # Split the prospective version out by bangs and dots, and pretend948 # that there is an implicit dot in between a release segment and949 # a pre-release segment.950 split_prospective = _version_split(normalized_prospective)951 952 # 0-pad the prospective version before shortening it to get the correct953 # shortened version.954 padded_prospective = _left_pad(split_prospective, spec_numeric_len)955 956 # Shorten the prospective version to be the same length as the spec957 # so that we can determine if the specifier is a prefix of the958 # prospective version or not.959 shortened_prospective = padded_prospective[: len(split_spec)]960 961 return shortened_prospective == split_spec962 else:963 # Convert our spec string into a Version964 spec_version = self._require_spec_version(spec)965 966 # If the specifier does not have a local segment, then we want to967 # act as if the prospective version also does not have a local968 # segment.969 if not spec_version.local:970 prospective = _public_version(prospective)971 972 return prospective == spec_version973 974 def _compare_not_equal(self, prospective: Version, spec: str) -> bool:975 return not self._compare_equal(prospective, spec)976 977 def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:978 # NB: Local version identifiers are NOT permitted in the version979 # specifier, so local version labels can be universally removed from980 # the prospective version.981 return _public_version(prospective) <= self._require_spec_version(spec)982 983 def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:984 # NB: Local version identifiers are NOT permitted in the version985 # specifier, so local version labels can be universally removed from986 # the prospective version.987 return _public_version(prospective) >= self._require_spec_version(spec)988 989 def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:990 # Convert our spec to a Version instance, since we'll want to work with991 # it as a version.992 spec = self._require_spec_version(spec_str)993 994 # Check to see if the prospective version is less than the spec995 # version. If it's not we can short circuit and just return False now996 # instead of doing extra unneeded work.997 if not prospective < spec:998 return False999 1000 # The spec says: "<V MUST NOT allow a pre-release of the specified1001 # version unless the specified version is itself a pre-release."1002 if (1003 not spec.is_prerelease1004 and prospective.is_prerelease1005 and prospective >= _earliest_prerelease(spec)1006 ):1007 return False1008 1009 # If we've gotten to here, it means that prospective version is both1010 # less than the spec version *and* it's not a pre-release of the same1011 # version in the spec.1012 return True1013 1014 def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:1015 # Convert our spec to a Version instance, since we'll want to work with1016 # it as a version.1017 spec = self._require_spec_version(spec_str)1018 1019 # Check to see if the prospective version is greater than the spec1020 # version. If it's not we can short circuit and just return False now1021 # instead of doing extra unneeded work.1022 if not prospective > spec:1023 return False1024 1025 # The spec says: ">V MUST NOT allow a post-release of the specified1026 # version unless the specified version is itself a post-release."1027 if (1028 not spec.is_postrelease1029 and prospective.is_postrelease1030 and _post_base(prospective) == spec1031 ):1032 return False1033 1034 # Per the spec: ">V MUST NOT match a local version of the specified1035 # version". A "local version of V" is any version whose public part1036 # equals V. So >1.0a1 must not match 1.0a1+local, but must still1037 # match 1.0a2+local.1038 if prospective.local is not None and _public_version(prospective) == spec:1039 return False1040 1041 # If we've gotten to here, it means that prospective version is both1042 # greater than the spec version *and* it's not a pre-release of the1043 # same version in the spec.1044 return True1045 1046 def _compare_arbitrary(self, prospective: Version | str, spec: str) -> bool:1047 return str(prospective).lower() == str(spec).lower()1048 1049 def __contains__(self, item: str | Version) -> bool:1050 """Return whether or not the item is contained in this specifier.1051 1052 :param item: The item to check for.1053 1054 This is used for the ``in`` operator and behaves the same as1055 :meth:`contains` with no ``prereleases`` argument passed.1056 1057 >>> "1.2.3" in Specifier(">=1.2.3")1058 True1059 >>> Version("1.2.3") in Specifier(">=1.2.3")1060 True1061 >>> "1.0.0" in Specifier(">=1.2.3")1062 False1063 >>> "1.3.0a1" in Specifier(">=1.2.3")1064 True1065 >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)1066 True1067 """1068 return self.contains(item)1069 1070 def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool:1071 """Return whether or not the item is contained in this specifier.1072 1073 :param item:1074 The item to check for, which can be a version string or a1075 :class:`Version` instance.1076 :param prereleases:1077 Whether or not to match prereleases with this Specifier. If set to1078 ``None`` (the default), it will follow the recommendation from1079 :pep:`440` and match prereleases, as there are no other versions.1080 1081 >>> Specifier(">=1.2.3").contains("1.2.3")1082 True1083 >>> Specifier(">=1.2.3").contains(Version("1.2.3"))1084 True1085 >>> Specifier(">=1.2.3").contains("1.0.0")1086 False1087 >>> Specifier(">=1.2.3").contains("1.3.0a1")1088 True1089 >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1")1090 False1091 >>> Specifier(">=1.2.3").contains("1.3.0a1")1092 True1093 """1094 1095 return bool(list(self.filter([item], prereleases=prereleases)))1096 1097 @typing.overload1098 def filter(1099 self,1100 iterable: Iterable[UnparsedVersionVar],1101 prereleases: bool | None = None,1102 key: None = ...,1103 ) -> Iterator[UnparsedVersionVar]: ...1104 1105 @typing.overload1106 def filter(1107 self,1108 iterable: Iterable[T],1109 prereleases: bool | None = None,1110 key: Callable[[T], UnparsedVersion] = ...,1111 ) -> Iterator[T]: ...1112 1113 def filter(1114 self,1115 iterable: Iterable[Any],1116 prereleases: bool | None = None,1117 key: Callable[[Any], UnparsedVersion] | None = None,1118 ) -> Iterator[Any]:1119 """Filter items in the given iterable, that match the specifier.1120 1121 :param iterable:1122 An iterable that can contain version strings and :class:`Version` instances.1123 The items in the iterable will be filtered according to the specifier.1124 :param prereleases:1125 Whether or not to allow prereleases in the returned iterator. If set to1126 ``None`` (the default), it will follow the recommendation from :pep:`440`1127 and match prereleases if there are no other versions.1128 :param key:1129 A callable that takes a single argument (an item from the iterable) and1130 returns a version string or :class:`Version` instance to be used for1131 filtering.1132 1133 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))1134 ['1.3']1135 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))1136 ['1.2.3', '1.3', <Version('1.4')>]1137 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))1138 ['1.5a1']1139 >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))1140 ['1.3', '1.5a1']1141 >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))1142 ['1.3', '1.5a1']1143 >>> list(Specifier(">=1.2.3").filter(1144 ... [{"ver": "1.2"}, {"ver": "1.3"}],1145 ... key=lambda x: x["ver"]))1146 [{'ver': '1.3'}]1147 """1148 prereleases_versions = []1149 found_non_prereleases = False1150 1151 # Determine if to include prereleases by default1152 include_prereleases = (1153 prereleases if prereleases is not None else self.prereleases1154 )1155 1156 # Get the matching operator1157 operator_callable = self._get_operator(self.operator)1158 1159 # Filter versions1160 for version in iterable:1161 parsed_version = _coerce_version(version if key is None else key(version))1162 match = False1163 if parsed_version is None:1164 # === operator can match arbitrary (non-version) strings1165 if self.operator == "===" and self._compare_arbitrary(1166 version, self.version1167 ):1168 yield version1169 elif self.operator == "===":1170 match = self._compare_arbitrary(1171 version if key is None else key(version), self.version1172 )1173 else:1174 match = operator_callable(parsed_version, self.version)1175 1176 if match and parsed_version is not None:1177 # If it's not a prerelease or prereleases are allowed, yield it directly1178 if not parsed_version.is_prerelease or include_prereleases:1179 found_non_prereleases = True1180 yield version1181 # Otherwise collect prereleases for potential later use1182 elif prereleases is None and self._prereleases is not False:1183 prereleases_versions.append(version)1184 1185 # If no non-prereleases were found and prereleases weren't1186 # explicitly forbidden, yield the collected prereleases1187 if (1188 not found_non_prereleases1189 and prereleases is None1190 and self._prereleases is not False1191 ):1192 yield from prereleases_versions1193 1194 1195_prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)")1196 1197 1198def _pep440_filter_prereleases(1199 iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None1200) -> Iterator[Any]: