Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: recipe_engine/third_party/setuptools/_vendor/packaging/specifiers.py

Issue 1344583003: Recipe package system. (Closed) Base URL: git@github.com:luci/recipes-py.git@master
Patch Set: Recompiled proto Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2014 Donald Stufft
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 from __future__ import absolute_import, division, print_function
15
16 import abc
17 import functools
18 import itertools
19 import re
20
21 from ._compat import string_types, with_metaclass
22 from .version import Version, LegacyVersion, parse
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)):
32
33 @abc.abstractmethod
34 def __str__(self):
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 """
43 Returns a hash value for this Specifier like object.
44 """
45
46 @abc.abstractmethod
47 def __eq__(self, other):
48 """
49 Returns a boolean representing whether or not the two Specifier like
50 objects are equal.
51 """
52
53 @abc.abstractmethod
54 def __ne__(self, other):
55 """
56 Returns a boolean representing whether or not the two Specifier like
57 objects are not equal.
58 """
59
60 @abc.abstractproperty
61 def prereleases(self):
62 """
63 Returns whether or not pre-releases as a whole are allowed by this
64 specifier.
65 """
66
67 @prereleases.setter
68 def prereleases(self, value):
69 """
70 Sets whether or not pre-releases as a whole are allowed by this
71 specifier.
72 """
73
74 @abc.abstractmethod
75 def contains(self, item, prereleases=None):
76 """
77 Determines if the given item is contained within this specifier.
78 """
79
80 @abc.abstractmethod
81 def filter(self, iterable, prereleases=None):
82 """
83 Takes an iterable of items and filters them so that only items which
84 are contained within this specifier are allowed in it.
85 """
86
87
88 class _IndividualSpecifier(BaseSpecifier):
89
90 _operators = {}
91
92 def __init__(self, spec="", prereleases=None):
93 match = self._regex.search(spec)
94 if not match:
95 raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
96
97 self._spec = (
98 match.group("operator").strip(),
99 match.group("version").strip(),
100 )
101
102 # Store whether or not this Specifier should accept prereleases
103 self._prereleases = prereleases
104
105 def __repr__(self):
106 pre = (
107 ", prereleases={0!r}".format(self.prereleases)
108 if self._prereleases is not None
109 else ""
110 )
111
112 return "<{0}({1!r}{2})>".format(
113 self.__class__.__name__,
114 str(self),
115 pre,
116 )
117
118 def __str__(self):
119 return "{0}{1}".format(*self._spec)
120
121 def __hash__(self):
122 return hash(self._spec)
123
124 def __eq__(self, other):
125 if isinstance(other, string_types):
126 try:
127 other = self.__class__(other)
128 except InvalidSpecifier:
129 return NotImplemented
130 elif not isinstance(other, self.__class__):
131 return NotImplemented
132
133 return self._spec == other._spec
134
135 def __ne__(self, other):
136 if isinstance(other, string_types):
137 try:
138 other = self.__class__(other)
139 except InvalidSpecifier:
140 return NotImplemented
141 elif not isinstance(other, self.__class__):
142 return NotImplemented
143
144 return self._spec != other._spec
145
146 def _get_operator(self, op):
147 return getattr(self, "_compare_{0}".format(self._operators[op]))
148
149 def _coerce_version(self, version):
150 if not isinstance(version, (LegacyVersion, Version)):
151 version = parse(version)
152 return version
153
154 @property
155 def prereleases(self):
156 return self._prereleases
157
158 @prereleases.setter
159 def prereleases(self, value):
160 self._prereleases = value
161
162 def contains(self, item, prereleases=None):
163 # Determine if prereleases are to be allowed or not.
164 if prereleases is None:
165 prereleases = self.prereleases
166
167 # Normalize item to a Version or LegacyVersion, this allows us to have
168 # a shortcut for ``"2.0" in Specifier(">=2")
169 item = self._coerce_version(item)
170
171 # Determine if we should be supporting prereleases in this specifier
172 # or not, if we do not support prereleases than we can short circuit
173 # logic if this version is a prereleases.
174 if item.is_prerelease and not prereleases:
175 return False
176
177 # Actually do the comparison to determine if this item is contained
178 # within this Specifier or not.
179 return self._get_operator(self._spec[0])(item, self._spec[1])
180
181 def filter(self, iterable, prereleases=None):
182 yielded = False
183 found_prereleases = []
184
185 kw = {"prereleases": prereleases if prereleases is not None else True}
186
187 # Attempt to iterate over all the values in the iterable and if any of
188 # them match, yield them.
189 for version in iterable:
190 parsed_version = self._coerce_version(version)
191
192 if self.contains(parsed_version, **kw):
193 # If our version is a prerelease, and we were not set to allow
194 # prereleases, then we'll store it for later incase nothing
195 # else matches this specifier.
196 if (parsed_version.is_prerelease
197 and not (prereleases or self.prereleases)):
198 found_prereleases.append(version)
199 # Either this is not a prerelease, or we should have been
200 # accepting prereleases from the begining.
201 else:
202 yielded = True
203 yield version
204
205 # Now that we've iterated over everything, determine if we've yielded
206 # any values, and if we have not and we have any prereleases stored up
207 # then we will go ahead and yield the prereleases.
208 if not yielded and found_prereleases:
209 for version in found_prereleases:
210 yield version
211
212
213 class LegacySpecifier(_IndividualSpecifier):
214
215 _regex = re.compile(
216 r"""
217 ^
218 \s*
219 (?P<operator>(==|!=|<=|>=|<|>))
220 \s*
221 (?P<version>
222 [^\s]* # We just match everything, except for whitespace since this
223 # is a "legacy" specifier and the version string can be just
224 # about anything.
225 )
226 \s*
227 $
228 """,
229 re.VERBOSE | re.IGNORECASE,
230 )
231
232 _operators = {
233 "==": "equal",
234 "!=": "not_equal",
235 "<=": "less_than_equal",
236 ">=": "greater_than_equal",
237 "<": "less_than",
238 ">": "greater_than",
239 }
240
241 def _coerce_version(self, version):
242 if not isinstance(version, LegacyVersion):
243 version = LegacyVersion(str(version))
244 return version
245
246 def _compare_equal(self, prospective, spec):
247 return prospective == self._coerce_version(spec)
248
249 def _compare_not_equal(self, prospective, spec):
250 return prospective != self._coerce_version(spec)
251
252 def _compare_less_than_equal(self, prospective, spec):
253 return prospective <= self._coerce_version(spec)
254
255 def _compare_greater_than_equal(self, prospective, spec):
256 return prospective >= self._coerce_version(spec)
257
258 def _compare_less_than(self, prospective, spec):
259 return prospective < self._coerce_version(spec)
260
261 def _compare_greater_than(self, prospective, spec):
262 return prospective > self._coerce_version(spec)
263
264
265 def _require_version_compare(fn):
266 @functools.wraps(fn)
267 def wrapped(self, prospective, spec):
268 if not isinstance(prospective, Version):
269 return False
270 return fn(self, prospective, spec)
271 return wrapped
272
273
274 class Specifier(_IndividualSpecifier):
275
276 _regex = re.compile(
277 r"""
278 ^
279 \s*
280 (?P<operator>(~=|==|!=|<=|>=|<|>|===))
281 (?P<version>
282 (?:
283 # The identity operators allow for an escape hatch that will
284 # do an exact string match of the version you wish to install.
285 # This will not be parsed by PEP 440 and we cannot determine
286 # any semantic meaning from it. This operator is discouraged
287 # but included entirely as an escape hatch.
288 (?<====) # Only match for the identity operator
289 \s*
290 [^\s]* # We just match everything, except for whitespace
291 # since we are only testing for strict identity.
292 )
293 |
294 (?:
295 # The (non)equality operators allow for wild card and local
296 # versions to be specified so we have to define these two
297 # operators separately to enable that.
298 (?<===|!=) # Only match for equals and not equals
299
300 \s*
301 v?
302 (?:[0-9]+!)? # epoch
303 [0-9]+(?:\.[0-9]+)* # release
304 (?: # pre release
305 [-_\.]?
306 (a|b|c|rc|alpha|beta|pre|preview)
307 [-_\.]?
308 [0-9]*
309 )?
310 (?: # post release
311 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
312 )?
313
314 # You cannot use a wild card and a dev or local version
315 # together so group them with a | and make them optional.
316 (?:
317 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
318 (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local
319 |
320 \.\* # Wild card syntax of .*
321 )?
322 )
323 |
324 (?:
325 # The compatible operator requires at least two digits in the
326 # release segment.
327 (?<=~=) # Only match for the compatible operator
328
329 \s*
330 v?
331 (?:[0-9]+!)? # epoch
332 [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *)
333 (?: # pre release
334 [-_\.]?
335 (a|b|c|rc|alpha|beta|pre|preview)
336 [-_\.]?
337 [0-9]*
338 )?
339 (?: # post release
340 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
341 )?
342 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
343 )
344 |
345 (?:
346 # All other operators only allow a sub set of what the
347 # (non)equality operators do. Specifically they do not allow
348 # local versions to be specified nor do they allow the prefix
349 # matching wild cards.
350 (?<!==|!=|~=) # We have special cases for these
351 # operators so we want to make sure they
352 # don't match here.
353
354 \s*
355 v?
356 (?:[0-9]+!)? # epoch
357 [0-9]+(?:\.[0-9]+)* # release
358 (?: # pre release
359 [-_\.]?
360 (a|b|c|rc|alpha|beta|pre|preview)
361 [-_\.]?
362 [0-9]*
363 )?
364 (?: # post release
365 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*)
366 )?
367 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release
368 )
369 )
370 \s*
371 $
372 """,
373 re.VERBOSE | re.IGNORECASE,
374 )
375
376 _operators = {
377 "~=": "compatible",
378 "==": "equal",
379 "!=": "not_equal",
380 "<=": "less_than_equal",
381 ">=": "greater_than_equal",
382 "<": "less_than",
383 ">": "greater_than",
384 "===": "arbitrary",
385 }
386
387 @_require_version_compare
388 def _compare_compatible(self, prospective, spec):
389 # Compatible releases have an equivalent combination of >= and ==. That
390 # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
391 # implement this in terms of the other specifiers instead of
392 # implementing it ourselves. The only thing we need to do is construct
393 # the other specifiers.
394
395 # We want everything but the last item in the version, but we want to
396 # ignore post and dev releases and we want to treat the pre-release as
397 # it's own separate segment.
398 prefix = ".".join(
399 list(
400 itertools.takewhile(
401 lambda x: (not x.startswith("post")
402 and not x.startswith("dev")),
403 _version_split(spec),
404 )
405 )[:-1]
406 )
407
408 # Add the prefix notation to the end of our string
409 prefix += ".*"
410
411 return (self._get_operator(">=")(prospective, spec)
412 and self._get_operator("==")(prospective, prefix))
413
414 @_require_version_compare
415 def _compare_equal(self, prospective, spec):
416 # We need special logic to handle prefix matching
417 if spec.endswith(".*"):
418 # Split the spec out by dots, and pretend that there is an implicit
419 # dot in between a release segment and a pre-release segment.
420 spec = _version_split(spec[:-2]) # Remove the trailing .*
421
422 # Split the prospective version out by dots, and pretend that there
423 # is an implicit dot in between a release segment and a pre-release
424 # segment.
425 prospective = _version_split(str(prospective))
426
427 # Shorten the prospective version to be the same length as the spec
428 # so that we can determine if the specifier is a prefix of the
429 # prospective version or not.
430 prospective = prospective[:len(spec)]
431
432 # Pad out our two sides with zeros so that they both equal the same
433 # length.
434 spec, prospective = _pad_version(spec, prospective)
435 else:
436 # Convert our spec string into a Version
437 spec = Version(spec)
438
439 # If the specifier does not have a local segment, then we want to
440 # act as if the prospective version also does not have a local
441 # segment.
442 if not spec.local:
443 prospective = Version(prospective.public)
444
445 return prospective == spec
446
447 @_require_version_compare
448 def _compare_not_equal(self, prospective, spec):
449 return not self._compare_equal(prospective, spec)
450
451 @_require_version_compare
452 def _compare_less_than_equal(self, prospective, spec):
453 return prospective <= Version(spec)
454
455 @_require_version_compare
456 def _compare_greater_than_equal(self, prospective, spec):
457 return prospective >= Version(spec)
458
459 @_require_version_compare
460 def _compare_less_than(self, prospective, spec):
461 # Less than are defined as exclusive operators, this implies that
462 # pre-releases do not match for the same series as the spec. This is
463 # implemented by making <V imply !=V.*.
464 spec = Version(spec)
465 return (prospective < spec
466 and self._get_operator("!=")(prospective, str(spec) + ".*"))
467
468 @_require_version_compare
469 def _compare_greater_than(self, prospective, spec):
470 # Greater than are defined as exclusive operators, this implies that
471 # pre-releases do not match for the same series as the spec. This is
472 # implemented by making >V imply !=V.*.
473 spec = Version(spec)
474 return (prospective > spec
475 and self._get_operator("!=")(prospective, str(spec) + ".*"))
476
477 def _compare_arbitrary(self, prospective, spec):
478 return str(prospective).lower() == str(spec).lower()
479
480 @property
481 def prereleases(self):
482 # If there is an explicit prereleases set for this, then we'll just
483 # blindly use that.
484 if self._prereleases is not None:
485 return self._prereleases
486
487 # Look at all of our specifiers and determine if they are inclusive
488 # operators, and if they are if they are including an explicit
489 # prerelease.
490 operator, version = self._spec
491 if operator in ["==", ">=", "<=", "~="]:
492 # The == specifier can include a trailing .*, if it does we
493 # want to remove before parsing.
494 if operator == "==" and version.endswith(".*"):
495 version = version[:-2]
496
497 # Parse the version, and if it is a pre-release than this
498 # specifier allows pre-releases.
499 if parse(version).is_prerelease:
500 return True
501
502 return False
503
504 @prereleases.setter
505 def prereleases(self, value):
506 self._prereleases = value
507
508
509 _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
510
511
512 def _version_split(version):
513 result = []
514 for item in version.split("."):
515 match = _prefix_regex.search(item)
516 if match:
517 result.extend(match.groups())
518 else:
519 result.append(item)
520 return result
521
522
523 def _pad_version(left, right):
524 left_split, right_split = [], []
525
526 # Get the release segment of our versions
527 left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left)))
528 right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
529
530 # Get the rest of our versions
531 left_split.append(left[len(left_split):])
532 right_split.append(left[len(right_split):])
533
534 # Insert our padding
535 left_split.insert(
536 1,
537 ["0"] * max(0, len(right_split[0]) - len(left_split[0])),
538 )
539 right_split.insert(
540 1,
541 ["0"] * max(0, len(left_split[0]) - len(right_split[0])),
542 )
543
544 return (
545 list(itertools.chain(*left_split)),
546 list(itertools.chain(*right_split)),
547 )
548
549
550 class SpecifierSet(BaseSpecifier):
551
552 def __init__(self, specifiers="", prereleases=None):
553 # Split on , to break each indidivual specifier into it's own item, and
554 # strip each item to remove leading/trailing whitespace.
555 specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
556
557 # Parsed each individual specifier, attempting first to make it a
558 # Specifier and falling back to a LegacySpecifier.
559 parsed = set()
560 for specifier in specifiers:
561 try:
562 parsed.add(Specifier(specifier))
563 except InvalidSpecifier:
564 parsed.add(LegacySpecifier(specifier))
565
566 # Turn our parsed specifiers into a frozen set and save them for later.
567 self._specs = frozenset(parsed)
568
569 # Store our prereleases value so we can use it later to determine if
570 # we accept prereleases or not.
571 self._prereleases = prereleases
572
573 def __repr__(self):
574 pre = (
575 ", prereleases={0!r}".format(self.prereleases)
576 if self._prereleases is not None
577 else ""
578 )
579
580 return "<SpecifierSet({0!r}{1})>".format(str(self), pre)
581
582 def __str__(self):
583 return ",".join(sorted(str(s) for s in self._specs))
584
585 def __hash__(self):
586 return hash(self._specs)
587
588 def __and__(self, other):
589 if isinstance(other, string_types):
590 other = SpecifierSet(other)
591 elif not isinstance(other, SpecifierSet):
592 return NotImplemented
593
594 specifier = SpecifierSet()
595 specifier._specs = frozenset(self._specs | other._specs)
596
597 if self._prereleases is None and other._prereleases is not None:
598 specifier._prereleases = other._prereleases
599 elif self._prereleases is not None and other._prereleases is None:
600 specifier._prereleases = self._prereleases
601 elif self._prereleases == other._prereleases:
602 specifier._prereleases = self._prereleases
603 else:
604 raise ValueError(
605 "Cannot combine SpecifierSets with True and False prerelease "
606 "overrides."
607 )
608
609 return specifier
610
611 def __eq__(self, other):
612 if isinstance(other, string_types):
613 other = SpecifierSet(other)
614 elif isinstance(other, _IndividualSpecifier):
615 other = SpecifierSet(str(other))
616 elif not isinstance(other, SpecifierSet):
617 return NotImplemented
618
619 return self._specs == other._specs
620
621 def __ne__(self, other):
622 if isinstance(other, string_types):
623 other = SpecifierSet(other)
624 elif isinstance(other, _IndividualSpecifier):
625 other = SpecifierSet(str(other))
626 elif not isinstance(other, SpecifierSet):
627 return NotImplemented
628
629 return self._specs != other._specs
630
631 @property
632 def prereleases(self):
633 # If we have been given an explicit prerelease modifier, then we'll
634 # pass that through here.
635 if self._prereleases is not None:
636 return self._prereleases
637
638 # Otherwise we'll see if any of the given specifiers accept
639 # prereleases, if any of them do we'll return True, otherwise False.
640 # Note: The use of any() here means that an empty set of specifiers
641 # will always return False, this is an explicit design decision.
642 return any(s.prereleases for s in self._specs)
643
644 @prereleases.setter
645 def prereleases(self, value):
646 self._prereleases = value
647
648 def contains(self, item, prereleases=None):
649 # Ensure that our item is a Version or LegacyVersion instance.
650 if not isinstance(item, (LegacyVersion, Version)):
651 item = parse(item)
652
653 # We can determine if we're going to allow pre-releases by looking to
654 # see if any of the underlying items supports them. If none of them do
655 # and this item is a pre-release then we do not allow it and we can
656 # short circuit that here.
657 # Note: This means that 1.0.dev1 would not be contained in something
658 # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0
659 if (not (self.prereleases or prereleases)) and item.is_prerelease:
660 return False
661
662 # Determine if we're forcing a prerelease or not, we bypass
663 # self.prereleases here and use self._prereleases because we want to
664 # only take into consideration actual *forced* values. The underlying
665 # specifiers will handle the other logic.
666 # The logic here is: If prereleases is anything but None, we'll just
667 # go aheand and continue to use that. However if
668 # prereleases is None, then we'll use whatever the
669 # value of self._prereleases is as long as it is not
670 # None itself.
671 if prereleases is None and self._prereleases is not None:
672 prereleases = self._prereleases
673
674 # We simply dispatch to the underlying specs here to make sure that the
675 # given version is contained within all of them.
676 # Note: This use of all() here means that an empty set of specifiers
677 # will always return True, this is an explicit design decision.
678 return all(
679 s.contains(item, prereleases=prereleases)
680 for s in self._specs
681 )
682
683 def filter(self, iterable, prereleases=None):
684 # Determine if we're forcing a prerelease or not, we bypass
685 # self.prereleases here and use self._prereleases because we want to
686 # only take into consideration actual *forced* values. The underlying
687 # specifiers will handle the other logic.
688 # The logic here is: If prereleases is anything but None, we'll just
689 # go aheand and continue to use that. However if
690 # prereleases is None, then we'll use whatever the
691 # value of self._prereleases is as long as it is not
692 # None itself.
693 if prereleases is None and self._prereleases is not None:
694 prereleases = self._prereleases
695
696 # If we have any specifiers, then we want to wrap our iterable in the
697 # filter method for each one, this will act as a logical AND amongst
698 # each specifier.
699 if self._specs:
700 for spec in self._specs:
701 iterable = spec.filter(iterable, prereleases=prereleases)
702 return iterable
703 # If we do not have any specifiers, then we need to have a rough filter
704 # which will filter out any pre-releases, unless there are no final
705 # releases, and which will filter out LegacyVersion in general.
706 else:
707 filtered = []
708 found_prereleases = []
709
710 for item in iterable:
711 # Ensure that we some kind of Version class for this item.
712 if not isinstance(item, (LegacyVersion, Version)):
713 parsed_version = parse(item)
714 else:
715 parsed_version = item
716
717 # Filter out any item which is parsed as a LegacyVersion
718 if isinstance(parsed_version, LegacyVersion):
719 continue
720
721 # Store any item which is a pre-release for later unless we've
722 # already found a final version or we are accepting prereleases
723 if parsed_version.is_prerelease and not prereleases:
724 if not filtered:
725 found_prereleases.append(item)
726 else:
727 filtered.append(item)
728
729 # If we've found no items except for pre-releases, then we'll go
730 # ahead and use the pre-releases
731 if not filtered and found_prereleases and prereleases is None:
732 return found_prereleases
733
734 return filtered
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698