comparison env/lib/python3.9/site-packages/pip/_vendor/packaging/specifiers.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
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 from __future__ import absolute_import, division, print_function
5
6 import abc
7 import functools
8 import itertools
9 import re
10 import warnings
11
12 from ._compat import string_types, with_metaclass
13 from ._typing import TYPE_CHECKING
14 from .utils import canonicalize_version
15 from .version import LegacyVersion, Version, parse
16
17 if TYPE_CHECKING: # pragma: no cover
18 from typing import Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union
19
20 ParsedVersion = Union[Version, LegacyVersion]
21 UnparsedVersion = Union[Version, LegacyVersion, str]
22 CallableOperator = Callable[[ParsedVersion, str], bool]
23
24
25 class InvalidSpecifier(ValueError):
26 """
27 An invalid specifier was found, users should refer to PEP 440.
28 """
29
30
31 class 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
95 class _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
254 class 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
320 def _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
334 class 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
634 def _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
646 def _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
665 class 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