Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
specifiers.py1018 linesDownload Raw Back to packaging
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 11import abc12import itertools13import re14from typing import Callable, Iterable, Iterator, List, Optional, Tuple, TypeVar, Union15 16from .utils import canonicalize_version17from .version import Version18 19UnparsedVersion = Union[Version, str]20UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)21CallableOperator = Callable[[Version, str], bool]22 23 24def _coerce_version(version: UnparsedVersion) -> Version:25    if not isinstance(version, Version):26        version = Version(version)27    return version28 29 30class InvalidSpecifier(ValueError):31    """32    Raised when attempting to create a :class:`Specifier` with a specifier33    string that is invalid.34 35    >>> Specifier("lolwat")36    Traceback (most recent call last):37        ...38    packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat'39    """40 41 42class BaseSpecifier(metaclass=abc.ABCMeta):43    @abc.abstractmethod44    def __str__(self) -> str:45        """46        Returns the str representation of this Specifier-like object. This47        should be representative of the Specifier itself.48        """49 50    @abc.abstractmethod51    def __hash__(self) -> int:52        """53        Returns a hash value for this Specifier-like object.54        """55 56    @abc.abstractmethod57    def __eq__(self, other: object) -> bool:58        """59        Returns a boolean representing whether or not the two Specifier-like60        objects are equal.61 62        :param other: The other object to check against.63        """64 65    @property66    @abc.abstractmethod67    def prereleases(self) -> Optional[bool]:68        """Whether or not pre-releases as a whole are allowed.69 70        This can be set to either ``True`` or ``False`` to explicitly enable or disable71        prereleases or it can be set to ``None`` (the default) to use default semantics.72        """73 74    @prereleases.setter75    def prereleases(self, value: bool) -> None:76        """Setter for :attr:`prereleases`.77 78        :param value: The value to set.79        """80 81    @abc.abstractmethod82    def contains(self, item: str, prereleases: Optional[bool] = None) -> bool:83        """84        Determines if the given item is contained within this specifier.85        """86 87    @abc.abstractmethod88    def filter(89        self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None90    ) -> Iterator[UnparsedVersionVar]:91        """92        Takes an iterable of items and filters them so that only items which93        are contained within this specifier are allowed in it.94        """95 96 97class Specifier(BaseSpecifier):98    """This class abstracts handling of version specifiers.99 100    .. tip::101 102        It is generally not required to instantiate this manually. You should instead103        prefer to work with :class:`SpecifierSet` instead, which can parse104        comma-separated version specifiers (which is what package metadata contains).105    """106 107    _operator_regex_str = r"""108        (?P<operator>(~=|==|!=|<=|>=|<|>|===))109        """110    _version_regex_str = r"""111        (?P<version>112            (?:113                # The identity operators allow for an escape hatch that will114                # do an exact string match of the version you wish to install.115                # This will not be parsed by PEP 440 and we cannot determine116                # any semantic meaning from it. This operator is discouraged117                # but included entirely as an escape hatch.118                (?<====)  # Only match for the identity operator119                \s*120                [^\s;)]*  # The arbitrary version can be just about anything,121                          # we match everything except for whitespace, a122                          # semi-colon for marker support, and a closing paren123                          # since versions can be enclosed in them.124            )125            |126            (?:127                # The (non)equality operators allow for wild card and local128                # versions to be specified so we have to define these two129                # operators separately to enable that.130                (?<===|!=)            # Only match for equals and not equals131 132                \s*133                v?134                (?:[0-9]+!)?          # epoch135                [0-9]+(?:\.[0-9]+)*   # release136 137                # You cannot use a wild card and a pre-release, post-release, a dev or138                # local version together so group them with a | and make them optional.139                (?:140                    \.\*  # Wild card syntax of .*141                    |142                    (?:                                  # pre release143                        [-_\.]?144                        (alpha|beta|preview|pre|a|b|c|rc)145                        [-_\.]?146                        [0-9]*147                    )?148                    (?:                                  # post release149                        (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)150                    )?151                    (?:[-_\.]?dev[-_\.]?[0-9]*)?         # dev release152                    (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local153                )?154            )155            |156            (?:157                # The compatible operator requires at least two digits in the158                # release segment.159                (?<=~=)               # Only match for the compatible operator160 161                \s*162                v?163                (?:[0-9]+!)?          # epoch164                [0-9]+(?:\.[0-9]+)+   # release  (We have a + instead of a *)165                (?:                   # pre release166                    [-_\.]?167                    (alpha|beta|preview|pre|a|b|c|rc)168                    [-_\.]?169                    [0-9]*170                )?171                (?:                                   # post release172                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)173                )?174                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release175            )176            |177            (?:178                # All other operators only allow a sub set of what the179                # (non)equality operators do. Specifically they do not allow180                # local versions to be specified nor do they allow the prefix181                # matching wild cards.182                (?<!==|!=|~=)         # We have special cases for these183                                      # operators so we want to make sure they184                                      # don't match here.185 186                \s*187                v?188                (?:[0-9]+!)?          # epoch189                [0-9]+(?:\.[0-9]+)*   # release190                (?:                   # pre release191                    [-_\.]?192                    (alpha|beta|preview|pre|a|b|c|rc)193                    [-_\.]?194                    [0-9]*195                )?196                (?:                                   # post release197                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)198                )?199                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release200            )201        )202        """203 204    _regex = re.compile(205        r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$",206        re.VERBOSE | re.IGNORECASE,207    )208 209    _operators = {210        "~=": "compatible",211        "==": "equal",212        "!=": "not_equal",213        "<=": "less_than_equal",214        ">=": "greater_than_equal",215        "<": "less_than",216        ">": "greater_than",217        "===": "arbitrary",218    }219 220    def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:221        """Initialize a Specifier instance.222 223        :param spec:224            The string representation of a specifier which will be parsed and225            normalized before use.226        :param prereleases:227            This tells the specifier if it should accept prerelease versions if228            applicable or not. The default of ``None`` will autodetect it from the229            given specifiers.230        :raises InvalidSpecifier:231            If the given specifier is invalid (i.e. bad syntax).232        """233        match = self._regex.search(spec)234        if not match:235            raise InvalidSpecifier(f"Invalid specifier: '{spec}'")236 237        self._spec: Tuple[str, str] = (238            match.group("operator").strip(),239            match.group("version").strip(),240        )241 242        # Store whether or not this Specifier should accept prereleases243        self._prereleases = prereleases244 245    # https://github.com/python/mypy/pull/13475#pullrequestreview-1079784515246    @property  # type: ignore[override]247    def prereleases(self) -> bool:248        # If there is an explicit prereleases set for this, then we'll just249        # blindly use that.250        if self._prereleases is not None:251            return self._prereleases252 253        # Look at all of our specifiers and determine if they are inclusive254        # operators, and if they are if they are including an explicit255        # prerelease.256        operator, version = self._spec257        if operator in ["==", ">=", "<=", "~=", "==="]:258            # The == specifier can include a trailing .*, if it does we259            # want to remove before parsing.260            if operator == "==" and version.endswith(".*"):261                version = version[:-2]262 263            # Parse the version, and if it is a pre-release than this264            # specifier allows pre-releases.265            if Version(version).is_prerelease:266                return True267 268        return False269 270    @prereleases.setter271    def prereleases(self, value: bool) -> None:272        self._prereleases = value273 274    @property275    def operator(self) -> str:276        """The operator of this specifier.277 278        >>> Specifier("==1.2.3").operator279        '=='280        """281        return self._spec[0]282 283    @property284    def version(self) -> str:285        """The version of this specifier.286 287        >>> Specifier("==1.2.3").version288        '1.2.3'289        """290        return self._spec[1]291 292    def __repr__(self) -> str:293        """A representation of the Specifier that shows all internal state.294 295        >>> Specifier('>=1.0.0')296        <Specifier('>=1.0.0')>297        >>> Specifier('>=1.0.0', prereleases=False)298        <Specifier('>=1.0.0', prereleases=False)>299        >>> Specifier('>=1.0.0', prereleases=True)300        <Specifier('>=1.0.0', prereleases=True)>301        """302        pre = (303            f", prereleases={self.prereleases!r}"304            if self._prereleases is not None305            else ""306        )307 308        return f"<{self.__class__.__name__}({str(self)!r}{pre})>"309 310    def __str__(self) -> str:311        """A string representation of the Specifier that can be round-tripped.312 313        >>> str(Specifier('>=1.0.0'))314        '>=1.0.0'315        >>> str(Specifier('>=1.0.0', prereleases=False))316        '>=1.0.0'317        """318        return "{}{}".format(*self._spec)319 320    @property321    def _canonical_spec(self) -> Tuple[str, str]:322        canonical_version = canonicalize_version(323            self._spec[1],324            strip_trailing_zero=(self._spec[0] != "~="),325        )326        return self._spec[0], canonical_version327 328    def __hash__(self) -> int:329        return hash(self._canonical_spec)330 331    def __eq__(self, other: object) -> bool:332        """Whether or not the two Specifier-like objects are equal.333 334        :param other: The other object to check against.335 336        The value of :attr:`prereleases` is ignored.337 338        >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")339        True340        >>> (Specifier("==1.2.3", prereleases=False) ==341        ...  Specifier("==1.2.3", prereleases=True))342        True343        >>> Specifier("==1.2.3") == "==1.2.3"344        True345        >>> Specifier("==1.2.3") == Specifier("==1.2.4")346        False347        >>> Specifier("==1.2.3") == Specifier("~=1.2.3")348        False349        """350        if isinstance(other, str):351            try:352                other = self.__class__(str(other))353            except InvalidSpecifier:354                return NotImplemented355        elif not isinstance(other, self.__class__):356            return NotImplemented357 358        return self._canonical_spec == other._canonical_spec359 360    def _get_operator(self, op: str) -> CallableOperator:361        operator_callable: CallableOperator = getattr(362            self, f"_compare_{self._operators[op]}"363        )364        return operator_callable365 366    def _compare_compatible(self, prospective: Version, spec: str) -> bool:367 368        # Compatible releases have an equivalent combination of >= and ==. That369        # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to370        # implement this in terms of the other specifiers instead of371        # implementing it ourselves. The only thing we need to do is construct372        # the other specifiers.373 374        # We want everything but the last item in the version, but we want to375        # ignore suffix segments.376        prefix = _version_join(377            list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]378        )379 380        # Add the prefix notation to the end of our string381        prefix += ".*"382 383        return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(384            prospective, prefix385        )386 387    def _compare_equal(self, prospective: Version, spec: str) -> bool:388 389        # We need special logic to handle prefix matching390        if spec.endswith(".*"):391            # In the case of prefix matching we want to ignore local segment.392            normalized_prospective = canonicalize_version(393                prospective.public, strip_trailing_zero=False394            )395            # Get the normalized version string ignoring the trailing .*396            normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False)397            # Split the spec out by bangs and dots, and pretend that there is398            # an implicit dot in between a release segment and a pre-release segment.399            split_spec = _version_split(normalized_spec)400 401            # Split the prospective version out by bangs and dots, and pretend402            # that there is an implicit dot in between a release segment and403            # a pre-release segment.404            split_prospective = _version_split(normalized_prospective)405 406            # 0-pad the prospective version before shortening it to get the correct407            # shortened version.408            padded_prospective, _ = _pad_version(split_prospective, split_spec)409 410            # Shorten the prospective version to be the same length as the spec411            # so that we can determine if the specifier is a prefix of the412            # prospective version or not.413            shortened_prospective = padded_prospective[: len(split_spec)]414 415            return shortened_prospective == split_spec416        else:417            # Convert our spec string into a Version418            spec_version = Version(spec)419 420            # If the specifier does not have a local segment, then we want to421            # act as if the prospective version also does not have a local422            # segment.423            if not spec_version.local:424                prospective = Version(prospective.public)425 426            return prospective == spec_version427 428    def _compare_not_equal(self, prospective: Version, spec: str) -> bool:429        return not self._compare_equal(prospective, spec)430 431    def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:432 433        # NB: Local version identifiers are NOT permitted in the version434        # specifier, so local version labels can be universally removed from435        # the prospective version.436        return Version(prospective.public) <= Version(spec)437 438    def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:439 440        # NB: Local version identifiers are NOT permitted in the version441        # specifier, so local version labels can be universally removed from442        # the prospective version.443        return Version(prospective.public) >= Version(spec)444 445    def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:446 447        # Convert our spec to a Version instance, since we'll want to work with448        # it as a version.449        spec = Version(spec_str)450 451        # Check to see if the prospective version is less than the spec452        # version. If it's not we can short circuit and just return False now453        # instead of doing extra unneeded work.454        if not prospective < spec:455            return False456 457        # This special case is here so that, unless the specifier itself458        # includes is a pre-release version, that we do not accept pre-release459        # versions for the version mentioned in the specifier (e.g. <3.1 should460        # not match 3.1.dev0, but should match 3.0.dev0).461        if not spec.is_prerelease and prospective.is_prerelease:462            if Version(prospective.base_version) == Version(spec.base_version):463                return False464 465        # If we've gotten to here, it means that prospective version is both466        # less than the spec version *and* it's not a pre-release of the same467        # version in the spec.468        return True469 470    def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:471 472        # Convert our spec to a Version instance, since we'll want to work with473        # it as a version.474        spec = Version(spec_str)475 476        # Check to see if the prospective version is greater than the spec477        # version. If it's not we can short circuit and just return False now478        # instead of doing extra unneeded work.479        if not prospective > spec:480            return False481 482        # This special case is here so that, unless the specifier itself483        # includes is a post-release version, that we do not accept484        # post-release versions for the version mentioned in the specifier485        # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0).486        if not spec.is_postrelease and prospective.is_postrelease:487            if Version(prospective.base_version) == Version(spec.base_version):488                return False489 490        # Ensure that we do not allow a local version of the version mentioned491        # in the specifier, which is technically greater than, to match.492        if prospective.local is not None:493            if Version(prospective.base_version) == Version(spec.base_version):494                return False495 496        # If we've gotten to here, it means that prospective version is both497        # greater than the spec version *and* it's not a pre-release of the498        # same version in the spec.499        return True500 501    def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:502        return str(prospective).lower() == str(spec).lower()503 504    def __contains__(self, item: Union[str, Version]) -> bool:505        """Return whether or not the item is contained in this specifier.506 507        :param item: The item to check for.508 509        This is used for the ``in`` operator and behaves the same as510        :meth:`contains` with no ``prereleases`` argument passed.511 512        >>> "1.2.3" in Specifier(">=1.2.3")513        True514        >>> Version("1.2.3") in Specifier(">=1.2.3")515        True516        >>> "1.0.0" in Specifier(">=1.2.3")517        False518        >>> "1.3.0a1" in Specifier(">=1.2.3")519        False520        >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)521        True522        """523        return self.contains(item)524 525    def contains(526        self, item: UnparsedVersion, prereleases: Optional[bool] = None527    ) -> bool:528        """Return whether or not the item is contained in this specifier.529 530        :param item:531            The item to check for, which can be a version string or a532            :class:`Version` instance.533        :param prereleases:534            Whether or not to match prereleases with this Specifier. If set to535            ``None`` (the default), it uses :attr:`prereleases` to determine536            whether or not prereleases are allowed.537 538        >>> Specifier(">=1.2.3").contains("1.2.3")539        True540        >>> Specifier(">=1.2.3").contains(Version("1.2.3"))541        True542        >>> Specifier(">=1.2.3").contains("1.0.0")543        False544        >>> Specifier(">=1.2.3").contains("1.3.0a1")545        False546        >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1")547        True548        >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True)549        True550        """551 552        # Determine if prereleases are to be allowed or not.553        if prereleases is None:554            prereleases = self.prereleases555 556        # Normalize item to a Version, this allows us to have a shortcut for557        # "2.0" in Specifier(">=2")558        normalized_item = _coerce_version(item)559 560        # Determine if we should be supporting prereleases in this specifier561        # or not, if we do not support prereleases than we can short circuit562        # logic if this version is a prereleases.563        if normalized_item.is_prerelease and not prereleases:564            return False565 566        # Actually do the comparison to determine if this item is contained567        # within this Specifier or not.568        operator_callable: CallableOperator = self._get_operator(self.operator)569        return operator_callable(normalized_item, self.version)570 571    def filter(572        self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None573    ) -> Iterator[UnparsedVersionVar]:574        """Filter items in the given iterable, that match the specifier.575 576        :param iterable:577            An iterable that can contain version strings and :class:`Version` instances.578            The items in the iterable will be filtered according to the specifier.579        :param prereleases:580            Whether or not to allow prereleases in the returned iterator. If set to581            ``None`` (the default), it will be intelligently decide whether to allow582            prereleases or not (based on the :attr:`prereleases` attribute, and583            whether the only versions matching are prereleases).584 585        This method is smarter than just ``filter(Specifier().contains, [...])``586        because it implements the rule from :pep:`440` that a prerelease item587        SHOULD be accepted if no other versions match the given specifier.588 589        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))590        ['1.3']591        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))592        ['1.2.3', '1.3', <Version('1.4')>]593        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))594        ['1.5a1']595        >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))596        ['1.3', '1.5a1']597        >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))598        ['1.3', '1.5a1']599        """600 601        yielded = False602        found_prereleases = []603 604        kw = {"prereleases": prereleases if prereleases is not None else True}605 606        # Attempt to iterate over all the values in the iterable and if any of607        # them match, yield them.608        for version in iterable:609            parsed_version = _coerce_version(version)610 611            if self.contains(parsed_version, **kw):612                # If our version is a prerelease, and we were not set to allow613                # prereleases, then we'll store it for later in case nothing614                # else matches this specifier.615                if parsed_version.is_prerelease and not (616                    prereleases or self.prereleases617                ):618                    found_prereleases.append(version)619                # Either this is not a prerelease, or we should have been620                # accepting prereleases from the beginning.621                else:622                    yielded = True623                    yield version624 625        # Now that we've iterated over everything, determine if we've yielded626        # any values, and if we have not and we have any prereleases stored up627        # then we will go ahead and yield the prereleases.628        if not yielded and found_prereleases:629            for version in found_prereleases:630                yield version631 632 633_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")634 635 636def _version_split(version: str) -> List[str]:637    """Split version into components.638 639    The split components are intended for version comparison. The logic does640    not attempt to retain the original version string, so joining the641    components back with :func:`_version_join` may not produce the original642    version string.643    """644    result: List[str] = []645 646    epoch, _, rest = version.rpartition("!")647    result.append(epoch or "0")648 649    for item in rest.split("."):650        match = _prefix_regex.search(item)651        if match:652            result.extend(match.groups())653        else:654            result.append(item)655    return result656 657 658def _version_join(components: List[str]) -> str:659    """Join split version components into a version string.660 661    This function assumes the input came from :func:`_version_split`, where the662    first component must be the epoch (either empty or numeric), and all other663    components numeric.664    """665    epoch, *rest = components666    return f"{epoch}!{'.'.join(rest)}"667 668 669def _is_not_suffix(segment: str) -> bool:670    return not any(671        segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post")672    )673 674 675def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]:676    left_split, right_split = [], []677 678    # Get the release segment of our versions679    left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left)))680    right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))681 682    # Get the rest of our versions683    left_split.append(left[len(left_split[0]) :])684    right_split.append(right[len(right_split[0]) :])685 686    # Insert our padding687    left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))688    right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))689 690    return (691        list(itertools.chain.from_iterable(left_split)),692        list(itertools.chain.from_iterable(right_split)),693    )694 695 696class SpecifierSet(BaseSpecifier):697    """This class abstracts handling of a set of version specifiers.698 699    It can be passed a single specifier (``>=3.0``), a comma-separated list of700    specifiers (``>=3.0,!=3.1``), or no specifier at all.701    """702 703    def __init__(704        self, specifiers: str = "", prereleases: Optional[bool] = None705    ) -> None:706        """Initialize a SpecifierSet instance.707 708        :param specifiers:709            The string representation of a specifier or a comma-separated list of710            specifiers which will be parsed and normalized before use.711        :param prereleases:712            This tells the SpecifierSet if it should accept prerelease versions if713            applicable or not. The default of ``None`` will autodetect it from the714            given specifiers.715 716        :raises InvalidSpecifier:717            If the given ``specifiers`` are not parseable than this exception will be718            raised.719        """720 721        # Split on `,` to break each individual specifier into it's own item, and722        # strip each item to remove leading/trailing whitespace.723        split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]724 725        # Make each individual specifier a Specifier and save in a frozen set for later.726        self._specs = frozenset(map(Specifier, split_specifiers))727 728        # Store our prereleases value so we can use it later to determine if729        # we accept prereleases or not.730        self._prereleases = prereleases731 732    @property733    def prereleases(self) -> Optional[bool]:734        # If we have been given an explicit prerelease modifier, then we'll735        # pass that through here.736        if self._prereleases is not None:737            return self._prereleases738 739        # If we don't have any specifiers, and we don't have a forced value,740        # then we'll just return None since we don't know if this should have741        # pre-releases or not.742        if not self._specs:743            return None744 745        # Otherwise we'll see if any of the given specifiers accept746        # prereleases, if any of them do we'll return True, otherwise False.747        return any(s.prereleases for s in self._specs)748 749    @prereleases.setter750    def prereleases(self, value: bool) -> None:751        self._prereleases = value752 753    def __repr__(self) -> str:754        """A representation of the specifier set that shows all internal state.755 756        Note that the ordering of the individual specifiers within the set may not757        match the input string.758 759        >>> SpecifierSet('>=1.0.0,!=2.0.0')760        <SpecifierSet('!=2.0.0,>=1.0.0')>761        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)762        <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)>763        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)764        <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)>765        """766        pre = (767            f", prereleases={self.prereleases!r}"768            if self._prereleases is not None769            else ""770        )771 772        return f"<SpecifierSet({str(self)!r}{pre})>"773 774    def __str__(self) -> str:775        """A string representation of the specifier set that can be round-tripped.776 777        Note that the ordering of the individual specifiers within the set may not778        match the input string.779 780        >>> str(SpecifierSet(">=1.0.0,!=1.0.1"))781        '!=1.0.1,>=1.0.0'782        >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False))783        '!=1.0.1,>=1.0.0'784        """785        return ",".join(sorted(str(s) for s in self._specs))786 787    def __hash__(self) -> int:788        return hash(self._specs)789 790    def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":791        """Return a SpecifierSet which is a combination of the two sets.792 793        :param other: The other object to combine with.794 795        >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1'796        <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>797        >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1')798        <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>799        """800        if isinstance(other, str):801            other = SpecifierSet(other)802        elif not isinstance(other, SpecifierSet):803            return NotImplemented804 805        specifier = SpecifierSet()806        specifier._specs = frozenset(self._specs | other._specs)807 808        if self._prereleases is None and other._prereleases is not None:809            specifier._prereleases = other._prereleases810        elif self._prereleases is not None and other._prereleases is None:811            specifier._prereleases = self._prereleases812        elif self._prereleases == other._prereleases:813            specifier._prereleases = self._prereleases814        else:815            raise ValueError(816                "Cannot combine SpecifierSets with True and False prerelease "817                "overrides."818            )819 820        return specifier821 822    def __eq__(self, other: object) -> bool:823        """Whether or not the two SpecifierSet-like objects are equal.824 825        :param other: The other object to check against.826 827        The value of :attr:`prereleases` is ignored.828 829        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1")830        True831        >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) ==832        ...  SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True))833        True834        >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1"835        True836        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0")837        False838        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2")839        False840        """841        if isinstance(other, (str, Specifier)):842            other = SpecifierSet(str(other))843        elif not isinstance(other, SpecifierSet):844            return NotImplemented845 846        return self._specs == other._specs847 848    def __len__(self) -> int:849        """Returns the number of specifiers in this specifier set."""850        return len(self._specs)851 852    def __iter__(self) -> Iterator[Specifier]:853        """854        Returns an iterator over all the underlying :class:`Specifier` instances855        in this specifier set.856 857        >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str)858        [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>]859        """860        return iter(self._specs)861 862    def __contains__(self, item: UnparsedVersion) -> bool:863        """Return whether or not the item is contained in this specifier.864 865        :param item: The item to check for.866 867        This is used for the ``in`` operator and behaves the same as868        :meth:`contains` with no ``prereleases`` argument passed.869 870        >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1")871        True872        >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1")873        True874        >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")875        False876        >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")877        False878        >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)879        True880        """881        return self.contains(item)882 883    def contains(884        self,885        item: UnparsedVersion,886        prereleases: Optional[bool] = None,887        installed: Optional[bool] = None,888    ) -> bool:889        """Return whether or not the item is contained in this SpecifierSet.890 891        :param item:892            The item to check for, which can be a version string or a893            :class:`Version` instance.894        :param prereleases:895            Whether or not to match prereleases with this SpecifierSet. If set to896            ``None`` (the default), it uses :attr:`prereleases` to determine897            whether or not prereleases are allowed.898 899        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")900        True901        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3"))902        True903        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")904        False905        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")906        False907        >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1")908        True909        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)910        True911        """912        # Ensure that our item is a Version instance.913        if not isinstance(item, Version):914            item = Version(item)915 916        # Determine if we're forcing a prerelease or not, if we're not forcing917        # one for this particular filter call, then we'll use whatever the918        # SpecifierSet thinks for whether or not we should support prereleases.919        if prereleases is None:920            prereleases = self.prereleases921 922        # We can determine if we're going to allow pre-releases by looking to923        # see if any of the underlying items supports them. If none of them do924        # and this item is a pre-release then we do not allow it and we can925        # short circuit that here.926        # Note: This means that 1.0.dev1 would not be contained in something927        #       like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0928        if not prereleases and item.is_prerelease:929            return False930 931        if installed and item.is_prerelease:932            item = Version(item.base_version)933 934        # We simply dispatch to the underlying specs here to make sure that the935        # given version is contained within all of them.936        # Note: This use of all() here means that an empty set of specifiers937        #       will always return True, this is an explicit design decision.938        return all(s.contains(item, prereleases=prereleases) for s in self._specs)939 940    def filter(941        self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None942    ) -> Iterator[UnparsedVersionVar]:943        """Filter items in the given iterable, that match the specifiers in this set.944 945        :param iterable:946            An iterable that can contain version strings and :class:`Version` instances.947            The items in the iterable will be filtered according to the specifier.948        :param prereleases:949            Whether or not to allow prereleases in the returned iterator. If set to950            ``None`` (the default), it will be intelligently decide whether to allow951            prereleases or not (based on the :attr:`prereleases` attribute, and952            whether the only versions matching are prereleases).953 954        This method is smarter than just ``filter(SpecifierSet(...).contains, [...])``955        because it implements the rule from :pep:`440` that a prerelease item956        SHOULD be accepted if no other versions match the given specifier.957 958        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))959        ['1.3']960        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))961        ['1.3', <Version('1.4')>]962        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))963        []964        >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))965        ['1.3', '1.5a1']966        >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))967        ['1.3', '1.5a1']968 969        An "empty" SpecifierSet will filter items based on the presence of prerelease970        versions in the set.971 972        >>> list(SpecifierSet("").filter(["1.3", "1.5a1"]))973        ['1.3']974        >>> list(SpecifierSet("").filter(["1.5a1"]))975        ['1.5a1']976        >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"]))977        ['1.3', '1.5a1']978        >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))979        ['1.3', '1.5a1']980        """981        # Determine if we're forcing a prerelease or not, if we're not forcing982        # one for this particular filter call, then we'll use whatever the983        # SpecifierSet thinks for whether or not we should support prereleases.984        if prereleases is None:985            prereleases = self.prereleases986 987        # If we have any specifiers, then we want to wrap our iterable in the988        # filter method for each one, this will act as a logical AND amongst989        # each specifier.990        if self._specs:991            for spec in self._specs:992                iterable = spec.filter(iterable, prereleases=bool(prereleases))993            return iter(iterable)994        # If we do not have any specifiers, then we need to have a rough filter995        # which will filter out any pre-releases, unless there are no final996        # releases.997        else:998            filtered: List[UnparsedVersionVar] = []999            found_prereleases: List[UnparsedVersionVar] = []1000 1001            for item in iterable:1002                parsed_version = _coerce_version(item)1003 1004                # Store any item which is a pre-release for later unless we've1005                # already found a final version or we are accepting prereleases1006                if parsed_version.is_prerelease and not prereleases:1007                    if not filtered:1008                        found_prereleases.append(item)1009                else:1010                    filtered.append(item)1011 1012            # If we've found no items except for pre-releases, then we'll go1013            # ahead and use the pre-releases1014            if not filtered and found_prereleases and prereleases is None:1015                return iter(found_prereleases)1016 1017            return iter(filtered)1018 
codekingpro/portable-devtools · Team Ai