• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::
6
7    from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier
8    from packaging.version import Version
9"""
10
11import abc
12import itertools
13import re
14from typing import (
15    Callable,
16    Iterable,
17    Iterator,
18    List,
19    Optional,
20    Set,
21    Tuple,
22    TypeVar,
23    Union,
24)
25
26from .utils import canonicalize_version
27from .version import Version
28
29UnparsedVersion = Union[Version, str]
30UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion)
31CallableOperator = Callable[[Version, str], bool]
32
33
34def _coerce_version(version: UnparsedVersion) -> Version:
35    if not isinstance(version, Version):
36        version = Version(version)
37    return version
38
39
40class InvalidSpecifier(ValueError):
41    """
42    Raised when attempting to create a :class:`Specifier` with a specifier
43    string that is invalid.
44
45    >>> Specifier("lolwat")
46    Traceback (most recent call last):
47        ...
48    packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat'
49    """
50
51
52class BaseSpecifier(metaclass=abc.ABCMeta):
53    @abc.abstractmethod
54    def __str__(self) -> str:
55        """
56        Returns the str representation of this Specifier-like object. This
57        should be representative of the Specifier itself.
58        """
59
60    @abc.abstractmethod
61    def __hash__(self) -> int:
62        """
63        Returns a hash value for this Specifier-like object.
64        """
65
66    @abc.abstractmethod
67    def __eq__(self, other: object) -> bool:
68        """
69        Returns a boolean representing whether or not the two Specifier-like
70        objects are equal.
71
72        :param other: The other object to check against.
73        """
74
75    @property
76    @abc.abstractmethod
77    def prereleases(self) -> Optional[bool]:
78        """Whether or not pre-releases as a whole are allowed.
79
80        This can be set to either ``True`` or ``False`` to explicitly enable or disable
81        prereleases or it can be set to ``None`` (the default) to use default semantics.
82        """
83
84    @prereleases.setter
85    def prereleases(self, value: bool) -> None:
86        """Setter for :attr:`prereleases`.
87
88        :param value: The value to set.
89        """
90
91    @abc.abstractmethod
92    def contains(self, item: str, prereleases: Optional[bool] = None) -> bool:
93        """
94        Determines if the given item is contained within this specifier.
95        """
96
97    @abc.abstractmethod
98    def filter(
99        self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
100    ) -> Iterator[UnparsedVersionVar]:
101        """
102        Takes an iterable of items and filters them so that only items which
103        are contained within this specifier are allowed in it.
104        """
105
106
107class Specifier(BaseSpecifier):
108    """This class abstracts handling of version specifiers.
109
110    .. tip::
111
112        It is generally not required to instantiate this manually. You should instead
113        prefer to work with :class:`SpecifierSet` instead, which can parse
114        comma-separated version specifiers (which is what package metadata contains).
115    """
116
117    _operator_regex_str = r"""
118        (?P<operator>(~=|==|!=|<=|>=|<|>|===))
119        """
120    _version_regex_str = r"""
121        (?P<version>
122            (?:
123                # The identity operators allow for an escape hatch that will
124                # do an exact string match of the version you wish to install.
125                # This will not be parsed by PEP 440 and we cannot determine
126                # any semantic meaning from it. This operator is discouraged
127                # but included entirely as an escape hatch.
128                (?<====)  # Only match for the identity operator
129                \s*
130                [^\s;)]*  # The arbitrary version can be just about anything,
131                          # we match everything except for whitespace, a
132                          # semi-colon for marker support, and a closing paren
133                          # since versions can be enclosed in them.
134            )
135            |
136            (?:
137                # The (non)equality operators allow for wild card and local
138                # versions to be specified so we have to define these two
139                # operators separately to enable that.
140                (?<===|!=)            # Only match for equals and not equals
141
142                \s*
143                v?
144                (?:[0-9]+!)?          # epoch
145                [0-9]+(?:\.[0-9]+)*   # release
146
147                # You cannot use a wild card and a pre-release, post-release, a dev or
148                # local version together so group them with a | and make them optional.
149                (?:
150                    \.\*  # Wild card syntax of .*
151                    |
152                    (?:                                  # pre release
153                        [-_\.]?
154                        (alpha|beta|preview|pre|a|b|c|rc)
155                        [-_\.]?
156                        [0-9]*
157                    )?
158                    (?:                                  # post release
159                        (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
160                    )?
161                    (?:[-_\.]?dev[-_\.]?[0-9]*)?         # dev release
162                    (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
163                )?
164            )
165            |
166            (?:
167                # The compatible operator requires at least two digits in the
168                # release segment.
169                (?<=~=)               # Only match for the compatible operator
170
171                \s*
172                v?
173                (?:[0-9]+!)?          # epoch
174                [0-9]+(?:\.[0-9]+)+   # release  (We have a + instead of a *)
175                (?:                   # pre release
176                    [-_\.]?
177                    (alpha|beta|preview|pre|a|b|c|rc)
178                    [-_\.]?
179                    [0-9]*
180                )?
181                (?:                                   # post release
182                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
183                )?
184                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
185            )
186            |
187            (?:
188                # All other operators only allow a sub set of what the
189                # (non)equality operators do. Specifically they do not allow
190                # local versions to be specified nor do they allow the prefix
191                # matching wild cards.
192                (?<!==|!=|~=)         # We have special cases for these
193                                      # operators so we want to make sure they
194                                      # don't match here.
195
196                \s*
197                v?
198                (?:[0-9]+!)?          # epoch
199                [0-9]+(?:\.[0-9]+)*   # release
200                (?:                   # pre release
201                    [-_\.]?
202                    (alpha|beta|preview|pre|a|b|c|rc)
203                    [-_\.]?
204                    [0-9]*
205                )?
206                (?:                                   # post release
207                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
208                )?
209                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
210            )
211        )
212        """
213
214    _regex = re.compile(
215        r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$",
216        re.VERBOSE | re.IGNORECASE,
217    )
218
219    _operators = {
220        "~=": "compatible",
221        "==": "equal",
222        "!=": "not_equal",
223        "<=": "less_than_equal",
224        ">=": "greater_than_equal",
225        "<": "less_than",
226        ">": "greater_than",
227        "===": "arbitrary",
228    }
229
230    def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
231        """Initialize a Specifier instance.
232
233        :param spec:
234            The string representation of a specifier which will be parsed and
235            normalized before use.
236        :param prereleases:
237            This tells the specifier if it should accept prerelease versions if
238            applicable or not. The default of ``None`` will autodetect it from the
239            given specifiers.
240        :raises InvalidSpecifier:
241            If the given specifier is invalid (i.e. bad syntax).
242        """
243        match = self._regex.search(spec)
244        if not match:
245            raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
246
247        self._spec: Tuple[str, str] = (
248            match.group("operator").strip(),
249            match.group("version").strip(),
250        )
251
252        # Store whether or not this Specifier should accept prereleases
253        self._prereleases = prereleases
254
255    # https://github.com/python/mypy/pull/13475#pullrequestreview-1079784515
256    @property  # type: ignore[override]
257    def prereleases(self) -> bool:
258        # If there is an explicit prereleases set for this, then we'll just
259        # blindly use that.
260        if self._prereleases is not None:
261            return self._prereleases
262
263        # Look at all of our specifiers and determine if they are inclusive
264        # operators, and if they are if they are including an explicit
265        # prerelease.
266        operator, version = self._spec
267        if operator in ["==", ">=", "<=", "~=", "==="]:
268            # The == specifier can include a trailing .*, if it does we
269            # want to remove before parsing.
270            if operator == "==" and version.endswith(".*"):
271                version = version[:-2]
272
273            # Parse the version, and if it is a pre-release than this
274            # specifier allows pre-releases.
275            if Version(version).is_prerelease:
276                return True
277
278        return False
279
280    @prereleases.setter
281    def prereleases(self, value: bool) -> None:
282        self._prereleases = value
283
284    @property
285    def operator(self) -> str:
286        """The operator of this specifier.
287
288        >>> Specifier("==1.2.3").operator
289        '=='
290        """
291        return self._spec[0]
292
293    @property
294    def version(self) -> str:
295        """The version of this specifier.
296
297        >>> Specifier("==1.2.3").version
298        '1.2.3'
299        """
300        return self._spec[1]
301
302    def __repr__(self) -> str:
303        """A representation of the Specifier that shows all internal state.
304
305        >>> Specifier('>=1.0.0')
306        <Specifier('>=1.0.0')>
307        >>> Specifier('>=1.0.0', prereleases=False)
308        <Specifier('>=1.0.0', prereleases=False)>
309        >>> Specifier('>=1.0.0', prereleases=True)
310        <Specifier('>=1.0.0', prereleases=True)>
311        """
312        pre = (
313            f", prereleases={self.prereleases!r}"
314            if self._prereleases is not None
315            else ""
316        )
317
318        return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
319
320    def __str__(self) -> str:
321        """A string representation of the Specifier that can be round-tripped.
322
323        >>> str(Specifier('>=1.0.0'))
324        '>=1.0.0'
325        >>> str(Specifier('>=1.0.0', prereleases=False))
326        '>=1.0.0'
327        """
328        return "{}{}".format(*self._spec)
329
330    @property
331    def _canonical_spec(self) -> Tuple[str, str]:
332        canonical_version = canonicalize_version(
333            self._spec[1],
334            strip_trailing_zero=(self._spec[0] != "~="),
335        )
336        return self._spec[0], canonical_version
337
338    def __hash__(self) -> int:
339        return hash(self._canonical_spec)
340
341    def __eq__(self, other: object) -> bool:
342        """Whether or not the two Specifier-like objects are equal.
343
344        :param other: The other object to check against.
345
346        The value of :attr:`prereleases` is ignored.
347
348        >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")
349        True
350        >>> (Specifier("==1.2.3", prereleases=False) ==
351        ...  Specifier("==1.2.3", prereleases=True))
352        True
353        >>> Specifier("==1.2.3") == "==1.2.3"
354        True
355        >>> Specifier("==1.2.3") == Specifier("==1.2.4")
356        False
357        >>> Specifier("==1.2.3") == Specifier("~=1.2.3")
358        False
359        """
360        if isinstance(other, str):
361            try:
362                other = self.__class__(str(other))
363            except InvalidSpecifier:
364                return NotImplemented
365        elif not isinstance(other, self.__class__):
366            return NotImplemented
367
368        return self._canonical_spec == other._canonical_spec
369
370    def _get_operator(self, op: str) -> CallableOperator:
371        operator_callable: CallableOperator = getattr(
372            self, f"_compare_{self._operators[op]}"
373        )
374        return operator_callable
375
376    def _compare_compatible(self, prospective: Version, spec: str) -> bool:
377
378        # Compatible releases have an equivalent combination of >= and ==. That
379        # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
380        # implement this in terms of the other specifiers instead of
381        # implementing it ourselves. The only thing we need to do is construct
382        # the other specifiers.
383
384        # We want everything but the last item in the version, but we want to
385        # ignore suffix segments.
386        prefix = _version_join(
387            list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
388        )
389
390        # Add the prefix notation to the end of our string
391        prefix += ".*"
392
393        return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
394            prospective, prefix
395        )
396
397    def _compare_equal(self, prospective: Version, spec: str) -> bool:
398
399        # We need special logic to handle prefix matching
400        if spec.endswith(".*"):
401            # In the case of prefix matching we want to ignore local segment.
402            normalized_prospective = canonicalize_version(
403                prospective.public, strip_trailing_zero=False
404            )
405            # Get the normalized version string ignoring the trailing .*
406            normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False)
407            # Split the spec out by bangs and dots, and pretend that there is
408            # an implicit dot in between a release segment and a pre-release segment.
409            split_spec = _version_split(normalized_spec)
410
411            # Split the prospective version out by bangs and dots, and pretend
412            # that there is an implicit dot in between a release segment and
413            # a pre-release segment.
414            split_prospective = _version_split(normalized_prospective)
415
416            # 0-pad the prospective version before shortening it to get the correct
417            # shortened version.
418            padded_prospective, _ = _pad_version(split_prospective, split_spec)
419
420            # Shorten the prospective version to be the same length as the spec
421            # so that we can determine if the specifier is a prefix of the
422            # prospective version or not.
423            shortened_prospective = padded_prospective[: len(split_spec)]
424
425            return shortened_prospective == split_spec
426        else:
427            # Convert our spec string into a Version
428            spec_version = Version(spec)
429
430            # If the specifier does not have a local segment, then we want to
431            # act as if the prospective version also does not have a local
432            # segment.
433            if not spec_version.local:
434                prospective = Version(prospective.public)
435
436            return prospective == spec_version
437
438    def _compare_not_equal(self, prospective: Version, spec: str) -> bool:
439        return not self._compare_equal(prospective, spec)
440
441    def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:
442
443        # NB: Local version identifiers are NOT permitted in the version
444        # specifier, so local version labels can be universally removed from
445        # the prospective version.
446        return Version(prospective.public) <= Version(spec)
447
448    def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
449
450        # NB: Local version identifiers are NOT permitted in the version
451        # specifier, so local version labels can be universally removed from
452        # the prospective version.
453        return Version(prospective.public) >= Version(spec)
454
455    def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
456
457        # Convert our spec to a Version instance, since we'll want to work with
458        # it as a version.
459        spec = Version(spec_str)
460
461        # Check to see if the prospective version is less than the spec
462        # version. If it's not we can short circuit and just return False now
463        # instead of doing extra unneeded work.
464        if not prospective < spec:
465            return False
466
467        # This special case is here so that, unless the specifier itself
468        # includes is a pre-release version, that we do not accept pre-release
469        # versions for the version mentioned in the specifier (e.g. <3.1 should
470        # not match 3.1.dev0, but should match 3.0.dev0).
471        if not spec.is_prerelease and prospective.is_prerelease:
472            if Version(prospective.base_version) == Version(spec.base_version):
473                return False
474
475        # If we've gotten to here, it means that prospective version is both
476        # less than the spec version *and* it's not a pre-release of the same
477        # version in the spec.
478        return True
479
480    def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
481
482        # Convert our spec to a Version instance, since we'll want to work with
483        # it as a version.
484        spec = Version(spec_str)
485
486        # Check to see if the prospective version is greater than the spec
487        # version. If it's not we can short circuit and just return False now
488        # instead of doing extra unneeded work.
489        if not prospective > spec:
490            return False
491
492        # This special case is here so that, unless the specifier itself
493        # includes is a post-release version, that we do not accept
494        # post-release versions for the version mentioned in the specifier
495        # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0).
496        if not spec.is_postrelease and prospective.is_postrelease:
497            if Version(prospective.base_version) == Version(spec.base_version):
498                return False
499
500        # Ensure that we do not allow a local version of the version mentioned
501        # in the specifier, which is technically greater than, to match.
502        if prospective.local is not None:
503            if Version(prospective.base_version) == Version(spec.base_version):
504                return False
505
506        # If we've gotten to here, it means that prospective version is both
507        # greater than the spec version *and* it's not a pre-release of the
508        # same version in the spec.
509        return True
510
511    def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
512        return str(prospective).lower() == str(spec).lower()
513
514    def __contains__(self, item: Union[str, Version]) -> bool:
515        """Return whether or not the item is contained in this specifier.
516
517        :param item: The item to check for.
518
519        This is used for the ``in`` operator and behaves the same as
520        :meth:`contains` with no ``prereleases`` argument passed.
521
522        >>> "1.2.3" in Specifier(">=1.2.3")
523        True
524        >>> Version("1.2.3") in Specifier(">=1.2.3")
525        True
526        >>> "1.0.0" in Specifier(">=1.2.3")
527        False
528        >>> "1.3.0a1" in Specifier(">=1.2.3")
529        False
530        >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)
531        True
532        """
533        return self.contains(item)
534
535    def contains(
536        self, item: UnparsedVersion, prereleases: Optional[bool] = None
537    ) -> bool:
538        """Return whether or not the item is contained in this specifier.
539
540        :param item:
541            The item to check for, which can be a version string or a
542            :class:`Version` instance.
543        :param prereleases:
544            Whether or not to match prereleases with this Specifier. If set to
545            ``None`` (the default), it uses :attr:`prereleases` to determine
546            whether or not prereleases are allowed.
547
548        >>> Specifier(">=1.2.3").contains("1.2.3")
549        True
550        >>> Specifier(">=1.2.3").contains(Version("1.2.3"))
551        True
552        >>> Specifier(">=1.2.3").contains("1.0.0")
553        False
554        >>> Specifier(">=1.2.3").contains("1.3.0a1")
555        False
556        >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1")
557        True
558        >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True)
559        True
560        """
561
562        # Determine if prereleases are to be allowed or not.
563        if prereleases is None:
564            prereleases = self.prereleases
565
566        # Normalize item to a Version, this allows us to have a shortcut for
567        # "2.0" in Specifier(">=2")
568        normalized_item = _coerce_version(item)
569
570        # Determine if we should be supporting prereleases in this specifier
571        # or not, if we do not support prereleases than we can short circuit
572        # logic if this version is a prereleases.
573        if normalized_item.is_prerelease and not prereleases:
574            return False
575
576        # Actually do the comparison to determine if this item is contained
577        # within this Specifier or not.
578        operator_callable: CallableOperator = self._get_operator(self.operator)
579        return operator_callable(normalized_item, self.version)
580
581    def filter(
582        self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
583    ) -> Iterator[UnparsedVersionVar]:
584        """Filter items in the given iterable, that match the specifier.
585
586        :param iterable:
587            An iterable that can contain version strings and :class:`Version` instances.
588            The items in the iterable will be filtered according to the specifier.
589        :param prereleases:
590            Whether or not to allow prereleases in the returned iterator. If set to
591            ``None`` (the default), it will be intelligently decide whether to allow
592            prereleases or not (based on the :attr:`prereleases` attribute, and
593            whether the only versions matching are prereleases).
594
595        This method is smarter than just ``filter(Specifier().contains, [...])``
596        because it implements the rule from :pep:`440` that a prerelease item
597        SHOULD be accepted if no other versions match the given specifier.
598
599        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
600        ['1.3']
601        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))
602        ['1.2.3', '1.3', <Version('1.4')>]
603        >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))
604        ['1.5a1']
605        >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
606        ['1.3', '1.5a1']
607        >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
608        ['1.3', '1.5a1']
609        """
610
611        yielded = False
612        found_prereleases = []
613
614        kw = {"prereleases": prereleases if prereleases is not None else True}
615
616        # Attempt to iterate over all the values in the iterable and if any of
617        # them match, yield them.
618        for version in iterable:
619            parsed_version = _coerce_version(version)
620
621            if self.contains(parsed_version, **kw):
622                # If our version is a prerelease, and we were not set to allow
623                # prereleases, then we'll store it for later in case nothing
624                # else matches this specifier.
625                if parsed_version.is_prerelease and not (
626                    prereleases or self.prereleases
627                ):
628                    found_prereleases.append(version)
629                # Either this is not a prerelease, or we should have been
630                # accepting prereleases from the beginning.
631                else:
632                    yielded = True
633                    yield version
634
635        # Now that we've iterated over everything, determine if we've yielded
636        # any values, and if we have not and we have any prereleases stored up
637        # then we will go ahead and yield the prereleases.
638        if not yielded and found_prereleases:
639            for version in found_prereleases:
640                yield version
641
642
643_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
644
645
646def _version_split(version: str) -> List[str]:
647    """Split version into components.
648
649    The split components are intended for version comparison. The logic does
650    not attempt to retain the original version string, so joining the
651    components back with :func:`_version_join` may not produce the original
652    version string.
653    """
654    result: List[str] = []
655
656    epoch, _, rest = version.rpartition("!")
657    result.append(epoch or "0")
658
659    for item in rest.split("."):
660        match = _prefix_regex.search(item)
661        if match:
662            result.extend(match.groups())
663        else:
664            result.append(item)
665    return result
666
667
668def _version_join(components: List[str]) -> str:
669    """Join split version components into a version string.
670
671    This function assumes the input came from :func:`_version_split`, where the
672    first component must be the epoch (either empty or numeric), and all other
673    components numeric.
674    """
675    epoch, *rest = components
676    return f"{epoch}!{'.'.join(rest)}"
677
678
679def _is_not_suffix(segment: str) -> bool:
680    return not any(
681        segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post")
682    )
683
684
685def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]:
686    left_split, right_split = [], []
687
688    # Get the release segment of our versions
689    left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left)))
690    right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
691
692    # Get the rest of our versions
693    left_split.append(left[len(left_split[0]) :])
694    right_split.append(right[len(right_split[0]) :])
695
696    # Insert our padding
697    left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
698    right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
699
700    return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
701
702
703class SpecifierSet(BaseSpecifier):
704    """This class abstracts handling of a set of version specifiers.
705
706    It can be passed a single specifier (``>=3.0``), a comma-separated list of
707    specifiers (``>=3.0,!=3.1``), or no specifier at all.
708    """
709
710    def __init__(
711        self, specifiers: str = "", prereleases: Optional[bool] = None
712    ) -> None:
713        """Initialize a SpecifierSet instance.
714
715        :param specifiers:
716            The string representation of a specifier or a comma-separated list of
717            specifiers which will be parsed and normalized before use.
718        :param prereleases:
719            This tells the SpecifierSet if it should accept prerelease versions if
720            applicable or not. The default of ``None`` will autodetect it from the
721            given specifiers.
722
723        :raises InvalidSpecifier:
724            If the given ``specifiers`` are not parseable than this exception will be
725            raised.
726        """
727
728        # Split on `,` to break each individual specifier into it's own item, and
729        # strip each item to remove leading/trailing whitespace.
730        split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
731
732        # Parsed each individual specifier, attempting first to make it a
733        # Specifier.
734        parsed: Set[Specifier] = set()
735        for specifier in split_specifiers:
736            parsed.add(Specifier(specifier))
737
738        # Turn our parsed specifiers into a frozen set and save them for later.
739        self._specs = frozenset(parsed)
740
741        # Store our prereleases value so we can use it later to determine if
742        # we accept prereleases or not.
743        self._prereleases = prereleases
744
745    @property
746    def prereleases(self) -> Optional[bool]:
747        # If we have been given an explicit prerelease modifier, then we'll
748        # pass that through here.
749        if self._prereleases is not None:
750            return self._prereleases
751
752        # If we don't have any specifiers, and we don't have a forced value,
753        # then we'll just return None since we don't know if this should have
754        # pre-releases or not.
755        if not self._specs:
756            return None
757
758        # Otherwise we'll see if any of the given specifiers accept
759        # prereleases, if any of them do we'll return True, otherwise False.
760        return any(s.prereleases for s in self._specs)
761
762    @prereleases.setter
763    def prereleases(self, value: bool) -> None:
764        self._prereleases = value
765
766    def __repr__(self) -> str:
767        """A representation of the specifier set that shows all internal state.
768
769        Note that the ordering of the individual specifiers within the set may not
770        match the input string.
771
772        >>> SpecifierSet('>=1.0.0,!=2.0.0')
773        <SpecifierSet('!=2.0.0,>=1.0.0')>
774        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)
775        <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)>
776        >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)
777        <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)>
778        """
779        pre = (
780            f", prereleases={self.prereleases!r}"
781            if self._prereleases is not None
782            else ""
783        )
784
785        return f"<SpecifierSet({str(self)!r}{pre})>"
786
787    def __str__(self) -> str:
788        """A string representation of the specifier set that can be round-tripped.
789
790        Note that the ordering of the individual specifiers within the set may not
791        match the input string.
792
793        >>> str(SpecifierSet(">=1.0.0,!=1.0.1"))
794        '!=1.0.1,>=1.0.0'
795        >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False))
796        '!=1.0.1,>=1.0.0'
797        """
798        return ",".join(sorted(str(s) for s in self._specs))
799
800    def __hash__(self) -> int:
801        return hash(self._specs)
802
803    def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":
804        """Return a SpecifierSet which is a combination of the two sets.
805
806        :param other: The other object to combine with.
807
808        >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1'
809        <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
810        >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1')
811        <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
812        """
813        if isinstance(other, str):
814            other = SpecifierSet(other)
815        elif not isinstance(other, SpecifierSet):
816            return NotImplemented
817
818        specifier = SpecifierSet()
819        specifier._specs = frozenset(self._specs | other._specs)
820
821        if self._prereleases is None and other._prereleases is not None:
822            specifier._prereleases = other._prereleases
823        elif self._prereleases is not None and other._prereleases is None:
824            specifier._prereleases = self._prereleases
825        elif self._prereleases == other._prereleases:
826            specifier._prereleases = self._prereleases
827        else:
828            raise ValueError(
829                "Cannot combine SpecifierSets with True and False prerelease "
830                "overrides."
831            )
832
833        return specifier
834
835    def __eq__(self, other: object) -> bool:
836        """Whether or not the two SpecifierSet-like objects are equal.
837
838        :param other: The other object to check against.
839
840        The value of :attr:`prereleases` is ignored.
841
842        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1")
843        True
844        >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) ==
845        ...  SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True))
846        True
847        >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1"
848        True
849        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0")
850        False
851        >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2")
852        False
853        """
854        if isinstance(other, (str, Specifier)):
855            other = SpecifierSet(str(other))
856        elif not isinstance(other, SpecifierSet):
857            return NotImplemented
858
859        return self._specs == other._specs
860
861    def __len__(self) -> int:
862        """Returns the number of specifiers in this specifier set."""
863        return len(self._specs)
864
865    def __iter__(self) -> Iterator[Specifier]:
866        """
867        Returns an iterator over all the underlying :class:`Specifier` instances
868        in this specifier set.
869
870        >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str)
871        [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>]
872        """
873        return iter(self._specs)
874
875    def __contains__(self, item: UnparsedVersion) -> bool:
876        """Return whether or not the item is contained in this specifier.
877
878        :param item: The item to check for.
879
880        This is used for the ``in`` operator and behaves the same as
881        :meth:`contains` with no ``prereleases`` argument passed.
882
883        >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1")
884        True
885        >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1")
886        True
887        >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")
888        False
889        >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")
890        False
891        >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)
892        True
893        """
894        return self.contains(item)
895
896    def contains(
897        self,
898        item: UnparsedVersion,
899        prereleases: Optional[bool] = None,
900        installed: Optional[bool] = None,
901    ) -> bool:
902        """Return whether or not the item is contained in this SpecifierSet.
903
904        :param item:
905            The item to check for, which can be a version string or a
906            :class:`Version` instance.
907        :param prereleases:
908            Whether or not to match prereleases with this SpecifierSet. If set to
909            ``None`` (the default), it uses :attr:`prereleases` to determine
910            whether or not prereleases are allowed.
911
912        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")
913        True
914        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3"))
915        True
916        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")
917        False
918        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")
919        False
920        >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1")
921        True
922        >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
923        True
924        """
925        # Ensure that our item is a Version instance.
926        if not isinstance(item, Version):
927            item = Version(item)
928
929        # Determine if we're forcing a prerelease or not, if we're not forcing
930        # one for this particular filter call, then we'll use whatever the
931        # SpecifierSet thinks for whether or not we should support prereleases.
932        if prereleases is None:
933            prereleases = self.prereleases
934
935        # We can determine if we're going to allow pre-releases by looking to
936        # see if any of the underlying items supports them. If none of them do
937        # and this item is a pre-release then we do not allow it and we can
938        # short circuit that here.
939        # Note: This means that 1.0.dev1 would not be contained in something
940        #       like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0
941        if not prereleases and item.is_prerelease:
942            return False
943
944        if installed and item.is_prerelease:
945            item = Version(item.base_version)
946
947        # We simply dispatch to the underlying specs here to make sure that the
948        # given version is contained within all of them.
949        # Note: This use of all() here means that an empty set of specifiers
950        #       will always return True, this is an explicit design decision.
951        return all(s.contains(item, prereleases=prereleases) for s in self._specs)
952
953    def filter(
954        self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
955    ) -> Iterator[UnparsedVersionVar]:
956        """Filter items in the given iterable, that match the specifiers in this set.
957
958        :param iterable:
959            An iterable that can contain version strings and :class:`Version` instances.
960            The items in the iterable will be filtered according to the specifier.
961        :param prereleases:
962            Whether or not to allow prereleases in the returned iterator. If set to
963            ``None`` (the default), it will be intelligently decide whether to allow
964            prereleases or not (based on the :attr:`prereleases` attribute, and
965            whether the only versions matching are prereleases).
966
967        This method is smarter than just ``filter(SpecifierSet(...).contains, [...])``
968        because it implements the rule from :pep:`440` that a prerelease item
969        SHOULD be accepted if no other versions match the given specifier.
970
971        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
972        ['1.3']
973        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))
974        ['1.3', <Version('1.4')>]
975        >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))
976        []
977        >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
978        ['1.3', '1.5a1']
979        >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
980        ['1.3', '1.5a1']
981
982        An "empty" SpecifierSet will filter items based on the presence of prerelease
983        versions in the set.
984
985        >>> list(SpecifierSet("").filter(["1.3", "1.5a1"]))
986        ['1.3']
987        >>> list(SpecifierSet("").filter(["1.5a1"]))
988        ['1.5a1']
989        >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"]))
990        ['1.3', '1.5a1']
991        >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))
992        ['1.3', '1.5a1']
993        """
994        # Determine if we're forcing a prerelease or not, if we're not forcing
995        # one for this particular filter call, then we'll use whatever the
996        # SpecifierSet thinks for whether or not we should support prereleases.
997        if prereleases is None:
998            prereleases = self.prereleases
999
1000        # If we have any specifiers, then we want to wrap our iterable in the
1001        # filter method for each one, this will act as a logical AND amongst
1002        # each specifier.
1003        if self._specs:
1004            for spec in self._specs:
1005                iterable = spec.filter(iterable, prereleases=bool(prereleases))
1006            return iter(iterable)
1007        # If we do not have any specifiers, then we need to have a rough filter
1008        # which will filter out any pre-releases, unless there are no final
1009        # releases.
1010        else:
1011            filtered: List[UnparsedVersionVar] = []
1012            found_prereleases: List[UnparsedVersionVar] = []
1013
1014            for item in iterable:
1015                parsed_version = _coerce_version(item)
1016
1017                # Store any item which is a pre-release for later unless we've
1018                # already found a final version or we are accepting prereleases
1019                if parsed_version.is_prerelease and not prereleases:
1020                    if not filtered:
1021                        found_prereleases.append(item)
1022                else:
1023                    filtered.append(item)
1024
1025            # If we've found no items except for pre-releases, then we'll go
1026            # ahead and use the pre-releases
1027            if not filtered and found_prereleases and prereleases is None:
1028                return iter(found_prereleases)
1029
1030            return iter(filtered)
1031