Development of an internal social media platform with personalised dashboards for students
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.

specifiers.py 27KB

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