_ranges.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  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. """Private version-range helpers used by :mod:`packaging.specifiers`."""
  5. from __future__ import annotations
  6. import enum
  7. import functools
  8. from typing import (
  9. TYPE_CHECKING,
  10. Any,
  11. Final,
  12. )
  13. from .version import InvalidVersion, Version
  14. if TYPE_CHECKING:
  15. from collections.abc import Callable, Iterable, Iterator, Sequence
  16. from typing import Union
  17. # Total-order key for comparing two boundaries (boundary-vs-boundary only).
  18. # The post slot may be ``_BOUNDARY_INF`` for an AFTER_POSTS boundary.
  19. _BoundaryOrderSuffix = tuple[int, int, int, Union[int, float], int, int]
  20. _BoundaryOrderKey = tuple[int, tuple[int, ...], _BoundaryOrderSuffix, float]
  21. __all__ = [
  22. "FULL_RANGE",
  23. "bounds_for_spec",
  24. "coerce_version",
  25. "filter_by_ranges",
  26. "intersect_ranges",
  27. "intersect_specifier_bounds",
  28. "least_version_above",
  29. "matches_bounds_only",
  30. "range_is_empty",
  31. "ranges_are_prerelease_only",
  32. "resolve_prereleases",
  33. "standard_ranges",
  34. "wildcard_ranges",
  35. ]
  36. #: The smallest possible PEP 440 version. No valid version is less than this.
  37. MIN_VERSION: Final[Version] = Version("0.dev0")
  38. #: The smallest non-pre-release version, i.e. the nearest non-pre-release at or
  39. #: above the ``-inf`` floor.
  40. MIN_RELEASE: Final[Version] = Version("0")
  41. #: Sorts above any real post number and any local label, so a boundary can be
  42. #: ordered above the version family it covers when two boundaries are compared.
  43. _BOUNDARY_INF: Final[float] = float("inf")
  44. class BoundaryKind(enum.Enum):
  45. """Where a boundary marker sits in the version ordering."""
  46. AFTER_LOCALS = enum.auto() # after V+local, before V.post0
  47. AFTER_POSTS = enum.auto() # after V.postN, before next release
  48. @functools.total_ordering
  49. class BoundaryVersion:
  50. """A point on the version line between two real PEP 440 versions.
  51. Relative to a base version V::
  52. V < V+local < AFTER_LOCALS(V) < V.post0 < AFTER_POSTS(V)
  53. AFTER_LOCALS is the upper bound of ``<=V``, ``==V``, ``!=V`` (no
  54. local), and the lower bound of the upper-side range of ``!=V``.
  55. AFTER_POSTS is the lower bound of ``>V`` (V final or pre-release),
  56. excluding V's post-releases per PEP 440.
  57. """
  58. __slots__ = (
  59. "_cached_dev",
  60. "_cached_epoch",
  61. "_cached_post",
  62. "_cached_pre",
  63. "_cached_trimmed_release",
  64. "kind",
  65. "version",
  66. )
  67. def __init__(self, version: Version, kind: BoundaryKind) -> None:
  68. self.version = version
  69. self.kind = kind
  70. self._cached_trimmed_release = trim_release(version.release)
  71. self._cached_epoch = version.epoch
  72. self._cached_pre = version.pre
  73. self._cached_post = version.post
  74. self._cached_dev = version.dev
  75. def _is_family(self, other: Version) -> bool:
  76. """Is ``other`` a version that this boundary sorts above?"""
  77. if other.epoch != self._cached_epoch:
  78. return False
  79. # Inline release-trim comparison: other.release matches the
  80. # trimmed release iff its leading slice is equal and any extra
  81. # components are zero. Avoids trim_release's tuple allocation.
  82. other_release = other.release
  83. trimmed_release = self._cached_trimmed_release
  84. trimmed_length = len(trimmed_release)
  85. if len(other_release) < trimmed_length:
  86. return False
  87. if other_release[:trimmed_length] != trimmed_release:
  88. return False
  89. for i in range(trimmed_length, len(other_release)):
  90. if other_release[i] != 0:
  91. return False
  92. if other.pre != self._cached_pre:
  93. return False
  94. if self.kind == BoundaryKind.AFTER_LOCALS:
  95. # Local family: same public version, any local label.
  96. return other.post == self._cached_post and other.dev == self._cached_dev
  97. # Post family: V itself + any post-release of V.
  98. return other.dev == self._cached_dev or other.post is not None
  99. def _order_key(self) -> _BoundaryOrderKey:
  100. """Sort key placing this boundary just above the versions it covers.
  101. It extends ``V``'s comparison key ``(epoch, release, suffix)`` with
  102. a trailing ``_BOUNDARY_INF`` local component, so the key sorts after
  103. ``V`` and every ``V+local`` (whose keys carry a real, finite local
  104. segment). ``suffix`` is the 6-int comparison suffix
  105. ``(pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)``.
  106. For an AFTER_POSTS boundary the suffix is replaced with one whose
  107. post number is ``_BOUNDARY_INF``, so the key also sorts after every
  108. ``V.postN``. An AFTER_LOCALS boundary uses ``V``'s suffix unchanged.
  109. """
  110. version_key = self.version._key
  111. suffix: _BoundaryOrderSuffix = version_key[2]
  112. if self.kind == BoundaryKind.AFTER_POSTS:
  113. suffix = (suffix[0], suffix[1], 1, _BOUNDARY_INF, 1, 0)
  114. return version_key[0], version_key[1], suffix, _BOUNDARY_INF
  115. def __eq__(self, other: object) -> bool:
  116. # Key off the order key so equality matches the ``<`` / ``>`` order:
  117. # ``AFTER_POSTS(1.0)`` and ``AFTER_POSTS(1.0.post1)`` are the same point.
  118. if isinstance(other, BoundaryVersion):
  119. return self._order_key() == other._order_key()
  120. return NotImplemented
  121. def __lt__(self, other: BoundaryVersion | Version) -> bool:
  122. if isinstance(other, BoundaryVersion):
  123. return self._order_key() < other._order_key()
  124. # boundary < other_version iff V < other AND other not in family.
  125. # The cheap V >= other path short-circuits before the family check.
  126. if not (self.version < other):
  127. return False
  128. return not self._is_family(other)
  129. def __gt__(self, other: BoundaryVersion | Version) -> bool:
  130. # Defined directly to bypass functools.total_ordering's
  131. # NotImplemented round-trip on reflected ``Version < boundary``.
  132. if isinstance(other, BoundaryVersion):
  133. return self._order_key() > other._order_key()
  134. if self.version >= other:
  135. return True
  136. return self._is_family(other)
  137. def __hash__(self) -> int:
  138. # Keyed to ``__eq__`` (the order key), so equal boundaries hash equal.
  139. return hash(self._order_key())
  140. def __repr__(self) -> str:
  141. return f"{self.__class__.__name__}({self.version!r}, {self.kind.name})"
  142. if TYPE_CHECKING:
  143. _VersionOrBoundary = Union[Version, BoundaryVersion, None]
  144. @functools.total_ordering
  145. class LowerBound:
  146. """Lower bound of a version range.
  147. A version *v* of ``None`` means unbounded below (-inf).
  148. At equal versions, ``[v`` sorts before ``(v`` because an inclusive
  149. bound starts earlier.
  150. """
  151. __slots__ = ("_above", "inclusive", "version")
  152. def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:
  153. self.version = version
  154. self.inclusive = inclusive
  155. # Pre-bind a predicate "is parsed at or above this lower
  156. # bound?" for the hot filter / contains loops. One direct
  157. # call per check, no operator-dispatch chain.
  158. if version is None:
  159. self._above: Callable[[Version], bool] | None = None
  160. elif isinstance(version, BoundaryVersion):
  161. # >V produces an AFTER_POSTS lower bound; the upper-side
  162. # range of !=V produces an AFTER_LOCALS lower bound.
  163. if version.kind == BoundaryKind.AFTER_POSTS:
  164. self._above = _make_above_after_posts(version.version)
  165. else:
  166. self._above = _make_above_after_locals(version.version)
  167. elif inclusive:
  168. self._above = version.__le__
  169. else:
  170. self._above = version.__lt__
  171. def __eq__(self, other: object) -> bool:
  172. if not isinstance(other, LowerBound):
  173. return NotImplemented
  174. return self.version == other.version and self.inclusive == other.inclusive
  175. def __lt__(self, other: LowerBound) -> bool:
  176. if not isinstance(other, LowerBound):
  177. return NotImplemented
  178. # -inf < anything (except -inf itself).
  179. if self.version is None:
  180. return other.version is not None
  181. if other.version is None:
  182. return False
  183. if self.version != other.version:
  184. return self.version < other.version
  185. # [v < (v: inclusive starts earlier.
  186. return self.inclusive and not other.inclusive
  187. def __hash__(self) -> int:
  188. return hash((self.version, self.inclusive))
  189. def __repr__(self) -> str:
  190. bracket = "[" if self.inclusive else "("
  191. return f"<{self.__class__.__name__} {bracket}{self.version!r}>"
  192. @functools.total_ordering
  193. class UpperBound:
  194. """Upper bound of a version range.
  195. A version *v* of ``None`` means unbounded above (+inf).
  196. At equal versions, ``v)`` sorts before ``v]`` because an exclusive
  197. bound ends earlier.
  198. """
  199. __slots__ = ("_below", "inclusive", "version")
  200. def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None:
  201. self.version = version
  202. self.inclusive = inclusive
  203. # Pre-bind a predicate "is parsed at or below this upper
  204. # bound?". See LowerBound for the rationale.
  205. if version is None:
  206. self._below: Callable[[Version], bool] | None = None
  207. elif isinstance(version, BoundaryVersion):
  208. # Standard specifiers only ever produce AFTER_LOCALS upper
  209. # bounds (from <=V / ==V / !=V with no local).
  210. if version.kind == BoundaryKind.AFTER_LOCALS:
  211. self._below = _make_below_after_locals(version.version)
  212. else:
  213. # An AFTER_POSTS upper is not produced by any specifier, but
  214. # range algebra reaches it: complementing ``>V`` flips the
  215. # ``AFTER_POSTS(V)`` lower into this upper bound.
  216. self._below = version.__ge__
  217. elif inclusive:
  218. self._below = version.__ge__
  219. else:
  220. self._below = version.__gt__
  221. def __eq__(self, other: object) -> bool:
  222. if not isinstance(other, UpperBound):
  223. return NotImplemented
  224. return self.version == other.version and self.inclusive == other.inclusive
  225. def __lt__(self, other: UpperBound) -> bool:
  226. if not isinstance(other, UpperBound):
  227. return NotImplemented
  228. # Nothing < +inf (except +inf itself).
  229. if self.version is None:
  230. return False
  231. if other.version is None:
  232. return True
  233. if self.version != other.version:
  234. return self.version < other.version
  235. # v) < v]: exclusive ends earlier.
  236. return not self.inclusive and other.inclusive
  237. def __hash__(self) -> int:
  238. return hash((self.version, self.inclusive))
  239. def __repr__(self) -> str:
  240. bracket = "]" if self.inclusive else ")"
  241. return f"<{self.__class__.__name__} {self.version!r}{bracket}>"
  242. if TYPE_CHECKING:
  243. #: A single contiguous interval as a (lower, upper) bound pair.
  244. Interval = tuple[LowerBound, UpperBound]
  245. NEG_INF: Final[LowerBound] = LowerBound(None, False)
  246. POS_INF: Final[UpperBound] = UpperBound(None, False)
  247. FULL_RANGE: Final[tuple[Interval]] = ((NEG_INF, POS_INF),)
  248. def trim_release(release: tuple[int, ...]) -> tuple[int, ...]:
  249. """Strip trailing zeros from a release tuple for normalized comparison."""
  250. end = len(release)
  251. while end > 1 and release[end - 1] == 0:
  252. end -= 1
  253. return release if end == len(release) else release[:end]
  254. def _next_prefix_dev0(version: Version) -> Version:
  255. """Smallest version in the next prefix: 1.2 -> 1.3.dev0."""
  256. release = (*version.release[:-1], version.release[-1] + 1)
  257. return Version.from_parts(epoch=version.epoch, release=release, dev=0)
  258. def _base_dev0(version: Version) -> Version:
  259. """The .dev0 of a version's base release: 1.2 -> 1.2.dev0."""
  260. return Version.from_parts(epoch=version.epoch, release=version.release, dev=0)
  261. def coerce_version(version: Version | str) -> Version | None:
  262. if not isinstance(version, Version):
  263. try:
  264. version = Version(version)
  265. except InvalidVersion:
  266. return None
  267. return version
  268. def _make_above_after_posts(version: Version) -> Callable[[Version], bool]:
  269. """Predicate ``parsed > AFTER_POSTS(V)`` for a lower bound.
  270. Per PEP 440, ``>V`` excludes V's post-releases unless V is itself
  271. a post-release. AFTER_POSTS sits above V and every V.postN (with
  272. or without local), and just below the next release.
  273. """
  274. version_ge = version.__ge__
  275. version_epoch = version.epoch
  276. version_pre = version.pre
  277. version_release_trimmed = trim_release(version.release)
  278. trimmed_length = len(version_release_trimmed)
  279. def above(parsed: Version) -> bool:
  280. if version_ge(parsed):
  281. return False
  282. # parsed > V cmpkey-wise: above the boundary iff NOT in V's
  283. # post family.
  284. if parsed.epoch != version_epoch:
  285. return True
  286. parsed_release = parsed.release
  287. if len(parsed_release) < trimmed_length:
  288. return True
  289. if parsed_release[:trimmed_length] != version_release_trimmed:
  290. return True
  291. for i in range(trimmed_length, len(parsed_release)):
  292. if parsed_release[i] != 0:
  293. return True
  294. if parsed.pre != version_pre:
  295. return True
  296. # Same release and pre as V: parsed is in V's post family (V itself,
  297. # V+local, or V.postN), which the boundary sits above. A V.devN
  298. # (different dev, no post) sorts before V and was already caught by
  299. # ``version_ge`` above, so the answer here is always "not above".
  300. return False
  301. return above
  302. def _make_above_after_locals(version: Version) -> Callable[[Version], bool]:
  303. """Predicate ``parsed > AFTER_LOCALS(V)`` for a lower bound.
  304. Used by the upper-side range of ``!=V`` (when V has no local
  305. segment). AFTER_LOCALS sits above V and every ``V+local`` but
  306. just below ``V.post0``.
  307. """
  308. version_ge = version.__ge__
  309. version_epoch = version.epoch
  310. version_pre = version.pre
  311. version_post = version.post
  312. version_dev = version.dev
  313. version_release_trimmed = trim_release(version.release)
  314. trimmed_length = len(version_release_trimmed)
  315. def above(parsed: Version) -> bool:
  316. if version_ge(parsed):
  317. return False
  318. # parsed > V cmpkey-wise: above the boundary iff NOT in V's
  319. # local family (same public version, any local segment).
  320. if parsed.epoch != version_epoch:
  321. return True
  322. parsed_release = parsed.release
  323. if len(parsed_release) < trimmed_length:
  324. return True
  325. if parsed_release[:trimmed_length] != version_release_trimmed:
  326. return True
  327. for i in range(trimmed_length, len(parsed_release)):
  328. if parsed_release[i] != 0:
  329. return True
  330. if parsed.pre != version_pre:
  331. return True
  332. if parsed.post != version_post:
  333. return True
  334. return parsed.dev != version_dev
  335. return above
  336. def _make_below_after_locals(version: Version) -> Callable[[Version], bool]:
  337. """Predicate ``parsed <= AFTER_LOCALS(V)`` for an upper bound.
  338. Used by ``<=V``, ``==V``, ``!=V`` (no local). ``parsed`` is at or
  339. below the boundary when it is at or below V cmpkey-wise, or when
  340. it is in V's local family.
  341. """
  342. version_ge = version.__ge__
  343. version_epoch = version.epoch
  344. version_pre = version.pre
  345. version_post = version.post
  346. version_dev = version.dev
  347. version_release_trimmed = trim_release(version.release)
  348. trimmed_length = len(version_release_trimmed)
  349. def below(parsed: Version) -> bool:
  350. if version_ge(parsed):
  351. return True
  352. # parsed > V cmpkey-wise: below the boundary iff in V's local
  353. # family.
  354. if parsed.epoch != version_epoch:
  355. return False
  356. parsed_release = parsed.release
  357. if len(parsed_release) < trimmed_length:
  358. return False
  359. if parsed_release[:trimmed_length] != version_release_trimmed:
  360. return False
  361. for i in range(trimmed_length, len(parsed_release)):
  362. if parsed_release[i] != 0:
  363. return False
  364. if parsed.pre != version_pre:
  365. return False
  366. if parsed.post != version_post:
  367. return False
  368. return parsed.dev == version_dev
  369. return below
  370. def least_version_above(boundary: BoundaryVersion) -> Version | None:
  371. """Smallest real version strictly above *boundary*, or ``None`` if none exists."""
  372. base = boundary.version
  373. if boundary.kind == BoundaryKind.AFTER_LOCALS:
  374. # AFTER_LOCALS(V) sits just below V.post0, so its least successor is
  375. # V.post0.dev0 (V.dev(N+1) if V has a dev, V.post(N+1).dev0 if a post).
  376. if base.dev is not None:
  377. return base.__replace__(dev=base.dev + 1, local=None)
  378. next_post = (base.post + 1) if base.post is not None else 0
  379. return base.__replace__(post=next_post, dev=0, local=None)
  380. # AFTER_POSTS(V): a pre-release V steps to the next pre-release's .dev0;
  381. # a final-release AFTER_POSTS has no least successor.
  382. if base.pre is not None:
  383. kind, number = base.pre
  384. return base.__replace__(pre=(kind, number + 1), post=None, dev=0, local=None)
  385. return None
  386. def range_is_empty(lower: LowerBound, upper: UpperBound) -> bool:
  387. """True when the range defined by *lower* and *upper* contains no versions.
  388. A boundary lower sits just below the next real version, so an ordered pair
  389. is still empty when the upper excludes that least successor:
  390. ``(AFTER_POSTS(1.0a1), 1.0a2.dev0)`` holds no version.
  391. """
  392. if upper.version is None:
  393. return False
  394. if lower.version is None:
  395. # Nothing sorts below MIN_VERSION, so an exclusive upper at or below it
  396. # leaves an empty floor interval such as ``(-inf, 0.dev0)``.
  397. return (
  398. not upper.inclusive
  399. and isinstance(upper.version, Version)
  400. and upper.version <= MIN_VERSION
  401. )
  402. if isinstance(lower.version, BoundaryVersion):
  403. successor = least_version_above(lower.version)
  404. if successor is not None:
  405. if upper.version == successor:
  406. return not upper.inclusive
  407. return upper.version < successor
  408. if lower.version == upper.version:
  409. return not (lower.inclusive and upper.inclusive)
  410. return lower.version > upper.version
  411. def intersect_ranges(
  412. left: Sequence[Interval],
  413. right: Sequence[Interval],
  414. ) -> list[Interval]:
  415. """Intersect two sorted, non-overlapping range lists (two-pointer merge)."""
  416. result: list[Interval] = []
  417. left_index = right_index = 0
  418. while left_index < len(left) and right_index < len(right):
  419. left_lower, left_upper = left[left_index]
  420. right_lower, right_upper = right[right_index]
  421. lower = max(left_lower, right_lower)
  422. upper = min(left_upper, right_upper)
  423. if not range_is_empty(lower, upper):
  424. result.append((lower, upper))
  425. # Advance whichever side has the smaller upper bound.
  426. if left_upper < right_upper:
  427. left_index += 1
  428. else:
  429. right_index += 1
  430. return result
  431. def filter_by_ranges(
  432. ranges: Sequence[Interval],
  433. iterable: Iterable[Any],
  434. key: Callable[[Any], Version | str] | None,
  435. prereleases: bool | None,
  436. region: Sequence[Interval] = (),
  437. ) -> Iterator[Any]:
  438. """Filter *iterable* against precomputed version *ranges*.
  439. With ``prereleases=None``, the PEP 440 default applies: pre-releases are
  440. excluded unless no final matches, in which case buffered pre-releases come
  441. out at the end. A pre-release inside the opt-in ``region`` is the exception:
  442. it is force-admitted in place, as ``prereleases=True`` would yield it. A
  443. force-admitted pre-release is not a final, so it never suppresses the buffer.
  444. """
  445. if prereleases is None:
  446. prerelease_buffer: list[Any] = []
  447. found_final = False
  448. if len(ranges) == 1:
  449. # Hot path: most specifiers and small SpecifierSets reduce to
  450. # a single contiguous range.
  451. lower, upper = ranges[0]
  452. above = lower._above
  453. below = upper._below
  454. for item in iterable:
  455. parsed = coerce_version(item if key is None else key(item))
  456. if parsed is None:
  457. continue
  458. if above is not None and not above(parsed):
  459. continue
  460. if below is not None and not below(parsed):
  461. continue
  462. if not parsed.is_prerelease:
  463. found_final = True
  464. yield item
  465. elif region and matches_bounds_only(region, parsed):
  466. yield item
  467. elif not found_final:
  468. prerelease_buffer.append(item)
  469. if not found_final:
  470. yield from prerelease_buffer
  471. return
  472. for item in iterable:
  473. parsed = coerce_version(item if key is None else key(item))
  474. if parsed is None:
  475. continue
  476. for lower, upper in ranges:
  477. above = lower._above
  478. if above is not None and not above(parsed):
  479. break
  480. below = upper._below
  481. if below is None or below(parsed):
  482. if not parsed.is_prerelease:
  483. found_final = True
  484. yield item
  485. elif region and matches_bounds_only(region, parsed):
  486. yield item
  487. elif not found_final:
  488. prerelease_buffer.append(item)
  489. break
  490. if not found_final:
  491. yield from prerelease_buffer
  492. return
  493. exclude_prereleases = prereleases is False
  494. if len(ranges) == 1:
  495. # Hot path: most specifiers and small SpecifierSets reduce to
  496. # a single contiguous range.
  497. lower, upper = ranges[0]
  498. above = lower._above
  499. below = upper._below
  500. for item in iterable:
  501. parsed = coerce_version(item if key is None else key(item))
  502. if parsed is None:
  503. continue
  504. if exclude_prereleases and parsed.is_prerelease:
  505. continue
  506. if above is not None and not above(parsed):
  507. continue
  508. if below is None or below(parsed):
  509. yield item
  510. return
  511. for item in iterable:
  512. parsed = coerce_version(item if key is None else key(item))
  513. if parsed is None:
  514. continue
  515. if exclude_prereleases and parsed.is_prerelease:
  516. continue
  517. for lower, upper in ranges:
  518. above = lower._above
  519. if above is not None and not above(parsed):
  520. break
  521. below = upper._below
  522. if below is None or below(parsed):
  523. yield item
  524. break
  525. def _nearest_release_above_prerelease(version: Version) -> Version:
  526. """Smallest non-pre-release at or above a pre-release *version*."""
  527. if version.pre is not None:
  528. # An a/b/rc pre-release drops to its final release, which outranks
  529. # every post-release of that pre-release (1.0a1.post0 -> 1.0).
  530. return version.__replace__(pre=None, post=None, dev=None, local=None)
  531. # A dev-only release keeps its post-release (1.0.post0.dev0 -> 1.0.post0,
  532. # whose final 1.0 sorts below it).
  533. return version.__replace__(dev=None, local=None)
  534. def _lowest_release_at_or_above(value: Version | BoundaryVersion | None) -> Version:
  535. """Smallest non-pre-release version at or above *value*.
  536. ``None`` is the ``-inf`` floor, whose nearest non-pre-release is
  537. :data:`MIN_RELEASE`.
  538. """
  539. if value is None:
  540. return MIN_RELEASE
  541. if isinstance(value, BoundaryVersion):
  542. inner_version = value.version
  543. if inner_version.is_prerelease:
  544. return _nearest_release_above_prerelease(inner_version)
  545. # AFTER_LOCALS(1.0) -> nearest non-pre is 1.0.post0
  546. # AFTER_LOCALS(1.0.post0) -> nearest non-pre is 1.0.post1
  547. next_post = (inner_version.post + 1) if inner_version.post is not None else 0
  548. return inner_version.__replace__(post=next_post, local=None)
  549. if not value.is_prerelease:
  550. return value
  551. return _nearest_release_above_prerelease(value)
  552. def ranges_are_prerelease_only(ranges: Sequence[Interval]) -> bool:
  553. """True when every range in *ranges* contains only pre-releases.
  554. Used to detect unsatisfiable specifier sets when ``prereleases=False``:
  555. if every range is pre-release-only, every contained version is excluded.
  556. """
  557. for lower, upper in ranges:
  558. nearest = _lowest_release_at_or_above(lower.version)
  559. if upper.version is None or nearest < upper.version:
  560. return False
  561. if nearest == upper.version and upper.inclusive:
  562. return False
  563. return True
  564. def wildcard_ranges(op: str, base: Version) -> list[Interval]:
  565. """Ranges for ==V.* and !=V.*.
  566. ==1.2.* -> [1.2.dev0, 1.3.dev0); !=1.2.* -> complement.
  567. """
  568. lower = _base_dev0(base)
  569. upper = _next_prefix_dev0(base)
  570. if op == "==":
  571. return [(LowerBound(lower, True), UpperBound(upper, False))]
  572. # !=
  573. return [
  574. (NEG_INF, UpperBound(lower, False)),
  575. (LowerBound(upper, True), POS_INF),
  576. ]
  577. def standard_ranges(op: str, version: Version, has_local: bool) -> list[Interval]:
  578. """Ranges for the standard PEP 440 operators (no wildcard, no ===).
  579. *has_local* indicates whether the spec string included a ``+local``
  580. segment; relevant only for ``==`` / ``!=`` to decide whether the
  581. upper bound includes V's local family.
  582. """
  583. if op == ">=":
  584. return [(LowerBound(version, True), POS_INF)]
  585. if op == "<=":
  586. return [
  587. (
  588. NEG_INF,
  589. UpperBound(BoundaryVersion(version, BoundaryKind.AFTER_LOCALS), True),
  590. )
  591. ]
  592. if op == ">":
  593. if version.dev is not None:
  594. # >V.devN: dev versions have no post-releases, so the
  595. # next real version is V.dev(N+1).
  596. lower_bound = version.__replace__(dev=version.dev + 1, local=None)
  597. return [(LowerBound(lower_bound, True), POS_INF)]
  598. if version.post is not None:
  599. # >V.postN: next real version is V.post(N+1).dev0.
  600. lower_bound = version.__replace__(post=version.post + 1, dev=0, local=None)
  601. return [(LowerBound(lower_bound, True), POS_INF)]
  602. # >V (final or pre-release V): exclude V itself, V+local, and
  603. # every V.postN per PEP 440.
  604. return [
  605. (
  606. LowerBound(BoundaryVersion(version, BoundaryKind.AFTER_POSTS), False),
  607. POS_INF,
  608. )
  609. ]
  610. if op == "<":
  611. # <V excludes pre-releases of V when V is not a pre-release.
  612. # V.dev0 is the earliest pre-release of V.
  613. bound = (
  614. version if version.is_prerelease else version.__replace__(dev=0, local=None)
  615. )
  616. if bound <= MIN_VERSION:
  617. return []
  618. return [(NEG_INF, UpperBound(bound, False))]
  619. # ==, !=: local versions of V match when the spec has no local segment.
  620. after_locals = BoundaryVersion(version, BoundaryKind.AFTER_LOCALS)
  621. upper = version if has_local else after_locals
  622. if op == "==":
  623. return [(LowerBound(version, True), UpperBound(upper, True))]
  624. if op == "!=":
  625. return [
  626. (NEG_INF, UpperBound(version, False)),
  627. (LowerBound(upper, False), POS_INF),
  628. ]
  629. if op == "~=":
  630. prefix = version.__replace__(release=version.release[:-1])
  631. return [
  632. (LowerBound(version, True), UpperBound(_next_prefix_dev0(prefix), False))
  633. ]
  634. raise ValueError(f"Unknown operator: {op!r}") # pragma: no cover
  635. def bounds_for_spec(op: str, version_str: str, version: Version) -> list[Interval]:
  636. """Ranges for one specifier's ``(op, version_str)``.
  637. Dispatches between the wildcard and standard builders. ``version`` is the
  638. parsed ``version_str`` (its base, without the trailing ``.*``, for
  639. wildcards). ``===`` is not handled here; its match is a literal string
  640. compared in :mod:`packaging.specifiers`.
  641. """
  642. if version_str.endswith(".*"):
  643. return wildcard_ranges(op, version)
  644. return standard_ranges(op, version, "+" in version_str)
  645. def intersect_specifier_bounds(
  646. per_specifier_ranges: Iterable[Sequence[Interval]],
  647. ) -> Sequence[Interval]:
  648. """Intersect each specifier's ranges into a single sequence.
  649. Short-circuits once the running intersection is empty, since no later
  650. specifier can revive it. Callers must pass at least one specifier.
  651. """
  652. result: Sequence[Interval] | None = None
  653. for sub in per_specifier_ranges:
  654. if result is None:
  655. result = sub
  656. else:
  657. result = intersect_ranges(result, sub)
  658. if not result:
  659. break
  660. if result is None: # pragma: no cover - callers guard non-empty input
  661. raise RuntimeError("intersect_specifier_bounds called with no specifiers")
  662. return result
  663. def matches_bounds_only(ranges: Sequence[Interval], version: Version) -> bool:
  664. """Whether ``version`` falls within any of ``ranges``.
  665. The pure bounds membership test, for a single already-parsed version with
  666. no pre-release policy applied. ``ranges`` are sorted and non-overlapping,
  667. so a version below one range's lower bound is below every later range too.
  668. """
  669. for lower, upper in ranges:
  670. above = lower._above
  671. if above is not None and not above(version):
  672. return False
  673. below = upper._below
  674. if below is None or below(version):
  675. return True
  676. return False
  677. def resolve_prereleases(
  678. configured: bool | None, autodetected: bool | None
  679. ) -> bool | None:
  680. """Resolve a specifier's effective default pre-release policy.
  681. An explicit ``configured`` value wins; otherwise an autodetected ``True``
  682. propagates and anything else falls back to the PEP 440 default (``None``).
  683. """
  684. if configured is not None:
  685. return configured
  686. if autodetected:
  687. return True
  688. return None