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.
4from __future__ import absolute_import, division, print_function
5
6import abc
7import functools
8import itertools
9import re
10import warnings
11
12from ._compat import string_types, with_metaclass
13from ._typing import TYPE_CHECKING
14from .utils import canonicalize_version
15from .version import Version, LegacyVersion, parse
16
17if TYPE_CHECKING:  # pragma: no cover
18    from typing import List, Dict, Union, Iterable, Iterator, Optional, Callable, Tuple
19
20    ParsedVersion = Union[Version, LegacyVersion]
21    UnparsedVersion = Union[Version, LegacyVersion, str]
22    CallableOperator = Callable[[ParsedVersion, str], bool]
23
24
25class InvalidSpecifier(ValueError):
26    """
27    An invalid specifier was found, users should refer to PEP 440.
28    """
29
30
31class BaseSpecifier(with_metaclass(abc.ABCMeta, object)):  # type: ignore
32    @abc.abstractmethod
33    def __str__(self):
34        # type: () -> str
35        """
36        Returns the str representation of this Specifier like object. This
37        should be representative of the Specifier itself.
38        """
39
40    @abc.abstractmethod
41    def __hash__(self):
42        # type: () -> int
43        """
44        Returns a hash value for this Specifier like object.
45        """
46
47    @abc.abstractmethod
48    def __eq__(self, other):
49        # type: (object) -> bool
50        """
51        Returns a boolean representing whether or not the two Specifier like
52        objects are equal.
53        """
54
55    @abc.abstractmethod
56    def __ne__(self, other):
57        # type: (object) -> bool
58        """
59        Returns a boolean representing whether or not the two Specifier like
60        objects are not equal.
61        """
62
63    @abc.abstractproperty
64    def prereleases(self):
65        # type: () -> Optional[bool]
66        """
67        Returns whether or not pre-releases as a whole are allowed by this
68        specifier.
69        """
70
71    @prereleases.setter
72    def prereleases(self, value):
73        # type: (bool) -> None
74        """
75        Sets whether or not pre-releases as a whole are allowed by this
76        specifier.
77        """
78
79    @abc.abstractmethod
80    def contains(self, item, prereleases=None):
81        # type: (str, Optional[bool]) -> bool
82        """
83        Determines if the given item is contained within this specifier.
84        """
85
86    @abc.abstractmethod
87    def filter(self, iterable, prereleases=None):
88        # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
89        """
90        Takes an iterable of items and filters them so that only items which
91        are contained within this specifier are allowed in it.
92        """
93
94
95class _IndividualSpecifier(BaseSpecifier):
96
97    _operators = {}  # type: Dict[str, str]
98
99    def __init__(self, spec="", prereleases=None):
100        # type: (str, Optional[bool]) -> None
101        match = self._regex.search(spec)
102        if not match:
103            raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
104
105        self._spec = (
106            match.group("operator").strip(),
107            match.group("version").strip(),
108        )  # type: Tuple[str, str]
109
110        # Store whether or not this Specifier should accept prereleases
111        self._prereleases = prereleases
112
113    def __repr__(self):
114        # type: () -> str
115        pre = (
116            ", prereleases={0!r}".format(self.prereleases)
117            if self._prereleases is not None
118            else ""
119        )
120
121        return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
122
123    def __str__(self):
124        # type: () -> str
125        return "{0}{1}".format(*self._spec)
126
127    @property
128    def _canonical_spec(self):
129        # type: () -> Tuple[str, Union[Version, str]]
130        return self._spec[0], canonicalize_version(self._spec[1])
131
132    def __hash__(self):
133        # type: () -> int
134        return hash(self._canonical_spec)
135
136    def __eq__(self, other):
137        # type: (object) -> bool
138        if isinstance(other, string_types):
139            try:
140                other = self.__class__(str(other))
141            except InvalidSpecifier:
142                return NotImplemented
143        elif not isinstance(other, self.__class__):
144            return NotImplemented
145
146        return self._canonical_spec == other._canonical_spec
147
148    def __ne__(self, other):
149        # type: (object) -> bool
150        if isinstance(other, string_types):
151            try:
152                other = self.__class__(str(other))
153            except InvalidSpecifier:
154                return NotImplemented
155        elif not isinstance(other, self.__class__):
156            return NotImplemented
157
158        return self._spec != other._spec
159
160    def _get_operator(self, op):
161        # type: (str) -> CallableOperator
162        operator_callable = getattr(
163            self, "_compare_{0}".format(self._operators[op])
164        )  # type: CallableOperator
165        return operator_callable
166
167    def _coerce_version(self, version):
168        # type: (UnparsedVersion) -> ParsedVersion
169        if not isinstance(version, (LegacyVersion, Version)):
170            version = parse(version)
171        return version
172
173    @property
174    def operator(self):
175        # type: () -> str
176        return self._spec[0]
177
178    @property
179    def version(self):
180        # type: () -> str
181        return self._spec[1]
182
183    @property
184    def prereleases(self):
185        # type: () -> Optional[bool]
186        return self._prereleases
187
188    @prereleases.setter
189    def prereleases(self, value):
190        # type: (bool) -> None
191        self._prereleases = value
192
193    def __contains__(self, item):
194        # type: (str) -> bool
195        return self.contains(item)
196
197    def contains(self, item, prereleases=None):
198        # type: (UnparsedVersion, Optional[bool]) -> bool
199
200        # Determine if prereleases are to be allowed or not.
201        if prereleases is None:
202            prereleases = self.prereleases
203
204        # Normalize item to a Version or LegacyVersion, this allows us to have
205        # a shortcut for ``"2.0" in Specifier(">=2")
206        normalized_item = self._coerce_version(item)
207
208        # Determine if we should be supporting prereleases in this specifier
209        # or not, if we do not support prereleases than we can short circuit
210        # logic if this version is a prereleases.
211        if normalized_item.is_prerelease and not prereleases:
212            return False
213
214        # Actually do the comparison to determine if this item is contained
215        # within this Specifier or not.
216        operator_callable = self._get_operator(self.operator)  # type: CallableOperator
217        return operator_callable(normalized_item, self.version)
218
219    def filter(self, iterable, prereleases=None):
220        # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
221
222        yielded = False
223        found_prereleases = []
224
225        kw = {"prereleases": prereleases if prereleases is not None else True}
226
227        # Attempt to iterate over all the values in the iterable and if any of
228        # them match, yield them.
229        for version in iterable:
230            parsed_version = self._coerce_version(version)
231
232            if self.contains(parsed_version, **kw):
233                # If our version is a prerelease, and we were not set to allow
234                # prereleases, then we'll store it for later incase nothing
235                # else matches this specifier.
236                if parsed_version.is_prerelease and not (
237                    prereleases or self.prereleases
238                ):
239                    found_prereleases.append(version)
240                # Either this is not a prerelease, or we should have been
241                # accepting prereleases from the beginning.
242                else:
243                    yielded = True
244                    yield version
245
246        # Now that we've iterated over everything, determine if we've yielded
247        # any values, and if we have not and we have any prereleases stored up
248        # then we will go ahead and yield the prereleases.
249        if not yielded and found_prereleases:
250            for version in found_prereleases:
251                yield version
252
253
254class LegacySpecifier(_IndividualSpecifier):
255
256    _regex_str = r"""
257        (?P<operator>(==|!=|<=|>=|<|>))
258        \s*
259        (?P<version>
260            [^,;\s)]* # Since this is a "legacy" specifier, and the version
261                      # string can be just about anything, we match everything
262                      # except for whitespace, a semi-colon for marker support,
263                      # a closing paren since versions can be enclosed in
264                      # them, and a comma since it's a version separator.
265        )
266        """
267
268    _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
269
270    _operators = {
271        "==": "equal",
272        "!=": "not_equal",
273        "<=": "less_than_equal",
274        ">=": "greater_than_equal",
275        "<": "less_than",
276        ">": "greater_than",
277    }
278
279    def __init__(self, spec="", prereleases=None):
280        # type: (str, Optional[bool]) -> None
281        super(LegacySpecifier, self).__init__(spec, prereleases)
282
283        warnings.warn(
284            "Creating a LegacyVersion has been deprecated and will be "
285            "removed in the next major release",
286            DeprecationWarning,
287        )
288
289    def _coerce_version(self, version):
290        # type: (Union[ParsedVersion, str]) -> LegacyVersion
291        if not isinstance(version, LegacyVersion):
292            version = LegacyVersion(str(version))
293        return version
294
295    def _compare_equal(self, prospective, spec):
296        # type: (LegacyVersion, str) -> bool
297        return prospective == self._coerce_version(spec)
298
299    def _compare_not_equal(self, prospective, spec):
300        # type: (LegacyVersion, str) -> bool
301        return prospective != self._coerce_version(spec)
302
303    def _compare_less_than_equal(self, prospective, spec):
304        # type: (LegacyVersion, str) -> bool
305        return prospective <= self._coerce_version(spec)
306
307    def _compare_greater_than_equal(self, prospective, spec):
308        # type: (LegacyVersion, str) -> bool
309        return prospective >= self._coerce_version(spec)
310
311    def _compare_less_than(self, prospective, spec):
312        # type: (LegacyVersion, str) -> bool
313        return prospective < self._coerce_version(spec)
314
315    def _compare_greater_than(self, prospective, spec):
316        # type: (LegacyVersion, str) -> bool
317        return prospective > self._coerce_version(spec)
318
319
320def _require_version_compare(
321    fn,  # type: (Callable[[Specifier, ParsedVersion, str], bool])
322):
323    # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool]
324    @functools.wraps(fn)
325    def wrapped(self, prospective, spec):
326        # type: (Specifier, ParsedVersion, str) -> bool
327        if not isinstance(prospective, Version):
328            return False
329        return fn(self, prospective, spec)
330
331    return wrapped
332
333
334class Specifier(_IndividualSpecifier):
335
336    _regex_str = r"""
337        (?P<operator>(~=|==|!=|<=|>=|<|>|===))
338        (?P<version>
339            (?:
340                # The identity operators allow for an escape hatch that will
341                # do an exact string match of the version you wish to install.
342                # This will not be parsed by PEP 440 and we cannot determine
343                # any semantic meaning from it. This operator is discouraged
344                # but included entirely as an escape hatch.
345                (?<====)  # Only match for the identity operator
346                \s*
347                [^\s]*    # We just match everything, except for whitespace
348                          # since we are only testing for strict identity.
349            )
350            |
351            (?:
352                # The (non)equality operators allow for wild card and local
353                # versions to be specified so we have to define these two
354                # operators separately to enable that.
355                (?<===|!=)            # Only match for equals and not equals
356
357                \s*
358                v?
359                (?:[0-9]+!)?          # epoch
360                [0-9]+(?:\.[0-9]+)*   # release
361                (?:                   # pre release
362                    [-_\.]?
363                    (a|b|c|rc|alpha|beta|pre|preview)
364                    [-_\.]?
365                    [0-9]*
366                )?
367                (?:                   # post release
368                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
369                )?
370
371                # You cannot use a wild card and a dev or local version
372                # together so group them with a | and make them optional.
373                (?:
374                    (?:[-_\.]?dev[-_\.]?[0-9]*)?         # dev release
375                    (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
376                    |
377                    \.\*  # Wild card syntax of .*
378                )?
379            )
380            |
381            (?:
382                # The compatible operator requires at least two digits in the
383                # release segment.
384                (?<=~=)               # Only match for the compatible operator
385
386                \s*
387                v?
388                (?:[0-9]+!)?          # epoch
389                [0-9]+(?:\.[0-9]+)+   # release  (We have a + instead of a *)
390                (?:                   # pre release
391                    [-_\.]?
392                    (a|b|c|rc|alpha|beta|pre|preview)
393                    [-_\.]?
394                    [0-9]*
395                )?
396                (?:                                   # post release
397                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
398                )?
399                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
400            )
401            |
402            (?:
403                # All other operators only allow a sub set of what the
404                # (non)equality operators do. Specifically they do not allow
405                # local versions to be specified nor do they allow the prefix
406                # matching wild cards.
407                (?<!==|!=|~=)         # We have special cases for these
408                                      # operators so we want to make sure they
409                                      # don't match here.
410
411                \s*
412                v?
413                (?:[0-9]+!)?          # epoch
414                [0-9]+(?:\.[0-9]+)*   # release
415                (?:                   # pre release
416                    [-_\.]?
417                    (a|b|c|rc|alpha|beta|pre|preview)
418                    [-_\.]?
419                    [0-9]*
420                )?
421                (?:                                   # post release
422                    (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
423                )?
424                (?:[-_\.]?dev[-_\.]?[0-9]*)?          # dev release
425            )
426        )
427        """
428
429    _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
430
431    _operators = {
432        "~=": "compatible",
433        "==": "equal",
434        "!=": "not_equal",
435        "<=": "less_than_equal",
436        ">=": "greater_than_equal",
437        "<": "less_than",
438        ">": "greater_than",
439        "===": "arbitrary",
440    }
441
442    @_require_version_compare
443    def _compare_compatible(self, prospective, spec):
444        # type: (ParsedVersion, str) -> bool
445
446        # Compatible releases have an equivalent combination of >= and ==. That
447        # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
448        # implement this in terms of the other specifiers instead of
449        # implementing it ourselves. The only thing we need to do is construct
450        # the other specifiers.
451
452        # We want everything but the last item in the version, but we want to
453        # ignore post and dev releases and we want to treat the pre-release as
454        # it's own separate segment.
455        prefix = ".".join(
456            list(
457                itertools.takewhile(
458                    lambda x: (not x.startswith("post") and not x.startswith("dev")),
459                    _version_split(spec),
460                )
461            )[:-1]
462        )
463
464        # Add the prefix notation to the end of our string
465        prefix += ".*"
466
467        return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
468            prospective, prefix
469        )
470
471    @_require_version_compare
472    def _compare_equal(self, prospective, spec):
473        # type: (ParsedVersion, str) -> bool
474
475        # We need special logic to handle prefix matching
476        if spec.endswith(".*"):
477            # In the case of prefix matching we want to ignore local segment.
478            prospective = Version(prospective.public)
479            # Split the spec out by dots, and pretend that there is an implicit
480            # dot in between a release segment and a pre-release segment.
481            split_spec = _version_split(spec[:-2])  # Remove the trailing .*
482
483            # Split the prospective version out by dots, and pretend that there
484            # is an implicit dot in between a release segment and a pre-release
485            # segment.
486            split_prospective = _version_split(str(prospective))
487
488            # Shorten the prospective version to be the same length as the spec
489            # so that we can determine if the specifier is a prefix of the
490            # prospective version or not.
491            shortened_prospective = split_prospective[: len(split_spec)]
492
493            # Pad out our two sides with zeros so that they both equal the same
494            # length.
495            padded_spec, padded_prospective = _pad_version(
496                split_spec, shortened_prospective
497            )
498
499            return padded_prospective == padded_spec
500        else:
501            # Convert our spec string into a Version
502            spec_version = Version(spec)
503
504            # If the specifier does not have a local segment, then we want to
505            # act as if the prospective version also does not have a local
506            # segment.
507            if not spec_version.local:
508                prospective = Version(prospective.public)
509
510            return prospective == spec_version
511
512    @_require_version_compare
513    def _compare_not_equal(self, prospective, spec):
514        # type: (ParsedVersion, str) -> bool
515        return not self._compare_equal(prospective, spec)
516
517    @_require_version_compare
518    def _compare_less_than_equal(self, prospective, spec):
519        # type: (ParsedVersion, str) -> bool
520
521        # NB: Local version identifiers are NOT permitted in the version
522        # specifier, so local version labels can be universally removed from
523        # the prospective version.
524        return Version(prospective.public) <= Version(spec)
525
526    @_require_version_compare
527    def _compare_greater_than_equal(self, prospective, spec):
528        # type: (ParsedVersion, str) -> bool
529
530        # NB: Local version identifiers are NOT permitted in the version
531        # specifier, so local version labels can be universally removed from
532        # the prospective version.
533        return Version(prospective.public) >= Version(spec)
534
535    @_require_version_compare
536    def _compare_less_than(self, prospective, spec_str):
537        # type: (ParsedVersion, str) -> bool
538
539        # Convert our spec to a Version instance, since we'll want to work with
540        # it as a version.
541        spec = Version(spec_str)
542
543        # Check to see if the prospective version is less than the spec
544        # version. If it's not we can short circuit and just return False now
545        # instead of doing extra unneeded work.
546        if not prospective < spec:
547            return False
548
549        # This special case is here so that, unless the specifier itself
550        # includes is a pre-release version, that we do not accept pre-release
551        # versions for the version mentioned in the specifier (e.g. <3.1 should
552        # not match 3.1.dev0, but should match 3.0.dev0).
553        if not spec.is_prerelease and prospective.is_prerelease:
554            if Version(prospective.base_version) == Version(spec.base_version):
555                return False
556
557        # If we've gotten to here, it means that prospective version is both
558        # less than the spec version *and* it's not a pre-release of the same
559        # version in the spec.
560        return True
561
562    @_require_version_compare
563    def _compare_greater_than(self, prospective, spec_str):
564        # type: (ParsedVersion, str) -> bool
565
566        # Convert our spec to a Version instance, since we'll want to work with
567        # it as a version.
568        spec = Version(spec_str)
569
570        # Check to see if the prospective version is greater than the spec
571        # version. If it's not we can short circuit and just return False now
572        # instead of doing extra unneeded work.
573        if not prospective > spec:
574            return False
575
576        # This special case is here so that, unless the specifier itself
577        # includes is a post-release version, that we do not accept
578        # post-release versions for the version mentioned in the specifier
579        # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0).
580        if not spec.is_postrelease and prospective.is_postrelease:
581            if Version(prospective.base_version) == Version(spec.base_version):
582                return False
583
584        # Ensure that we do not allow a local version of the version mentioned
585        # in the specifier, which is technically greater than, to match.
586        if prospective.local is not None:
587            if Version(prospective.base_version) == Version(spec.base_version):
588                return False
589
590        # If we've gotten to here, it means that prospective version is both
591        # greater than the spec version *and* it's not a pre-release of the
592        # same version in the spec.
593        return True
594
595    def _compare_arbitrary(self, prospective, spec):
596        # type: (Version, str) -> bool
597        return str(prospective).lower() == str(spec).lower()
598
599    @property
600    def prereleases(self):
601        # type: () -> bool
602
603        # If there is an explicit prereleases set for this, then we'll just
604        # blindly use that.
605        if self._prereleases is not None:
606            return self._prereleases
607
608        # Look at all of our specifiers and determine if they are inclusive
609        # operators, and if they are if they are including an explicit
610        # prerelease.
611        operator, version = self._spec
612        if operator in ["==", ">=", "<=", "~=", "==="]:
613            # The == specifier can include a trailing .*, if it does we
614            # want to remove before parsing.
615            if operator == "==" and version.endswith(".*"):
616                version = version[:-2]
617
618            # Parse the version, and if it is a pre-release than this
619            # specifier allows pre-releases.
620            if parse(version).is_prerelease:
621                return True
622
623        return False
624
625    @prereleases.setter
626    def prereleases(self, value):
627        # type: (bool) -> None
628        self._prereleases = value
629
630
631_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
632
633
634def _version_split(version):
635    # type: (str) -> List[str]
636    result = []  # type: List[str]
637    for item in version.split("."):
638        match = _prefix_regex.search(item)
639        if match:
640            result.extend(match.groups())
641        else:
642            result.append(item)
643    return result
644
645
646def _pad_version(left, right):
647    # type: (List[str], List[str]) -> Tuple[List[str], List[str]]
648    left_split, right_split = [], []
649
650    # Get the release segment of our versions
651    left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left)))
652    right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
653
654    # Get the rest of our versions
655    left_split.append(left[len(left_split[0]) :])
656    right_split.append(right[len(right_split[0]) :])
657
658    # Insert our padding
659    left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
660    right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
661
662    return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
663
664
665class SpecifierSet(BaseSpecifier):
666    def __init__(self, specifiers="", prereleases=None):
667        # type: (str, Optional[bool]) -> None
668
669        # Split on , to break each individual specifier into it's own item, and
670        # strip each item to remove leading/trailing whitespace.
671        split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
672
673        # Parsed each individual specifier, attempting first to make it a
674        # Specifier and falling back to a LegacySpecifier.
675        parsed = set()
676        for specifier in split_specifiers:
677            try:
678                parsed.add(Specifier(specifier))
679            except InvalidSpecifier:
680                parsed.add(LegacySpecifier(specifier))
681
682        # Turn our parsed specifiers into a frozen set and save them for later.
683        self._specs = frozenset(parsed)
684
685        # Store our prereleases value so we can use it later to determine if
686        # we accept prereleases or not.
687        self._prereleases = prereleases
688
689    def __repr__(self):
690        # type: () -> str
691        pre = (
692            ", prereleases={0!r}".format(self.prereleases)
693            if self._prereleases is not None
694            else ""
695        )
696
697        return "<SpecifierSet({0!r}{1})>".format(str(self), pre)
698
699    def __str__(self):
700        # type: () -> str
701        return ",".join(sorted(str(s) for s in self._specs))
702
703    def __hash__(self):
704        # type: () -> int
705        return hash(self._specs)
706
707    def __and__(self, other):
708        # type: (Union[SpecifierSet, str]) -> SpecifierSet
709        if isinstance(other, string_types):
710            other = SpecifierSet(other)
711        elif not isinstance(other, SpecifierSet):
712            return NotImplemented
713
714        specifier = SpecifierSet()
715        specifier._specs = frozenset(self._specs | other._specs)
716
717        if self._prereleases is None and other._prereleases is not None:
718            specifier._prereleases = other._prereleases
719        elif self._prereleases is not None and other._prereleases is None:
720            specifier._prereleases = self._prereleases
721        elif self._prereleases == other._prereleases:
722            specifier._prereleases = self._prereleases
723        else:
724            raise ValueError(
725                "Cannot combine SpecifierSets with True and False prerelease "
726                "overrides."
727            )
728
729        return specifier
730
731    def __eq__(self, other):
732        # type: (object) -> bool
733        if isinstance(other, (string_types, _IndividualSpecifier)):
734            other = SpecifierSet(str(other))
735        elif not isinstance(other, SpecifierSet):
736            return NotImplemented
737
738        return self._specs == other._specs
739
740    def __ne__(self, other):
741        # type: (object) -> bool
742        if isinstance(other, (string_types, _IndividualSpecifier)):
743            other = SpecifierSet(str(other))
744        elif not isinstance(other, SpecifierSet):
745            return NotImplemented
746
747        return self._specs != other._specs
748
749    def __len__(self):
750        # type: () -> int
751        return len(self._specs)
752
753    def __iter__(self):
754        # type: () -> Iterator[_IndividualSpecifier]
755        return iter(self._specs)
756
757    @property
758    def prereleases(self):
759        # type: () -> Optional[bool]
760
761        # If we have been given an explicit prerelease modifier, then we'll
762        # pass that through here.
763        if self._prereleases is not None:
764            return self._prereleases
765
766        # If we don't have any specifiers, and we don't have a forced value,
767        # then we'll just return None since we don't know if this should have
768        # pre-releases or not.
769        if not self._specs:
770            return None
771
772        # Otherwise we'll see if any of the given specifiers accept
773        # prereleases, if any of them do we'll return True, otherwise False.
774        return any(s.prereleases for s in self._specs)
775
776    @prereleases.setter
777    def prereleases(self, value):
778        # type: (bool) -> None
779        self._prereleases = value
780
781    def __contains__(self, item):
782        # type: (Union[ParsedVersion, str]) -> bool
783        return self.contains(item)
784
785    def contains(self, item, prereleases=None):
786        # type: (Union[ParsedVersion, str], Optional[bool]) -> bool
787
788        # Ensure that our item is a Version or LegacyVersion instance.
789        if not isinstance(item, (LegacyVersion, Version)):
790            item = parse(item)
791
792        # Determine if we're forcing a prerelease or not, if we're not forcing
793        # one for this particular filter call, then we'll use whatever the
794        # SpecifierSet thinks for whether or not we should support prereleases.
795        if prereleases is None:
796            prereleases = self.prereleases
797
798        # We can determine if we're going to allow pre-releases by looking to
799        # see if any of the underlying items supports them. If none of them do
800        # and this item is a pre-release then we do not allow it and we can
801        # short circuit that here.
802        # Note: This means that 1.0.dev1 would not be contained in something
803        #       like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0
804        if not prereleases and item.is_prerelease:
805            return False
806
807        # We simply dispatch to the underlying specs here to make sure that the
808        # given version is contained within all of them.
809        # Note: This use of all() here means that an empty set of specifiers
810        #       will always return True, this is an explicit design decision.
811        return all(s.contains(item, prereleases=prereleases) for s in self._specs)
812
813    def filter(
814        self,
815        iterable,  # type: Iterable[Union[ParsedVersion, str]]
816        prereleases=None,  # type: Optional[bool]
817    ):
818        # type: (...) -> Iterable[Union[ParsedVersion, str]]
819
820        # Determine if we're forcing a prerelease or not, if we're not forcing
821        # one for this particular filter call, then we'll use whatever the
822        # SpecifierSet thinks for whether or not we should support prereleases.
823        if prereleases is None:
824            prereleases = self.prereleases
825
826        # If we have any specifiers, then we want to wrap our iterable in the
827        # filter method for each one, this will act as a logical AND amongst
828        # each specifier.
829        if self._specs:
830            for spec in self._specs:
831                iterable = spec.filter(iterable, prereleases=bool(prereleases))
832            return iterable
833        # If we do not have any specifiers, then we need to have a rough filter
834        # which will filter out any pre-releases, unless there are no final
835        # releases, and which will filter out LegacyVersion in general.
836        else:
837            filtered = []  # type: List[Union[ParsedVersion, str]]
838            found_prereleases = []  # type: List[Union[ParsedVersion, str]]
839
840            for item in iterable:
841                # Ensure that we some kind of Version class for this item.
842                if not isinstance(item, (LegacyVersion, Version)):
843                    parsed_version = parse(item)
844                else:
845                    parsed_version = item
846
847                # Filter out any item which is parsed as a LegacyVersion
848                if isinstance(parsed_version, LegacyVersion):
849                    continue
850
851                # Store any item which is a pre-release for later unless we've
852                # already found a final version or we are accepting prereleases
853                if parsed_version.is_prerelease and not prereleases:
854                    if not filtered:
855                        found_prereleases.append(item)
856                else:
857                    filtered.append(item)
858
859            # If we've found no items except for pre-releases, then we'll go
860            # ahead and use the pre-releases
861            if not filtered and found_prereleases and prereleases is None:
862                return found_prereleases
863
864            return filtered
865