specifiers.py 40 KB

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