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""" 5.. testsetup:: 6 7 from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier 8 from packaging.version import Version 9""" 10 11import abc 12import itertools 13import re 14from typing import ( 15 Callable, 16 Iterable, 17 Iterator, 18 List, 19 Optional, 20 Set, 21 Tuple, 22 TypeVar, 23 Union, 24) 25 26from .utils import canonicalize_version 27from .version import Version 28 29UnparsedVersion = Union[Version, str] 30UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) 31CallableOperator = Callable[[Version, str], bool] 32 33 34def _coerce_version(version: UnparsedVersion) -> Version: 35 if not isinstance(version, Version): 36 version = Version(version) 37 return version 38 39 40class InvalidSpecifier(ValueError): 41 """ 42 Raised when attempting to create a :class:`Specifier` with a specifier 43 string that is invalid. 44 45 >>> Specifier("lolwat") 46 Traceback (most recent call last): 47 ... 48 packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' 49 """ 50 51 52class BaseSpecifier(metaclass=abc.ABCMeta): 53 @abc.abstractmethod 54 def __str__(self) -> str: 55 """ 56 Returns the str representation of this Specifier-like object. This 57 should be representative of the Specifier itself. 58 """ 59 60 @abc.abstractmethod 61 def __hash__(self) -> int: 62 """ 63 Returns a hash value for this Specifier-like object. 64 """ 65 66 @abc.abstractmethod 67 def __eq__(self, other: object) -> bool: 68 """ 69 Returns a boolean representing whether or not the two Specifier-like 70 objects are equal. 71 72 :param other: The other object to check against. 73 """ 74 75 @property 76 @abc.abstractmethod 77 def prereleases(self) -> Optional[bool]: 78 """Whether or not pre-releases as a whole are allowed. 79 80 This can be set to either ``True`` or ``False`` to explicitly enable or disable 81 prereleases or it can be set to ``None`` (the default) to use default semantics. 82 """ 83 84 @prereleases.setter 85 def prereleases(self, value: bool) -> None: 86 """Setter for :attr:`prereleases`. 87 88 :param value: The value to set. 89 """ 90 91 @abc.abstractmethod 92 def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: 93 """ 94 Determines if the given item is contained within this specifier. 95 """ 96 97 @abc.abstractmethod 98 def filter( 99 self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None 100 ) -> Iterator[UnparsedVersionVar]: 101 """ 102 Takes an iterable of items and filters them so that only items which 103 are contained within this specifier are allowed in it. 104 """ 105 106 107class Specifier(BaseSpecifier): 108 """This class abstracts handling of version specifiers. 109 110 .. tip:: 111 112 It is generally not required to instantiate this manually. You should instead 113 prefer to work with :class:`SpecifierSet` instead, which can parse 114 comma-separated version specifiers (which is what package metadata contains). 115 """ 116 117 _operator_regex_str = r""" 118 (?P<operator>(~=|==|!=|<=|>=|<|>|===)) 119 """ 120 _version_regex_str = r""" 121 (?P<version> 122 (?: 123 # The identity operators allow for an escape hatch that will 124 # do an exact string match of the version you wish to install. 125 # This will not be parsed by PEP 440 and we cannot determine 126 # any semantic meaning from it. This operator is discouraged 127 # but included entirely as an escape hatch. 128 (?<====) # Only match for the identity operator 129 \s* 130 [^\s;)]* # The arbitrary version can be just about anything, 131 # we match everything except for whitespace, a 132 # semi-colon for marker support, and a closing paren 133 # since versions can be enclosed in them. 134 ) 135 | 136 (?: 137 # The (non)equality operators allow for wild card and local 138 # versions to be specified so we have to define these two 139 # operators separately to enable that. 140 (?<===|!=) # Only match for equals and not equals 141 142 \s* 143 v? 144 (?:[0-9]+!)? # epoch 145 [0-9]+(?:\.[0-9]+)* # release 146 147 # You cannot use a wild card and a pre-release, post-release, a dev or 148 # local version together so group them with a | and make them optional. 149 (?: 150 \.\* # Wild card syntax of .* 151 | 152 (?: # pre release 153 [-_\.]? 154 (alpha|beta|preview|pre|a|b|c|rc) 155 [-_\.]? 156 [0-9]* 157 )? 158 (?: # post release 159 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) 160 )? 161 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release 162 (?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local 163 )? 164 ) 165 | 166 (?: 167 # The compatible operator requires at least two digits in the 168 # release segment. 169 (?<=~=) # Only match for the compatible operator 170 171 \s* 172 v? 173 (?:[0-9]+!)? # epoch 174 [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) 175 (?: # pre release 176 [-_\.]? 177 (alpha|beta|preview|pre|a|b|c|rc) 178 [-_\.]? 179 [0-9]* 180 )? 181 (?: # post release 182 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) 183 )? 184 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release 185 ) 186 | 187 (?: 188 # All other operators only allow a sub set of what the 189 # (non)equality operators do. Specifically they do not allow 190 # local versions to be specified nor do they allow the prefix 191 # matching wild cards. 192 (?<!==|!=|~=) # We have special cases for these 193 # operators so we want to make sure they 194 # don't match here. 195 196 \s* 197 v? 198 (?:[0-9]+!)? # epoch 199 [0-9]+(?:\.[0-9]+)* # release 200 (?: # pre release 201 [-_\.]? 202 (alpha|beta|preview|pre|a|b|c|rc) 203 [-_\.]? 204 [0-9]* 205 )? 206 (?: # post release 207 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) 208 )? 209 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release 210 ) 211 ) 212 """ 213 214 _regex = re.compile( 215 r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$", 216 re.VERBOSE | re.IGNORECASE, 217 ) 218 219 _operators = { 220 "~=": "compatible", 221 "==": "equal", 222 "!=": "not_equal", 223 "<=": "less_than_equal", 224 ">=": "greater_than_equal", 225 "<": "less_than", 226 ">": "greater_than", 227 "===": "arbitrary", 228 } 229 230 def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: 231 """Initialize a Specifier instance. 232 233 :param spec: 234 The string representation of a specifier which will be parsed and 235 normalized before use. 236 :param prereleases: 237 This tells the specifier if it should accept prerelease versions if 238 applicable or not. The default of ``None`` will autodetect it from the 239 given specifiers. 240 :raises InvalidSpecifier: 241 If the given specifier is invalid (i.e. bad syntax). 242 """ 243 match = self._regex.search(spec) 244 if not match: 245 raise InvalidSpecifier(f"Invalid specifier: '{spec}'") 246 247 self._spec: Tuple[str, str] = ( 248 match.group("operator").strip(), 249 match.group("version").strip(), 250 ) 251 252 # Store whether or not this Specifier should accept prereleases 253 self._prereleases = prereleases 254 255 # https://github.com/python/mypy/pull/13475#pullrequestreview-1079784515 256 @property # type: ignore[override] 257 def prereleases(self) -> bool: 258 # If there is an explicit prereleases set for this, then we'll just 259 # blindly use that. 260 if self._prereleases is not None: 261 return self._prereleases 262 263 # Look at all of our specifiers and determine if they are inclusive 264 # operators, and if they are if they are including an explicit 265 # prerelease. 266 operator, version = self._spec 267 if operator in ["==", ">=", "<=", "~=", "==="]: 268 # The == specifier can include a trailing .*, if it does we 269 # want to remove before parsing. 270 if operator == "==" and version.endswith(".*"): 271 version = version[:-2] 272 273 # Parse the version, and if it is a pre-release than this 274 # specifier allows pre-releases. 275 if Version(version).is_prerelease: 276 return True 277 278 return False 279 280 @prereleases.setter 281 def prereleases(self, value: bool) -> None: 282 self._prereleases = value 283 284 @property 285 def operator(self) -> str: 286 """The operator of this specifier. 287 288 >>> Specifier("==1.2.3").operator 289 '==' 290 """ 291 return self._spec[0] 292 293 @property 294 def version(self) -> str: 295 """The version of this specifier. 296 297 >>> Specifier("==1.2.3").version 298 '1.2.3' 299 """ 300 return self._spec[1] 301 302 def __repr__(self) -> str: 303 """A representation of the Specifier that shows all internal state. 304 305 >>> Specifier('>=1.0.0') 306 <Specifier('>=1.0.0')> 307 >>> Specifier('>=1.0.0', prereleases=False) 308 <Specifier('>=1.0.0', prereleases=False)> 309 >>> Specifier('>=1.0.0', prereleases=True) 310 <Specifier('>=1.0.0', prereleases=True)> 311 """ 312 pre = ( 313 f", prereleases={self.prereleases!r}" 314 if self._prereleases is not None 315 else "" 316 ) 317 318 return f"<{self.__class__.__name__}({str(self)!r}{pre})>" 319 320 def __str__(self) -> str: 321 """A string representation of the Specifier that can be round-tripped. 322 323 >>> str(Specifier('>=1.0.0')) 324 '>=1.0.0' 325 >>> str(Specifier('>=1.0.0', prereleases=False)) 326 '>=1.0.0' 327 """ 328 return "{}{}".format(*self._spec) 329 330 @property 331 def _canonical_spec(self) -> Tuple[str, str]: 332 canonical_version = canonicalize_version( 333 self._spec[1], 334 strip_trailing_zero=(self._spec[0] != "~="), 335 ) 336 return self._spec[0], canonical_version 337 338 def __hash__(self) -> int: 339 return hash(self._canonical_spec) 340 341 def __eq__(self, other: object) -> bool: 342 """Whether or not the two Specifier-like objects are equal. 343 344 :param other: The other object to check against. 345 346 The value of :attr:`prereleases` is ignored. 347 348 >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") 349 True 350 >>> (Specifier("==1.2.3", prereleases=False) == 351 ... Specifier("==1.2.3", prereleases=True)) 352 True 353 >>> Specifier("==1.2.3") == "==1.2.3" 354 True 355 >>> Specifier("==1.2.3") == Specifier("==1.2.4") 356 False 357 >>> Specifier("==1.2.3") == Specifier("~=1.2.3") 358 False 359 """ 360 if isinstance(other, str): 361 try: 362 other = self.__class__(str(other)) 363 except InvalidSpecifier: 364 return NotImplemented 365 elif not isinstance(other, self.__class__): 366 return NotImplemented 367 368 return self._canonical_spec == other._canonical_spec 369 370 def _get_operator(self, op: str) -> CallableOperator: 371 operator_callable: CallableOperator = getattr( 372 self, f"_compare_{self._operators[op]}" 373 ) 374 return operator_callable 375 376 def _compare_compatible(self, prospective: Version, spec: str) -> bool: 377 378 # Compatible releases have an equivalent combination of >= and ==. That 379 # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to 380 # implement this in terms of the other specifiers instead of 381 # implementing it ourselves. The only thing we need to do is construct 382 # the other specifiers. 383 384 # We want everything but the last item in the version, but we want to 385 # ignore suffix segments. 386 prefix = _version_join( 387 list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] 388 ) 389 390 # Add the prefix notation to the end of our string 391 prefix += ".*" 392 393 return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( 394 prospective, prefix 395 ) 396 397 def _compare_equal(self, prospective: Version, spec: str) -> bool: 398 399 # We need special logic to handle prefix matching 400 if spec.endswith(".*"): 401 # In the case of prefix matching we want to ignore local segment. 402 normalized_prospective = canonicalize_version( 403 prospective.public, strip_trailing_zero=False 404 ) 405 # Get the normalized version string ignoring the trailing .* 406 normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) 407 # Split the spec out by bangs and dots, and pretend that there is 408 # an implicit dot in between a release segment and a pre-release segment. 409 split_spec = _version_split(normalized_spec) 410 411 # Split the prospective version out by bangs and dots, and pretend 412 # that there is an implicit dot in between a release segment and 413 # a pre-release segment. 414 split_prospective = _version_split(normalized_prospective) 415 416 # 0-pad the prospective version before shortening it to get the correct 417 # shortened version. 418 padded_prospective, _ = _pad_version(split_prospective, split_spec) 419 420 # Shorten the prospective version to be the same length as the spec 421 # so that we can determine if the specifier is a prefix of the 422 # prospective version or not. 423 shortened_prospective = padded_prospective[: len(split_spec)] 424 425 return shortened_prospective == split_spec 426 else: 427 # Convert our spec string into a Version 428 spec_version = Version(spec) 429 430 # If the specifier does not have a local segment, then we want to 431 # act as if the prospective version also does not have a local 432 # segment. 433 if not spec_version.local: 434 prospective = Version(prospective.public) 435 436 return prospective == spec_version 437 438 def _compare_not_equal(self, prospective: Version, spec: str) -> bool: 439 return not self._compare_equal(prospective, spec) 440 441 def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: 442 443 # NB: Local version identifiers are NOT permitted in the version 444 # specifier, so local version labels can be universally removed from 445 # the prospective version. 446 return Version(prospective.public) <= Version(spec) 447 448 def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: 449 450 # NB: Local version identifiers are NOT permitted in the version 451 # specifier, so local version labels can be universally removed from 452 # the prospective version. 453 return Version(prospective.public) >= Version(spec) 454 455 def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: 456 457 # Convert our spec to a Version instance, since we'll want to work with 458 # it as a version. 459 spec = Version(spec_str) 460 461 # Check to see if the prospective version is less than the spec 462 # version. If it's not we can short circuit and just return False now 463 # instead of doing extra unneeded work. 464 if not prospective < spec: 465 return False 466 467 # This special case is here so that, unless the specifier itself 468 # includes is a pre-release version, that we do not accept pre-release 469 # versions for the version mentioned in the specifier (e.g. <3.1 should 470 # not match 3.1.dev0, but should match 3.0.dev0). 471 if not spec.is_prerelease and prospective.is_prerelease: 472 if Version(prospective.base_version) == Version(spec.base_version): 473 return False 474 475 # If we've gotten to here, it means that prospective version is both 476 # less than the spec version *and* it's not a pre-release of the same 477 # version in the spec. 478 return True 479 480 def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: 481 482 # Convert our spec to a Version instance, since we'll want to work with 483 # it as a version. 484 spec = Version(spec_str) 485 486 # Check to see if the prospective version is greater than the spec 487 # version. If it's not we can short circuit and just return False now 488 # instead of doing extra unneeded work. 489 if not prospective > spec: 490 return False 491 492 # This special case is here so that, unless the specifier itself 493 # includes is a post-release version, that we do not accept 494 # post-release versions for the version mentioned in the specifier 495 # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). 496 if not spec.is_postrelease and prospective.is_postrelease: 497 if Version(prospective.base_version) == Version(spec.base_version): 498 return False 499 500 # Ensure that we do not allow a local version of the version mentioned 501 # in the specifier, which is technically greater than, to match. 502 if prospective.local is not None: 503 if Version(prospective.base_version) == Version(spec.base_version): 504 return False 505 506 # If we've gotten to here, it means that prospective version is both 507 # greater than the spec version *and* it's not a pre-release of the 508 # same version in the spec. 509 return True 510 511 def _compare_arbitrary(self, prospective: Version, spec: str) -> bool: 512 return str(prospective).lower() == str(spec).lower() 513 514 def __contains__(self, item: Union[str, Version]) -> bool: 515 """Return whether or not the item is contained in this specifier. 516 517 :param item: The item to check for. 518 519 This is used for the ``in`` operator and behaves the same as 520 :meth:`contains` with no ``prereleases`` argument passed. 521 522 >>> "1.2.3" in Specifier(">=1.2.3") 523 True 524 >>> Version("1.2.3") in Specifier(">=1.2.3") 525 True 526 >>> "1.0.0" in Specifier(">=1.2.3") 527 False 528 >>> "1.3.0a1" in Specifier(">=1.2.3") 529 False 530 >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) 531 True 532 """ 533 return self.contains(item) 534 535 def contains( 536 self, item: UnparsedVersion, prereleases: Optional[bool] = None 537 ) -> bool: 538 """Return whether or not the item is contained in this specifier. 539 540 :param item: 541 The item to check for, which can be a version string or a 542 :class:`Version` instance. 543 :param prereleases: 544 Whether or not to match prereleases with this Specifier. If set to 545 ``None`` (the default), it uses :attr:`prereleases` to determine 546 whether or not prereleases are allowed. 547 548 >>> Specifier(">=1.2.3").contains("1.2.3") 549 True 550 >>> Specifier(">=1.2.3").contains(Version("1.2.3")) 551 True 552 >>> Specifier(">=1.2.3").contains("1.0.0") 553 False 554 >>> Specifier(">=1.2.3").contains("1.3.0a1") 555 False 556 >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") 557 True 558 >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) 559 True 560 """ 561 562 # Determine if prereleases are to be allowed or not. 563 if prereleases is None: 564 prereleases = self.prereleases 565 566 # Normalize item to a Version, this allows us to have a shortcut for 567 # "2.0" in Specifier(">=2") 568 normalized_item = _coerce_version(item) 569 570 # Determine if we should be supporting prereleases in this specifier 571 # or not, if we do not support prereleases than we can short circuit 572 # logic if this version is a prereleases. 573 if normalized_item.is_prerelease and not prereleases: 574 return False 575 576 # Actually do the comparison to determine if this item is contained 577 # within this Specifier or not. 578 operator_callable: CallableOperator = self._get_operator(self.operator) 579 return operator_callable(normalized_item, self.version) 580 581 def filter( 582 self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None 583 ) -> Iterator[UnparsedVersionVar]: 584 """Filter items in the given iterable, that match the specifier. 585 586 :param iterable: 587 An iterable that can contain version strings and :class:`Version` instances. 588 The items in the iterable will be filtered according to the specifier. 589 :param prereleases: 590 Whether or not to allow prereleases in the returned iterator. If set to 591 ``None`` (the default), it will be intelligently decide whether to allow 592 prereleases or not (based on the :attr:`prereleases` attribute, and 593 whether the only versions matching are prereleases). 594 595 This method is smarter than just ``filter(Specifier().contains, [...])`` 596 because it implements the rule from :pep:`440` that a prerelease item 597 SHOULD be accepted if no other versions match the given specifier. 598 599 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) 600 ['1.3'] 601 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) 602 ['1.2.3', '1.3', <Version('1.4')>] 603 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) 604 ['1.5a1'] 605 >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) 606 ['1.3', '1.5a1'] 607 >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) 608 ['1.3', '1.5a1'] 609 """ 610 611 yielded = False 612 found_prereleases = [] 613 614 kw = {"prereleases": prereleases if prereleases is not None else True} 615 616 # Attempt to iterate over all the values in the iterable and if any of 617 # them match, yield them. 618 for version in iterable: 619 parsed_version = _coerce_version(version) 620 621 if self.contains(parsed_version, **kw): 622 # If our version is a prerelease, and we were not set to allow 623 # prereleases, then we'll store it for later in case nothing 624 # else matches this specifier. 625 if parsed_version.is_prerelease and not ( 626 prereleases or self.prereleases 627 ): 628 found_prereleases.append(version) 629 # Either this is not a prerelease, or we should have been 630 # accepting prereleases from the beginning. 631 else: 632 yielded = True 633 yield version 634 635 # Now that we've iterated over everything, determine if we've yielded 636 # any values, and if we have not and we have any prereleases stored up 637 # then we will go ahead and yield the prereleases. 638 if not yielded and found_prereleases: 639 for version in found_prereleases: 640 yield version 641 642 643_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$") 644 645 646def _version_split(version: str) -> List[str]: 647 """Split version into components. 648 649 The split components are intended for version comparison. The logic does 650 not attempt to retain the original version string, so joining the 651 components back with :func:`_version_join` may not produce the original 652 version string. 653 """ 654 result: List[str] = [] 655 656 epoch, _, rest = version.rpartition("!") 657 result.append(epoch or "0") 658 659 for item in rest.split("."): 660 match = _prefix_regex.search(item) 661 if match: 662 result.extend(match.groups()) 663 else: 664 result.append(item) 665 return result 666 667 668def _version_join(components: List[str]) -> str: 669 """Join split version components into a version string. 670 671 This function assumes the input came from :func:`_version_split`, where the 672 first component must be the epoch (either empty or numeric), and all other 673 components numeric. 674 """ 675 epoch, *rest = components 676 return f"{epoch}!{'.'.join(rest)}" 677 678 679def _is_not_suffix(segment: str) -> bool: 680 return not any( 681 segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") 682 ) 683 684 685def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: 686 left_split, right_split = [], [] 687 688 # Get the release segment of our versions 689 left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) 690 right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) 691 692 # Get the rest of our versions 693 left_split.append(left[len(left_split[0]) :]) 694 right_split.append(right[len(right_split[0]) :]) 695 696 # Insert our padding 697 left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) 698 right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) 699 700 return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) 701 702 703class SpecifierSet(BaseSpecifier): 704 """This class abstracts handling of a set of version specifiers. 705 706 It can be passed a single specifier (``>=3.0``), a comma-separated list of 707 specifiers (``>=3.0,!=3.1``), or no specifier at all. 708 """ 709 710 def __init__( 711 self, specifiers: str = "", prereleases: Optional[bool] = None 712 ) -> None: 713 """Initialize a SpecifierSet instance. 714 715 :param specifiers: 716 The string representation of a specifier or a comma-separated list of 717 specifiers which will be parsed and normalized before use. 718 :param prereleases: 719 This tells the SpecifierSet if it should accept prerelease versions if 720 applicable or not. The default of ``None`` will autodetect it from the 721 given specifiers. 722 723 :raises InvalidSpecifier: 724 If the given ``specifiers`` are not parseable than this exception will be 725 raised. 726 """ 727 728 # Split on `,` to break each individual specifier into it's own item, and 729 # strip each item to remove leading/trailing whitespace. 730 split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] 731 732 # Parsed each individual specifier, attempting first to make it a 733 # Specifier. 734 parsed: Set[Specifier] = set() 735 for specifier in split_specifiers: 736 parsed.add(Specifier(specifier)) 737 738 # Turn our parsed specifiers into a frozen set and save them for later. 739 self._specs = frozenset(parsed) 740 741 # Store our prereleases value so we can use it later to determine if 742 # we accept prereleases or not. 743 self._prereleases = prereleases 744 745 @property 746 def prereleases(self) -> Optional[bool]: 747 # If we have been given an explicit prerelease modifier, then we'll 748 # pass that through here. 749 if self._prereleases is not None: 750 return self._prereleases 751 752 # If we don't have any specifiers, and we don't have a forced value, 753 # then we'll just return None since we don't know if this should have 754 # pre-releases or not. 755 if not self._specs: 756 return None 757 758 # Otherwise we'll see if any of the given specifiers accept 759 # prereleases, if any of them do we'll return True, otherwise False. 760 return any(s.prereleases for s in self._specs) 761 762 @prereleases.setter 763 def prereleases(self, value: bool) -> None: 764 self._prereleases = value 765 766 def __repr__(self) -> str: 767 """A representation of the specifier set that shows all internal state. 768 769 Note that the ordering of the individual specifiers within the set may not 770 match the input string. 771 772 >>> SpecifierSet('>=1.0.0,!=2.0.0') 773 <SpecifierSet('!=2.0.0,>=1.0.0')> 774 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) 775 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)> 776 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) 777 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)> 778 """ 779 pre = ( 780 f", prereleases={self.prereleases!r}" 781 if self._prereleases is not None 782 else "" 783 ) 784 785 return f"<SpecifierSet({str(self)!r}{pre})>" 786 787 def __str__(self) -> str: 788 """A string representation of the specifier set that can be round-tripped. 789 790 Note that the ordering of the individual specifiers within the set may not 791 match the input string. 792 793 >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) 794 '!=1.0.1,>=1.0.0' 795 >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) 796 '!=1.0.1,>=1.0.0' 797 """ 798 return ",".join(sorted(str(s) for s in self._specs)) 799 800 def __hash__(self) -> int: 801 return hash(self._specs) 802 803 def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": 804 """Return a SpecifierSet which is a combination of the two sets. 805 806 :param other: The other object to combine with. 807 808 >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' 809 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')> 810 >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') 811 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')> 812 """ 813 if isinstance(other, str): 814 other = SpecifierSet(other) 815 elif not isinstance(other, SpecifierSet): 816 return NotImplemented 817 818 specifier = SpecifierSet() 819 specifier._specs = frozenset(self._specs | other._specs) 820 821 if self._prereleases is None and other._prereleases is not None: 822 specifier._prereleases = other._prereleases 823 elif self._prereleases is not None and other._prereleases is None: 824 specifier._prereleases = self._prereleases 825 elif self._prereleases == other._prereleases: 826 specifier._prereleases = self._prereleases 827 else: 828 raise ValueError( 829 "Cannot combine SpecifierSets with True and False prerelease " 830 "overrides." 831 ) 832 833 return specifier 834 835 def __eq__(self, other: object) -> bool: 836 """Whether or not the two SpecifierSet-like objects are equal. 837 838 :param other: The other object to check against. 839 840 The value of :attr:`prereleases` is ignored. 841 842 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") 843 True 844 >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == 845 ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) 846 True 847 >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" 848 True 849 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") 850 False 851 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") 852 False 853 """ 854 if isinstance(other, (str, Specifier)): 855 other = SpecifierSet(str(other)) 856 elif not isinstance(other, SpecifierSet): 857 return NotImplemented 858 859 return self._specs == other._specs 860 861 def __len__(self) -> int: 862 """Returns the number of specifiers in this specifier set.""" 863 return len(self._specs) 864 865 def __iter__(self) -> Iterator[Specifier]: 866 """ 867 Returns an iterator over all the underlying :class:`Specifier` instances 868 in this specifier set. 869 870 >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) 871 [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>] 872 """ 873 return iter(self._specs) 874 875 def __contains__(self, item: UnparsedVersion) -> bool: 876 """Return whether or not the item is contained in this specifier. 877 878 :param item: The item to check for. 879 880 This is used for the ``in`` operator and behaves the same as 881 :meth:`contains` with no ``prereleases`` argument passed. 882 883 >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") 884 True 885 >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") 886 True 887 >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") 888 False 889 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") 890 False 891 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) 892 True 893 """ 894 return self.contains(item) 895 896 def contains( 897 self, 898 item: UnparsedVersion, 899 prereleases: Optional[bool] = None, 900 installed: Optional[bool] = None, 901 ) -> bool: 902 """Return whether or not the item is contained in this SpecifierSet. 903 904 :param item: 905 The item to check for, which can be a version string or a 906 :class:`Version` instance. 907 :param prereleases: 908 Whether or not to match prereleases with this SpecifierSet. If set to 909 ``None`` (the default), it uses :attr:`prereleases` to determine 910 whether or not prereleases are allowed. 911 912 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") 913 True 914 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) 915 True 916 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") 917 False 918 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") 919 False 920 >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1") 921 True 922 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) 923 True 924 """ 925 # Ensure that our item is a Version instance. 926 if not isinstance(item, Version): 927 item = Version(item) 928 929 # Determine if we're forcing a prerelease or not, if we're not forcing 930 # one for this particular filter call, then we'll use whatever the 931 # SpecifierSet thinks for whether or not we should support prereleases. 932 if prereleases is None: 933 prereleases = self.prereleases 934 935 # We can determine if we're going to allow pre-releases by looking to 936 # see if any of the underlying items supports them. If none of them do 937 # and this item is a pre-release then we do not allow it and we can 938 # short circuit that here. 939 # Note: This means that 1.0.dev1 would not be contained in something 940 # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 941 if not prereleases and item.is_prerelease: 942 return False 943 944 if installed and item.is_prerelease: 945 item = Version(item.base_version) 946 947 # We simply dispatch to the underlying specs here to make sure that the 948 # given version is contained within all of them. 949 # Note: This use of all() here means that an empty set of specifiers 950 # will always return True, this is an explicit design decision. 951 return all(s.contains(item, prereleases=prereleases) for s in self._specs) 952 953 def filter( 954 self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None 955 ) -> Iterator[UnparsedVersionVar]: 956 """Filter items in the given iterable, that match the specifiers in this set. 957 958 :param iterable: 959 An iterable that can contain version strings and :class:`Version` instances. 960 The items in the iterable will be filtered according to the specifier. 961 :param prereleases: 962 Whether or not to allow prereleases in the returned iterator. If set to 963 ``None`` (the default), it will be intelligently decide whether to allow 964 prereleases or not (based on the :attr:`prereleases` attribute, and 965 whether the only versions matching are prereleases). 966 967 This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` 968 because it implements the rule from :pep:`440` that a prerelease item 969 SHOULD be accepted if no other versions match the given specifier. 970 971 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) 972 ['1.3'] 973 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) 974 ['1.3', <Version('1.4')>] 975 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) 976 [] 977 >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) 978 ['1.3', '1.5a1'] 979 >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) 980 ['1.3', '1.5a1'] 981 982 An "empty" SpecifierSet will filter items based on the presence of prerelease 983 versions in the set. 984 985 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) 986 ['1.3'] 987 >>> list(SpecifierSet("").filter(["1.5a1"])) 988 ['1.5a1'] 989 >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) 990 ['1.3', '1.5a1'] 991 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) 992 ['1.3', '1.5a1'] 993 """ 994 # Determine if we're forcing a prerelease or not, if we're not forcing 995 # one for this particular filter call, then we'll use whatever the 996 # SpecifierSet thinks for whether or not we should support prereleases. 997 if prereleases is None: 998 prereleases = self.prereleases 999 1000 # If we have any specifiers, then we want to wrap our iterable in the 1001 # filter method for each one, this will act as a logical AND amongst 1002 # each specifier. 1003 if self._specs: 1004 for spec in self._specs: 1005 iterable = spec.filter(iterable, prereleases=bool(prereleases)) 1006 return iter(iterable) 1007 # If we do not have any specifiers, then we need to have a rough filter 1008 # which will filter out any pre-releases, unless there are no final 1009 # releases. 1010 else: 1011 filtered: List[UnparsedVersionVar] = [] 1012 found_prereleases: List[UnparsedVersionVar] = [] 1013 1014 for item in iterable: 1015 parsed_version = _coerce_version(item) 1016 1017 # Store any item which is a pre-release for later unless we've 1018 # already found a final version or we are accepting prereleases 1019 if parsed_version.is_prerelease and not prereleases: 1020 if not filtered: 1021 found_prereleases.append(item) 1022 else: 1023 filtered.append(item) 1024 1025 # If we've found no items except for pre-releases, then we'll go 1026 # ahead and use the pre-releases 1027 if not filtered and found_prereleases and prereleases is None: 1028 return iter(found_prereleases) 1029 1030 return iter(filtered) 1031