specifiers.py 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005
  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. @property
  220. def prereleases(self) -> bool:
  221. # If there is an explicit prereleases set for this, then we'll just
  222. # blindly use that.
  223. if self._prereleases is not None:
  224. return self._prereleases
  225. # Look at all of our specifiers and determine if they are inclusive
  226. # operators, and if they are if they are including an explicit
  227. # prerelease.
  228. operator, version = self._spec
  229. if operator in ["==", ">=", "<=", "~=", "==="]:
  230. # The == specifier can include a trailing .*, if it does we
  231. # want to remove before parsing.
  232. if operator == "==" and version.endswith(".*"):
  233. version = version[:-2]
  234. # Parse the version, and if it is a pre-release than this
  235. # specifier allows pre-releases.
  236. if Version(version).is_prerelease:
  237. return True
  238. return False
  239. @prereleases.setter
  240. def prereleases(self, value: bool) -> None:
  241. self._prereleases = value
  242. @property
  243. def operator(self) -> str:
  244. """The operator of this specifier.
  245. >>> Specifier("==1.2.3").operator
  246. '=='
  247. """
  248. return self._spec[0]
  249. @property
  250. def version(self) -> str:
  251. """The version of this specifier.
  252. >>> Specifier("==1.2.3").version
  253. '1.2.3'
  254. """
  255. return self._spec[1]
  256. def __repr__(self) -> str:
  257. """A representation of the Specifier that shows all internal state.
  258. >>> Specifier('>=1.0.0')
  259. <Specifier('>=1.0.0')>
  260. >>> Specifier('>=1.0.0', prereleases=False)
  261. <Specifier('>=1.0.0', prereleases=False)>
  262. >>> Specifier('>=1.0.0', prereleases=True)
  263. <Specifier('>=1.0.0', prereleases=True)>
  264. """
  265. pre = (
  266. f", prereleases={self.prereleases!r}"
  267. if self._prereleases is not None
  268. else ""
  269. )
  270. return f"<{self.__class__.__name__}({str(self)!r}{pre})>"
  271. def __str__(self) -> str:
  272. """A string representation of the Specifier that can be round-tripped.
  273. >>> str(Specifier('>=1.0.0'))
  274. '>=1.0.0'
  275. >>> str(Specifier('>=1.0.0', prereleases=False))
  276. '>=1.0.0'
  277. """
  278. return "{}{}".format(*self._spec)
  279. @property
  280. def _canonical_spec(self) -> Tuple[str, str]:
  281. canonical_version = canonicalize_version(
  282. self._spec[1],
  283. strip_trailing_zero=(self._spec[0] != "~="),
  284. )
  285. return self._spec[0], canonical_version
  286. def __hash__(self) -> int:
  287. return hash(self._canonical_spec)
  288. def __eq__(self, other: object) -> bool:
  289. """Whether or not the two Specifier-like objects are equal.
  290. :param other: The other object to check against.
  291. The value of :attr:`prereleases` is ignored.
  292. >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0")
  293. True
  294. >>> (Specifier("==1.2.3", prereleases=False) ==
  295. ... Specifier("==1.2.3", prereleases=True))
  296. True
  297. >>> Specifier("==1.2.3") == "==1.2.3"
  298. True
  299. >>> Specifier("==1.2.3") == Specifier("==1.2.4")
  300. False
  301. >>> Specifier("==1.2.3") == Specifier("~=1.2.3")
  302. False
  303. """
  304. if isinstance(other, str):
  305. try:
  306. other = self.__class__(str(other))
  307. except InvalidSpecifier:
  308. return NotImplemented
  309. elif not isinstance(other, self.__class__):
  310. return NotImplemented
  311. return self._canonical_spec == other._canonical_spec
  312. def _get_operator(self, op: str) -> CallableOperator:
  313. operator_callable: CallableOperator = getattr(
  314. self, f"_compare_{self._operators[op]}"
  315. )
  316. return operator_callable
  317. def _compare_compatible(self, prospective: Version, spec: str) -> bool:
  318. # Compatible releases have an equivalent combination of >= and ==. That
  319. # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
  320. # implement this in terms of the other specifiers instead of
  321. # implementing it ourselves. The only thing we need to do is construct
  322. # the other specifiers.
  323. # We want everything but the last item in the version, but we want to
  324. # ignore suffix segments.
  325. prefix = ".".join(
  326. list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
  327. )
  328. # Add the prefix notation to the end of our string
  329. prefix += ".*"
  330. return self._get_operator(">=")(prospective, spec) and self._get_operator("==")(
  331. prospective, prefix
  332. )
  333. def _compare_equal(self, prospective: Version, spec: str) -> bool:
  334. # We need special logic to handle prefix matching
  335. if spec.endswith(".*"):
  336. # In the case of prefix matching we want to ignore local segment.
  337. normalized_prospective = canonicalize_version(prospective.public)
  338. # Get the normalized version string ignoring the trailing .*
  339. normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False)
  340. # Split the spec out by dots, and pretend that there is an implicit
  341. # dot in between a release segment and a pre-release segment.
  342. split_spec = _version_split(normalized_spec)
  343. # Split the prospective version out by dots, and pretend that there
  344. # is an implicit dot in between a release segment and a pre-release
  345. # segment.
  346. split_prospective = _version_split(normalized_prospective)
  347. # 0-pad the prospective version before shortening it to get the correct
  348. # shortened version.
  349. padded_prospective, _ = _pad_version(split_prospective, split_spec)
  350. # Shorten the prospective version to be the same length as the spec
  351. # so that we can determine if the specifier is a prefix of the
  352. # prospective version or not.
  353. shortened_prospective = padded_prospective[: len(split_spec)]
  354. return shortened_prospective == split_spec
  355. else:
  356. # Convert our spec string into a Version
  357. spec_version = Version(spec)
  358. # If the specifier does not have a local segment, then we want to
  359. # act as if the prospective version also does not have a local
  360. # segment.
  361. if not spec_version.local:
  362. prospective = Version(prospective.public)
  363. return prospective == spec_version
  364. def _compare_not_equal(self, prospective: Version, spec: str) -> bool:
  365. return not self._compare_equal(prospective, spec)
  366. def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool:
  367. # NB: Local version identifiers are NOT permitted in the version
  368. # specifier, so local version labels can be universally removed from
  369. # the prospective version.
  370. return Version(prospective.public) <= Version(spec)
  371. def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool:
  372. # NB: Local version identifiers are NOT permitted in the version
  373. # specifier, so local version labels can be universally removed from
  374. # the prospective version.
  375. return Version(prospective.public) >= Version(spec)
  376. def _compare_less_than(self, prospective: Version, spec_str: str) -> bool:
  377. # Convert our spec to a Version instance, since we'll want to work with
  378. # it as a version.
  379. spec = Version(spec_str)
  380. # Check to see if the prospective version is less than the spec
  381. # version. If it's not we can short circuit and just return False now
  382. # instead of doing extra unneeded work.
  383. if not prospective < spec:
  384. return False
  385. # This special case is here so that, unless the specifier itself
  386. # includes is a pre-release version, that we do not accept pre-release
  387. # versions for the version mentioned in the specifier (e.g. <3.1 should
  388. # not match 3.1.dev0, but should match 3.0.dev0).
  389. if not spec.is_prerelease and prospective.is_prerelease:
  390. if Version(prospective.base_version) == Version(spec.base_version):
  391. return False
  392. # If we've gotten to here, it means that prospective version is both
  393. # less than the spec version *and* it's not a pre-release of the same
  394. # version in the spec.
  395. return True
  396. def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool:
  397. # Convert our spec to a Version instance, since we'll want to work with
  398. # it as a version.
  399. spec = Version(spec_str)
  400. # Check to see if the prospective version is greater than the spec
  401. # version. If it's not we can short circuit and just return False now
  402. # instead of doing extra unneeded work.
  403. if not prospective > spec:
  404. return False
  405. # This special case is here so that, unless the specifier itself
  406. # includes is a post-release version, that we do not accept
  407. # post-release versions for the version mentioned in the specifier
  408. # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0).
  409. if not spec.is_postrelease and prospective.is_postrelease:
  410. if Version(prospective.base_version) == Version(spec.base_version):
  411. return False
  412. # Ensure that we do not allow a local version of the version mentioned
  413. # in the specifier, which is technically greater than, to match.
  414. if prospective.local is not None:
  415. if Version(prospective.base_version) == Version(spec.base_version):
  416. return False
  417. # If we've gotten to here, it means that prospective version is both
  418. # greater than the spec version *and* it's not a pre-release of the
  419. # same version in the spec.
  420. return True
  421. def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
  422. return str(prospective).lower() == str(spec).lower()
  423. def __contains__(self, item: Union[str, Version]) -> bool:
  424. """Return whether or not the item is contained in this specifier.
  425. :param item: The item to check for.
  426. This is used for the ``in`` operator and behaves the same as
  427. :meth:`contains` with no ``prereleases`` argument passed.
  428. >>> "1.2.3" in Specifier(">=1.2.3")
  429. True
  430. >>> Version("1.2.3") in Specifier(">=1.2.3")
  431. True
  432. >>> "1.0.0" in Specifier(">=1.2.3")
  433. False
  434. >>> "1.3.0a1" in Specifier(">=1.2.3")
  435. False
  436. >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True)
  437. True
  438. """
  439. return self.contains(item)
  440. def contains(
  441. self, item: UnparsedVersion, prereleases: Optional[bool] = None
  442. ) -> bool:
  443. """Return whether or not the item is contained in this specifier.
  444. :param item:
  445. The item to check for, which can be a version string or a
  446. :class:`Version` instance.
  447. :param prereleases:
  448. Whether or not to match prereleases with this Specifier. If set to
  449. ``None`` (the default), it uses :attr:`prereleases` to determine
  450. whether or not prereleases are allowed.
  451. >>> Specifier(">=1.2.3").contains("1.2.3")
  452. True
  453. >>> Specifier(">=1.2.3").contains(Version("1.2.3"))
  454. True
  455. >>> Specifier(">=1.2.3").contains("1.0.0")
  456. False
  457. >>> Specifier(">=1.2.3").contains("1.3.0a1")
  458. False
  459. >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1")
  460. True
  461. >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True)
  462. True
  463. """
  464. # Determine if prereleases are to be allowed or not.
  465. if prereleases is None:
  466. prereleases = self.prereleases
  467. # Normalize item to a Version, this allows us to have a shortcut for
  468. # "2.0" in Specifier(">=2")
  469. normalized_item = _coerce_version(item)
  470. # Determine if we should be supporting prereleases in this specifier
  471. # or not, if we do not support prereleases than we can short circuit
  472. # logic if this version is a prereleases.
  473. if normalized_item.is_prerelease and not prereleases:
  474. return False
  475. # Actually do the comparison to determine if this item is contained
  476. # within this Specifier or not.
  477. operator_callable: CallableOperator = self._get_operator(self.operator)
  478. return operator_callable(normalized_item, self.version)
  479. def filter(
  480. self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
  481. ) -> Iterator[UnparsedVersionVar]:
  482. """Filter items in the given iterable, that match the specifier.
  483. :param iterable:
  484. An iterable that can contain version strings and :class:`Version` instances.
  485. The items in the iterable will be filtered according to the specifier.
  486. :param prereleases:
  487. Whether or not to allow prereleases in the returned iterator. If set to
  488. ``None`` (the default), it will be intelligently decide whether to allow
  489. prereleases or not (based on the :attr:`prereleases` attribute, and
  490. whether the only versions matching are prereleases).
  491. This method is smarter than just ``filter(Specifier().contains, [...])``
  492. because it implements the rule from :pep:`440` that a prerelease item
  493. SHOULD be accepted if no other versions match the given specifier.
  494. >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
  495. ['1.3']
  496. >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")]))
  497. ['1.2.3', '1.3', <Version('1.4')>]
  498. >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"]))
  499. ['1.5a1']
  500. >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
  501. ['1.3', '1.5a1']
  502. >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
  503. ['1.3', '1.5a1']
  504. """
  505. yielded = False
  506. found_prereleases = []
  507. kw = {"prereleases": prereleases if prereleases is not None else True}
  508. # Attempt to iterate over all the values in the iterable and if any of
  509. # them match, yield them.
  510. for version in iterable:
  511. parsed_version = _coerce_version(version)
  512. if self.contains(parsed_version, **kw):
  513. # If our version is a prerelease, and we were not set to allow
  514. # prereleases, then we'll store it for later in case nothing
  515. # else matches this specifier.
  516. if parsed_version.is_prerelease and not (
  517. prereleases or self.prereleases
  518. ):
  519. found_prereleases.append(version)
  520. # Either this is not a prerelease, or we should have been
  521. # accepting prereleases from the beginning.
  522. else:
  523. yielded = True
  524. yield version
  525. # Now that we've iterated over everything, determine if we've yielded
  526. # any values, and if we have not and we have any prereleases stored up
  527. # then we will go ahead and yield the prereleases.
  528. if not yielded and found_prereleases:
  529. for version in found_prereleases:
  530. yield version
  531. _prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
  532. def _version_split(version: str) -> List[str]:
  533. result: List[str] = []
  534. for item in version.split("."):
  535. match = _prefix_regex.search(item)
  536. if match:
  537. result.extend(match.groups())
  538. else:
  539. result.append(item)
  540. return result
  541. def _is_not_suffix(segment: str) -> bool:
  542. return not any(
  543. segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post")
  544. )
  545. def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]:
  546. left_split, right_split = [], []
  547. # Get the release segment of our versions
  548. left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left)))
  549. right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right)))
  550. # Get the rest of our versions
  551. left_split.append(left[len(left_split[0]) :])
  552. right_split.append(right[len(right_split[0]) :])
  553. # Insert our padding
  554. left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0])))
  555. right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0])))
  556. return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split)))
  557. class SpecifierSet(BaseSpecifier):
  558. """This class abstracts handling of a set of version specifiers.
  559. It can be passed a single specifier (``>=3.0``), a comma-separated list of
  560. specifiers (``>=3.0,!=3.1``), or no specifier at all.
  561. """
  562. def __init__(
  563. self, specifiers: str = "", prereleases: Optional[bool] = None
  564. ) -> None:
  565. """Initialize a SpecifierSet instance.
  566. :param specifiers:
  567. The string representation of a specifier or a comma-separated list of
  568. specifiers which will be parsed and normalized before use.
  569. :param prereleases:
  570. This tells the SpecifierSet if it should accept prerelease versions if
  571. applicable or not. The default of ``None`` will autodetect it from the
  572. given specifiers.
  573. :raises InvalidSpecifier:
  574. If the given ``specifiers`` are not parseable than this exception will be
  575. raised.
  576. """
  577. # Split on `,` to break each individual specifier into it's own item, and
  578. # strip each item to remove leading/trailing whitespace.
  579. split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
  580. # Parsed each individual specifier, attempting first to make it a
  581. # Specifier.
  582. parsed: Set[Specifier] = set()
  583. for specifier in split_specifiers:
  584. parsed.add(Specifier(specifier))
  585. # Turn our parsed specifiers into a frozen set and save them for later.
  586. self._specs = frozenset(parsed)
  587. # Store our prereleases value so we can use it later to determine if
  588. # we accept prereleases or not.
  589. self._prereleases = prereleases
  590. @property
  591. def prereleases(self) -> Optional[bool]:
  592. # If we have been given an explicit prerelease modifier, then we'll
  593. # pass that through here.
  594. if self._prereleases is not None:
  595. return self._prereleases
  596. # If we don't have any specifiers, and we don't have a forced value,
  597. # then we'll just return None since we don't know if this should have
  598. # pre-releases or not.
  599. if not self._specs:
  600. return None
  601. # Otherwise we'll see if any of the given specifiers accept
  602. # prereleases, if any of them do we'll return True, otherwise False.
  603. return any(s.prereleases for s in self._specs)
  604. @prereleases.setter
  605. def prereleases(self, value: bool) -> None:
  606. self._prereleases = value
  607. def __repr__(self) -> str:
  608. """A representation of the specifier set that shows all internal state.
  609. Note that the ordering of the individual specifiers within the set may not
  610. match the input string.
  611. >>> SpecifierSet('>=1.0.0,!=2.0.0')
  612. <SpecifierSet('!=2.0.0,>=1.0.0')>
  613. >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False)
  614. <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)>
  615. >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True)
  616. <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)>
  617. """
  618. pre = (
  619. f", prereleases={self.prereleases!r}"
  620. if self._prereleases is not None
  621. else ""
  622. )
  623. return f"<SpecifierSet({str(self)!r}{pre})>"
  624. def __str__(self) -> str:
  625. """A string representation of the specifier set that can be round-tripped.
  626. Note that the ordering of the individual specifiers within the set may not
  627. match the input string.
  628. >>> str(SpecifierSet(">=1.0.0,!=1.0.1"))
  629. '!=1.0.1,>=1.0.0'
  630. >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False))
  631. '!=1.0.1,>=1.0.0'
  632. """
  633. return ",".join(sorted(str(s) for s in self._specs))
  634. def __hash__(self) -> int:
  635. return hash(self._specs)
  636. def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":
  637. """Return a SpecifierSet which is a combination of the two sets.
  638. :param other: The other object to combine with.
  639. >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1'
  640. <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
  641. >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1')
  642. <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')>
  643. """
  644. if isinstance(other, str):
  645. other = SpecifierSet(other)
  646. elif not isinstance(other, SpecifierSet):
  647. return NotImplemented
  648. specifier = SpecifierSet()
  649. specifier._specs = frozenset(self._specs | other._specs)
  650. if self._prereleases is None and other._prereleases is not None:
  651. specifier._prereleases = other._prereleases
  652. elif self._prereleases is not None and other._prereleases is None:
  653. specifier._prereleases = self._prereleases
  654. elif self._prereleases == other._prereleases:
  655. specifier._prereleases = self._prereleases
  656. else:
  657. raise ValueError(
  658. "Cannot combine SpecifierSets with True and False prerelease "
  659. "overrides."
  660. )
  661. return specifier
  662. def __eq__(self, other: object) -> bool:
  663. """Whether or not the two SpecifierSet-like objects are equal.
  664. :param other: The other object to check against.
  665. The value of :attr:`prereleases` is ignored.
  666. >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1")
  667. True
  668. >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) ==
  669. ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True))
  670. True
  671. >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1"
  672. True
  673. >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0")
  674. False
  675. >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2")
  676. False
  677. """
  678. if isinstance(other, (str, Specifier)):
  679. other = SpecifierSet(str(other))
  680. elif not isinstance(other, SpecifierSet):
  681. return NotImplemented
  682. return self._specs == other._specs
  683. def __len__(self) -> int:
  684. """Returns the number of specifiers in this specifier set."""
  685. return len(self._specs)
  686. def __iter__(self) -> Iterator[Specifier]:
  687. """
  688. Returns an iterator over all the underlying :class:`Specifier` instances
  689. in this specifier set.
  690. >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str)
  691. [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>]
  692. """
  693. return iter(self._specs)
  694. def __contains__(self, item: UnparsedVersion) -> bool:
  695. """Return whether or not the item is contained in this specifier.
  696. :param item: The item to check for.
  697. This is used for the ``in`` operator and behaves the same as
  698. :meth:`contains` with no ``prereleases`` argument passed.
  699. >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1")
  700. True
  701. >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1")
  702. True
  703. >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1")
  704. False
  705. >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1")
  706. False
  707. >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)
  708. True
  709. """
  710. return self.contains(item)
  711. def contains(
  712. self,
  713. item: UnparsedVersion,
  714. prereleases: Optional[bool] = None,
  715. installed: Optional[bool] = None,
  716. ) -> bool:
  717. """Return whether or not the item is contained in this SpecifierSet.
  718. :param item:
  719. The item to check for, which can be a version string or a
  720. :class:`Version` instance.
  721. :param prereleases:
  722. Whether or not to match prereleases with this SpecifierSet. If set to
  723. ``None`` (the default), it uses :attr:`prereleases` to determine
  724. whether or not prereleases are allowed.
  725. >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3")
  726. True
  727. >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3"))
  728. True
  729. >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1")
  730. False
  731. >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1")
  732. False
  733. >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True).contains("1.3.0a1")
  734. True
  735. >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True)
  736. True
  737. """
  738. # Ensure that our item is a Version instance.
  739. if not isinstance(item, Version):
  740. item = Version(item)
  741. # Determine if we're forcing a prerelease or not, if we're not forcing
  742. # one for this particular filter call, then we'll use whatever the
  743. # SpecifierSet thinks for whether or not we should support prereleases.
  744. if prereleases is None:
  745. prereleases = self.prereleases
  746. # We can determine if we're going to allow pre-releases by looking to
  747. # see if any of the underlying items supports them. If none of them do
  748. # and this item is a pre-release then we do not allow it and we can
  749. # short circuit that here.
  750. # Note: This means that 1.0.dev1 would not be contained in something
  751. # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0
  752. if not prereleases and item.is_prerelease:
  753. return False
  754. if installed and item.is_prerelease:
  755. item = Version(item.base_version)
  756. # We simply dispatch to the underlying specs here to make sure that the
  757. # given version is contained within all of them.
  758. # Note: This use of all() here means that an empty set of specifiers
  759. # will always return True, this is an explicit design decision.
  760. return all(s.contains(item, prereleases=prereleases) for s in self._specs)
  761. def filter(
  762. self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None
  763. ) -> Iterator[UnparsedVersionVar]:
  764. """Filter items in the given iterable, that match the specifiers in this set.
  765. :param iterable:
  766. An iterable that can contain version strings and :class:`Version` instances.
  767. The items in the iterable will be filtered according to the specifier.
  768. :param prereleases:
  769. Whether or not to allow prereleases in the returned iterator. If set to
  770. ``None`` (the default), it will be intelligently decide whether to allow
  771. prereleases or not (based on the :attr:`prereleases` attribute, and
  772. whether the only versions matching are prereleases).
  773. This method is smarter than just ``filter(SpecifierSet(...).contains, [...])``
  774. because it implements the rule from :pep:`440` that a prerelease item
  775. SHOULD be accepted if no other versions match the given specifier.
  776. >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"]))
  777. ['1.3']
  778. >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")]))
  779. ['1.3', <Version('1.4')>]
  780. >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"]))
  781. []
  782. >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True))
  783. ['1.3', '1.5a1']
  784. >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"]))
  785. ['1.3', '1.5a1']
  786. An "empty" SpecifierSet will filter items based on the presence of prerelease
  787. versions in the set.
  788. >>> list(SpecifierSet("").filter(["1.3", "1.5a1"]))
  789. ['1.3']
  790. >>> list(SpecifierSet("").filter(["1.5a1"]))
  791. ['1.5a1']
  792. >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"]))
  793. ['1.3', '1.5a1']
  794. >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True))
  795. ['1.3', '1.5a1']
  796. """
  797. # Determine if we're forcing a prerelease or not, if we're not forcing
  798. # one for this particular filter call, then we'll use whatever the
  799. # SpecifierSet thinks for whether or not we should support prereleases.
  800. if prereleases is None:
  801. prereleases = self.prereleases
  802. # If we have any specifiers, then we want to wrap our iterable in the
  803. # filter method for each one, this will act as a logical AND amongst
  804. # each specifier.
  805. if self._specs:
  806. for spec in self._specs:
  807. iterable = spec.filter(iterable, prereleases=bool(prereleases))
  808. return iter(iterable)
  809. # If we do not have any specifiers, then we need to have a rough filter
  810. # which will filter out any pre-releases, unless there are no final
  811. # releases.
  812. else:
  813. filtered: List[UnparsedVersionVar] = []
  814. found_prereleases: List[UnparsedVersionVar] = []
  815. for item in iterable:
  816. parsed_version = _coerce_version(item)
  817. # Store any item which is a pre-release for later unless we've
  818. # already found a final version or we are accepting prereleases
  819. if parsed_version.is_prerelease and not prereleases:
  820. if not filtered:
  821. found_prereleases.append(item)
  822. else:
  823. filtered.append(item)
  824. # If we've found no items except for pre-releases, then we'll go
  825. # ahead and use the pre-releases
  826. if not filtered and found_prereleases and prereleases is None:
  827. return iter(found_prereleases)
  828. return iter(filtered)