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