Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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 38KB

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