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