comparison env/lib/python3.7/site-packages/packaging/specifiers.py @ 5:9b1c78e6ba9c draft default tip

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