specifiers.py 31 KB

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