You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

864 lines
31KB

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