Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pip/_vendor/packaging/specifiers.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

688 statements  

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 

7 from pip._vendor.packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier 

8 from pip._vendor.packaging.version import Version 

9""" 

10 

11from __future__ import annotations 

12 

13import abc 

14import enum 

15import functools 

16import itertools 

17import re 

18import sys 

19import typing 

20from typing import ( 

21 TYPE_CHECKING, 

22 Any, 

23 Callable, 

24 Final, 

25 Iterable, 

26 Iterator, 

27 Sequence, 

28 TypeVar, 

29 Union, 

30) 

31 

32from .utils import canonicalize_version 

33from .version import InvalidVersion, Version 

34 

35if sys.version_info >= (3, 10): 

36 from typing import TypeGuard # pragma: no cover 

37elif TYPE_CHECKING: 

38 from typing_extensions import TypeGuard 

39 

40__all__ = [ 

41 "BaseSpecifier", 

42 "InvalidSpecifier", 

43 "Specifier", 

44 "SpecifierSet", 

45] 

46 

47 

48def __dir__() -> list[str]: 

49 return __all__ 

50 

51 

52def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]: 

53 return ( 

54 isinstance(spec, tuple) 

55 and len(spec) == 2 

56 and isinstance(spec[0], str) 

57 and isinstance(spec[1], str) 

58 ) 

59 

60 

61def _validate_pre(pre: object, /) -> TypeGuard[bool | None]: 

62 return pre is None or isinstance(pre, bool) 

63 

64 

65T = TypeVar("T") 

66UnparsedVersion = Union[Version, str] 

67UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) 

68CallableOperator = Callable[[Version, str], bool] 

69 

70# The smallest possible PEP 440 version. No valid version is less than this. 

71_MIN_VERSION: Final[Version] = Version("0.dev0") 

72 

73 

74def _trim_release(release: tuple[int, ...]) -> tuple[int, ...]: 

75 """Strip trailing zeros from a release tuple for normalized comparison.""" 

76 end = len(release) 

77 while end > 1 and release[end - 1] == 0: 

78 end -= 1 

79 return release if end == len(release) else release[:end] 

80 

81 

82class _BoundaryKind(enum.Enum): 

83 """Where a boundary marker sits in the version ordering.""" 

84 

85 AFTER_LOCALS = enum.auto() # after V+local, before V.post0 

86 AFTER_POSTS = enum.auto() # after V.postN, before next release 

87 

88 

89@functools.total_ordering 

90class _BoundaryVersion: 

91 """A point on the version line between two real PEP 440 versions. 

92 

93 Some specifier semantics imply boundaries between real versions: 

94 ``<=1.0`` includes ``1.0+local`` and ``>1.0`` excludes 

95 ``1.0.post0``. No real :class:`Version` falls on those boundaries, 

96 so this class creates values that sort between the real versions 

97 on either side. 

98 

99 Two kinds exist, shown relative to a base version V:: 

100 

101 V < V+local < AFTER_LOCALS(V) < V.post0 < AFTER_POSTS(V) 

102 

103 ``AFTER_LOCALS`` sits after V and every V+local, but before 

104 V.post0. Upper bound of ``<=V``, ``==V``, ``!=V``. 

105 

106 ``AFTER_POSTS`` sits after every V.postN, but before the next 

107 release segment. Lower bound of ``>V`` (final or pre-release V) 

108 to exclude post-releases per PEP 440. 

109 """ 

110 

111 __slots__ = ("_kind", "_trimmed_release", "version") 

112 

113 def __init__(self, version: Version, kind: _BoundaryKind) -> None: 

114 self.version = version 

115 self._kind = kind 

116 self._trimmed_release = _trim_release(version.release) 

117 

118 def _is_family(self, other: Version) -> bool: 

119 """Is ``other`` a version that this boundary sorts above?""" 

120 v = self.version 

121 if not ( 

122 other.epoch == v.epoch 

123 and _trim_release(other.release) == self._trimmed_release 

124 and other.pre == v.pre 

125 ): 

126 return False 

127 if self._kind == _BoundaryKind.AFTER_LOCALS: 

128 # Local family: exact same public version (any local label). 

129 return other.post == v.post and other.dev == v.dev 

130 # Post family: same base + any post-release (or identical). 

131 return other.dev == v.dev or other.post is not None 

132 

133 def __eq__(self, other: object) -> bool: 

134 if isinstance(other, _BoundaryVersion): 

135 return self.version == other.version and self._kind == other._kind 

136 return NotImplemented 

137 

138 def __lt__(self, other: _BoundaryVersion | Version) -> bool: 

139 if isinstance(other, _BoundaryVersion): 

140 if self.version != other.version: 

141 return self.version < other.version 

142 return self._kind.value < other._kind.value 

143 return not self._is_family(other) and self.version < other 

144 

145 def __hash__(self) -> int: 

146 return hash((self.version, self._kind)) 

147 

148 def __repr__(self) -> str: 

149 return f"{self.__class__.__name__}({self.version!r}, {self._kind.name})" 

150 

151 

152@functools.total_ordering 

153class _LowerBound: 

154 """Lower bound of a version range. 

155 

156 A version *v* of ``None`` means unbounded below (-inf). 

157 At equal versions, ``[v`` sorts before ``(v`` because an inclusive 

158 bound starts earlier. 

159 """ 

160 

161 __slots__ = ("inclusive", "version") 

162 

163 def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None: 

164 self.version = version 

165 self.inclusive = inclusive 

166 

167 def __eq__(self, other: object) -> bool: 

168 if not isinstance(other, _LowerBound): 

169 return NotImplemented # pragma: no cover 

170 return self.version == other.version and self.inclusive == other.inclusive 

171 

172 def __lt__(self, other: _LowerBound) -> bool: 

173 if not isinstance(other, _LowerBound): # pragma: no cover 

174 return NotImplemented 

175 # -inf < anything (except -inf). 

176 if self.version is None: 

177 return other.version is not None 

178 if other.version is None: 

179 return False 

180 if self.version != other.version: 

181 return self.version < other.version 

182 # [v < (v: inclusive starts earlier. 

183 return self.inclusive and not other.inclusive 

184 

185 def __hash__(self) -> int: 

186 return hash((self.version, self.inclusive)) 

187 

188 def __repr__(self) -> str: 

189 bracket = "[" if self.inclusive else "(" 

190 return f"<{self.__class__.__name__} {bracket}{self.version!r}>" 

191 

192 

193@functools.total_ordering 

194class _UpperBound: 

195 """Upper bound of a version range. 

196 

197 A version *v* of ``None`` means unbounded above (+inf). 

198 At equal versions, ``v)`` sorts before ``v]`` because an exclusive 

199 bound ends earlier. 

200 """ 

201 

202 __slots__ = ("inclusive", "version") 

203 

204 def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None: 

205 self.version = version 

206 self.inclusive = inclusive 

207 

208 def __eq__(self, other: object) -> bool: 

209 if not isinstance(other, _UpperBound): 

210 return NotImplemented # pragma: no cover 

211 return self.version == other.version and self.inclusive == other.inclusive 

212 

213 def __lt__(self, other: _UpperBound) -> bool: 

214 if not isinstance(other, _UpperBound): # pragma: no cover 

215 return NotImplemented 

216 # Nothing < +inf (except +inf itself). 

217 if self.version is None: 

218 return False 

219 if other.version is None: 

220 return True 

221 if self.version != other.version: 

222 return self.version < other.version 

223 # v) < v]: exclusive ends earlier. 

224 return not self.inclusive and other.inclusive 

225 

226 def __hash__(self) -> int: 

227 return hash((self.version, self.inclusive)) 

228 

229 def __repr__(self) -> str: 

230 bracket = "]" if self.inclusive else ")" 

231 return f"<{self.__class__.__name__} {self.version!r}{bracket}>" 

232 

233 

234if typing.TYPE_CHECKING: 

235 _VersionOrBoundary = Union[Version, _BoundaryVersion, None] 

236 

237 #: A single contiguous version range, represented as a 

238 #: (lower bound, upper bound) pair. 

239 _VersionRange = tuple[_LowerBound, _UpperBound] 

240 

241_NEG_INF = _LowerBound(None, False) 

242_POS_INF = _UpperBound(None, False) 

243_FULL_RANGE: tuple[_VersionRange] = ((_NEG_INF, _POS_INF),) 

244 

245 

246def _range_is_empty(lower: _LowerBound, upper: _UpperBound) -> bool: 

247 """True when the range defined by *lower* and *upper* contains no versions.""" 

248 if lower.version is None or upper.version is None: 

249 return False 

250 if lower.version == upper.version: 

251 return not (lower.inclusive and upper.inclusive) 

252 return lower.version > upper.version 

253 

254 

255def _intersect_ranges( 

256 left: Sequence[_VersionRange], 

257 right: Sequence[_VersionRange], 

258) -> list[_VersionRange]: 

259 """Intersect two sorted, non-overlapping range lists (two-pointer merge).""" 

260 result: list[_VersionRange] = [] 

261 left_index = right_index = 0 

262 while left_index < len(left) and right_index < len(right): 

263 left_lower, left_upper = left[left_index] 

264 right_lower, right_upper = right[right_index] 

265 

266 lower = max(left_lower, right_lower) 

267 upper = min(left_upper, right_upper) 

268 

269 if not _range_is_empty(lower, upper): 

270 result.append((lower, upper)) 

271 

272 # Advance whichever side has the smaller upper bound. 

273 if left_upper < right_upper: 

274 left_index += 1 

275 else: 

276 right_index += 1 

277 

278 return result 

279 

280 

281def _next_prefix_dev0(version: Version) -> Version: 

282 """Smallest version in the next prefix: 1.2 -> 1.3.dev0.""" 

283 release = (*version.release[:-1], version.release[-1] + 1) 

284 return Version.from_parts(epoch=version.epoch, release=release, dev=0) 

285 

286 

287def _base_dev0(version: Version) -> Version: 

288 """The .dev0 of a version's base release: 1.2 -> 1.2.dev0.""" 

289 return Version.from_parts(epoch=version.epoch, release=version.release, dev=0) 

290 

291 

292def _coerce_version(version: UnparsedVersion) -> Version | None: 

293 if not isinstance(version, Version): 

294 try: 

295 version = Version(version) 

296 except InvalidVersion: 

297 return None 

298 return version 

299 

300 

301def _public_version(version: Version) -> Version: 

302 if version.local is None: 

303 return version 

304 return version.__replace__(local=None) 

305 

306 

307def _post_base(version: Version) -> Version: 

308 """The version that *version* is a post-release of. 

309 

310 1.0.post1 -> 1.0, 1.0a1.post0 -> 1.0a1, 1.0.post0.dev1 -> 1.0. 

311 """ 

312 return version.__replace__(post=None, dev=None, local=None) 

313 

314 

315def _earliest_prerelease(version: Version) -> Version: 

316 """Earliest pre-release of *version*. 

317 

318 1.2 -> 1.2.dev0, 1.2.post1 -> 1.2.post1.dev0. 

319 """ 

320 return version.__replace__(dev=0, local=None) 

321 

322 

323def _nearest_non_prerelease( 

324 v: _VersionOrBoundary, 

325) -> Version | None: 

326 """Smallest non-pre-release version at or above *v*, or None.""" 

327 if v is None: 

328 return None 

329 if isinstance(v, _BoundaryVersion): 

330 inner = v.version 

331 if inner.is_prerelease: 

332 # AFTER_LOCALS(1.0a1) -> nearest non-pre is 1.0 

333 return inner.__replace__(pre=None, dev=None, local=None) 

334 # AFTER_LOCALS(1.0) -> nearest non-pre is 1.0.post0 

335 # AFTER_LOCALS(1.0.post0) -> nearest non-pre is 1.0.post1 

336 k = (inner.post + 1) if inner.post is not None else 0 

337 return inner.__replace__(post=k, local=None) 

338 if not v.is_prerelease: 

339 return v 

340 # Strip pre/dev to get the final or post-release form. 

341 return v.__replace__(pre=None, dev=None, local=None) 

342 

343 

344class InvalidSpecifier(ValueError): 

345 """ 

346 Raised when attempting to create a :class:`Specifier` with a specifier 

347 string that is invalid. 

348 

349 >>> Specifier("lolwat") 

350 Traceback (most recent call last): 

351 ... 

352 packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' 

353 """ 

354 

355 

356class BaseSpecifier(metaclass=abc.ABCMeta): 

357 __slots__ = () 

358 __match_args__ = ("_str",) 

359 

360 @property 

361 def _str(self) -> str: 

362 """Internal property for match_args""" 

363 return str(self) 

364 

365 @abc.abstractmethod 

366 def __str__(self) -> str: 

367 """ 

368 Returns the str representation of this Specifier-like object. This 

369 should be representative of the Specifier itself. 

370 """ 

371 

372 @abc.abstractmethod 

373 def __hash__(self) -> int: 

374 """ 

375 Returns a hash value for this Specifier-like object. 

376 """ 

377 

378 @abc.abstractmethod 

379 def __eq__(self, other: object) -> bool: 

380 """ 

381 Returns a boolean representing whether or not the two Specifier-like 

382 objects are equal. 

383 

384 :param other: The other object to check against. 

385 """ 

386 

387 @property 

388 @abc.abstractmethod 

389 def prereleases(self) -> bool | None: 

390 """Whether or not pre-releases as a whole are allowed. 

391 

392 This can be set to either ``True`` or ``False`` to explicitly enable or disable 

393 prereleases or it can be set to ``None`` (the default) to use default semantics. 

394 """ 

395 

396 @prereleases.setter # noqa: B027 

397 def prereleases(self, value: bool) -> None: 

398 """Setter for :attr:`prereleases`. 

399 

400 :param value: The value to set. 

401 """ 

402 

403 @abc.abstractmethod 

404 def contains(self, item: str, prereleases: bool | None = None) -> bool: 

405 """ 

406 Determines if the given item is contained within this specifier. 

407 """ 

408 

409 @typing.overload 

410 def filter( 

411 self, 

412 iterable: Iterable[UnparsedVersionVar], 

413 prereleases: bool | None = None, 

414 key: None = ..., 

415 ) -> Iterator[UnparsedVersionVar]: ... 

416 

417 @typing.overload 

418 def filter( 

419 self, 

420 iterable: Iterable[T], 

421 prereleases: bool | None = None, 

422 key: Callable[[T], UnparsedVersion] = ..., 

423 ) -> Iterator[T]: ... 

424 

425 @abc.abstractmethod 

426 def filter( 

427 self, 

428 iterable: Iterable[Any], 

429 prereleases: bool | None = None, 

430 key: Callable[[Any], UnparsedVersion] | None = None, 

431 ) -> Iterator[Any]: 

432 """ 

433 Takes an iterable of items and filters them so that only items which 

434 are contained within this specifier are allowed in it. 

435 """ 

436 

437 

438class Specifier(BaseSpecifier): 

439 """This class abstracts handling of version specifiers. 

440 

441 .. tip:: 

442 

443 It is generally not required to instantiate this manually. You should instead 

444 prefer to work with :class:`SpecifierSet` instead, which can parse 

445 comma-separated version specifiers (which is what package metadata contains). 

446 

447 Instances are safe to serialize with :mod:`pickle`. They use a stable 

448 format so the same pickle can be loaded in future packaging releases. 

449 

450 .. versionchanged:: 26.2 

451 

452 Added a stable pickle format. Pickles created with packaging 26.2+ can 

453 be unpickled with future releases. Backward compatibility with pickles 

454 from pip._vendor.packaging < 26.2 is supported but may be removed in a future 

455 release. 

456 """ 

457 

458 __slots__ = ( 

459 "_prereleases", 

460 "_ranges", 

461 "_spec", 

462 "_spec_version", 

463 "_wildcard_split", 

464 ) 

465 

466 _specifier_regex_str = r""" 

467 (?: 

468 (?: 

469 # The identity operators allow for an escape hatch that will 

470 # do an exact string match of the version you wish to install. 

471 # This will not be parsed by PEP 440 and we cannot determine 

472 # any semantic meaning from it. This operator is discouraged 

473 # but included entirely as an escape hatch. 

474 === # Only match for the identity operator 

475 \s* 

476 [^\s;)]* # The arbitrary version can be just about anything, 

477 # we match everything except for whitespace, a 

478 # semi-colon for marker support, and a closing paren 

479 # since versions can be enclosed in them. 

480 ) 

481 | 

482 (?: 

483 # The (non)equality operators allow for wild card and local 

484 # versions to be specified so we have to define these two 

485 # operators separately to enable that. 

486 (?:==|!=) # Only match for equals and not equals 

487 

488 \s* 

489 v? 

490 (?:[0-9]+!)? # epoch 

491 [0-9]+(?:\.[0-9]+)* # release 

492 

493 # You cannot use a wild card and a pre-release, post-release, a dev or 

494 # local version together so group them with a | and make them optional. 

495 (?: 

496 \.\* # Wild card syntax of .* 

497 | 

498 (?a: # pre release 

499 [-_\.]? 

500 (alpha|beta|preview|pre|a|b|c|rc) 

501 [-_\.]? 

502 [0-9]* 

503 )? 

504 (?a: # post release 

505 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) 

506 )? 

507 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release 

508 (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local 

509 )? 

510 ) 

511 | 

512 (?: 

513 # The compatible operator requires at least two digits in the 

514 # release segment. 

515 (?:~=) # Only match for the compatible operator 

516 

517 \s* 

518 v? 

519 (?:[0-9]+!)? # epoch 

520 [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) 

521 (?: # pre release 

522 [-_\.]? 

523 (alpha|beta|preview|pre|a|b|c|rc) 

524 [-_\.]? 

525 [0-9]* 

526 )? 

527 (?: # post release 

528 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) 

529 )? 

530 (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release 

531 ) 

532 | 

533 (?: 

534 # All other operators only allow a sub set of what the 

535 # (non)equality operators do. Specifically they do not allow 

536 # local versions to be specified nor do they allow the prefix 

537 # matching wild cards. 

538 (?:<=|>=|<|>) 

539 

540 \s* 

541 v? 

542 (?:[0-9]+!)? # epoch 

543 [0-9]+(?:\.[0-9]+)* # release 

544 (?a: # pre release 

545 [-_\.]? 

546 (alpha|beta|preview|pre|a|b|c|rc) 

547 [-_\.]? 

548 [0-9]* 

549 )? 

550 (?a: # post release 

551 (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) 

552 )? 

553 (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release 

554 ) 

555 ) 

556 """ 

557 

558 _regex = re.compile( 

559 r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE 

560 ) 

561 

562 _operators: Final = { 

563 "~=": "compatible", 

564 "==": "equal", 

565 "!=": "not_equal", 

566 "<=": "less_than_equal", 

567 ">=": "greater_than_equal", 

568 "<": "less_than", 

569 ">": "greater_than", 

570 "===": "arbitrary", 

571 } 

572 

573 def __init__(self, spec: str = "", prereleases: bool | None = None) -> None: 

574 """Initialize a Specifier instance. 

575 

576 :param spec: 

577 The string representation of a specifier which will be parsed and 

578 normalized before use. 

579 :param prereleases: 

580 This tells the specifier if it should accept prerelease versions if 

581 applicable or not. The default of ``None`` will autodetect it from the 

582 given specifiers. 

583 :raises InvalidSpecifier: 

584 If the given specifier is invalid (i.e. bad syntax). 

585 """ 

586 if not self._regex.fullmatch(spec): 

587 raise InvalidSpecifier(f"Invalid specifier: {spec!r}") 

588 

589 spec = spec.strip() 

590 if spec.startswith("==="): 

591 operator, version = spec[:3], spec[3:].strip() 

592 elif spec.startswith(("~=", "==", "!=", "<=", ">=")): 

593 operator, version = spec[:2], spec[2:].strip() 

594 else: 

595 operator, version = spec[:1], spec[1:].strip() 

596 

597 self._spec: tuple[str, str] = (operator, version) 

598 

599 # Store whether or not this Specifier should accept prereleases 

600 self._prereleases = prereleases 

601 

602 # Specifier version cache 

603 self._spec_version: tuple[str, Version] | None = None 

604 

605 # Populated on first wildcard (==X.*) comparison 

606 self._wildcard_split: tuple[list[str], int] | None = None 

607 

608 # Version range cache (populated by _to_ranges) 

609 self._ranges: Sequence[_VersionRange] | None = None 

610 

611 def _get_spec_version(self, version: str) -> Version | None: 

612 """One element cache, as only one spec Version is needed per Specifier.""" 

613 if self._spec_version is not None and self._spec_version[0] == version: 

614 return self._spec_version[1] 

615 

616 version_specifier = _coerce_version(version) 

617 if version_specifier is None: 

618 return None 

619 

620 self._spec_version = (version, version_specifier) 

621 return version_specifier 

622 

623 def _require_spec_version(self, version: str) -> Version: 

624 """Get spec version, asserting it's valid (not for === operator). 

625 

626 This method should only be called for operators where version 

627 strings are guaranteed to be valid PEP 440 versions (not ===). 

628 """ 

629 spec_version = self._get_spec_version(version) 

630 assert spec_version is not None 

631 return spec_version 

632 

633 def _to_ranges(self) -> Sequence[_VersionRange]: 

634 """Convert this specifier to sorted, non-overlapping version ranges. 

635 

636 Each standard operator maps to one or two ranges. ``===`` is 

637 modeled as full range (actual check done separately). Cached. 

638 """ 

639 if self._ranges is not None: 

640 return self._ranges 

641 

642 op = self.operator 

643 ver_str = self.version 

644 

645 if op == "===": 

646 self._ranges = _FULL_RANGE 

647 return _FULL_RANGE 

648 

649 if ver_str.endswith(".*"): 

650 result = self._wildcard_ranges(op, ver_str) 

651 else: 

652 result = self._standard_ranges(op, ver_str) 

653 

654 self._ranges = result 

655 return result 

656 

657 def _wildcard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]: 

658 # ==1.2.* -> [1.2.dev0, 1.3.dev0); !=1.2.* -> complement. 

659 base = self._require_spec_version(ver_str[:-2]) 

660 lower = _base_dev0(base) 

661 upper = _next_prefix_dev0(base) 

662 if op == "==": 

663 return [(_LowerBound(lower, True), _UpperBound(upper, False))] 

664 # != 

665 return [ 

666 (_NEG_INF, _UpperBound(lower, False)), 

667 (_LowerBound(upper, True), _POS_INF), 

668 ] 

669 

670 def _standard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]: 

671 v = self._require_spec_version(ver_str) 

672 

673 if op == ">=": 

674 return [(_LowerBound(v, True), _POS_INF)] 

675 

676 if op == "<=": 

677 return [ 

678 ( 

679 _NEG_INF, 

680 _UpperBound(_BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS), True), 

681 ) 

682 ] 

683 

684 if op == ">": 

685 if v.dev is not None: 

686 # >V.devN: dev versions have no post-releases, so the 

687 # next real version is V.dev(N+1). 

688 lower_ver = v.__replace__(dev=v.dev + 1, local=None) 

689 return [(_LowerBound(lower_ver, True), _POS_INF)] 

690 if v.post is not None: 

691 # >V.postN: next real version is V.post(N+1).dev0. 

692 lower_ver = v.__replace__(post=v.post + 1, dev=0, local=None) 

693 return [(_LowerBound(lower_ver, True), _POS_INF)] 

694 # >V (final or pre-release): skip V+local and all V.postN. 

695 return [ 

696 ( 

697 _LowerBound(_BoundaryVersion(v, _BoundaryKind.AFTER_POSTS), False), 

698 _POS_INF, 

699 ) 

700 ] 

701 

702 if op == "<": 

703 # <V excludes prereleases of V when V is not a prerelease. 

704 # V.dev0 is the earliest prerelease of V (final, post, etc.). 

705 bound = v if v.is_prerelease else v.__replace__(dev=0, local=None) 

706 if bound <= _MIN_VERSION: 

707 return [] 

708 return [(_NEG_INF, _UpperBound(bound, False))] 

709 

710 # ==, !=: local versions of V match when spec has no local segment. 

711 has_local = "+" in ver_str 

712 after_locals = _BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS) 

713 upper = v if has_local else after_locals 

714 

715 if op == "==": 

716 return [(_LowerBound(v, True), _UpperBound(upper, True))] 

717 

718 if op == "!=": 

719 return [ 

720 (_NEG_INF, _UpperBound(v, False)), 

721 (_LowerBound(upper, False), _POS_INF), 

722 ] 

723 

724 if op == "~=": 

725 prefix = v.__replace__(release=v.release[:-1]) 

726 return [ 

727 (_LowerBound(v, True), _UpperBound(_next_prefix_dev0(prefix), False)) 

728 ] 

729 

730 raise ValueError(f"Unknown operator: {op!r}") # pragma: no cover 

731 

732 @property 

733 def prereleases(self) -> bool | None: 

734 # If there is an explicit prereleases set for this, then we'll just 

735 # blindly use that. 

736 if self._prereleases is not None: 

737 return self._prereleases 

738 

739 # Only the "!=" operator does not imply prereleases when 

740 # the version in the specifier is a prerelease. 

741 operator, version_str = self._spec 

742 if operator == "!=": 

743 return False 

744 

745 # The == specifier with trailing .* cannot include prereleases 

746 # e.g. "==1.0a1.*" is not valid. 

747 if operator == "==" and version_str.endswith(".*"): 

748 return False 

749 

750 # "===" can have arbitrary string versions, so we cannot parse 

751 # those, we take prereleases as unknown (None) for those. 

752 version = self._get_spec_version(version_str) 

753 if version is None: 

754 return None 

755 

756 # For all other operators, use the check if spec Version 

757 # object implies pre-releases. 

758 return version.is_prerelease 

759 

760 @prereleases.setter 

761 def prereleases(self, value: bool | None) -> None: 

762 self._prereleases = value 

763 

764 def __getstate__(self) -> tuple[tuple[str, str], bool | None]: 

765 # Return state as a 2-item tuple for compactness: 

766 # ((operator, version), prereleases) 

767 # Cache members are excluded and will be recomputed on demand. 

768 return (self._spec, self._prereleases) 

769 

770 def __setstate__(self, state: object) -> None: 

771 # Always discard cached values - they will be recomputed on demand. 

772 self._spec_version = None 

773 self._wildcard_split = None 

774 self._ranges = None 

775 

776 if isinstance(state, tuple): 

777 if len(state) == 2: 

778 # New format (26.2+): ((operator, version), prereleases) 

779 spec, prereleases = state 

780 if _validate_spec(spec) and _validate_pre(prereleases): 

781 self._spec = spec 

782 self._prereleases = prereleases 

783 return 

784 if len(state) == 2 and isinstance(state[1], dict): 

785 # Format (packaging 26.0-26.1): (None, {slot: value}). 

786 _, slot_dict = state 

787 spec = slot_dict.get("_spec") 

788 prereleases = slot_dict.get("_prereleases", "invalid") 

789 if _validate_spec(spec) and _validate_pre(prereleases): 

790 self._spec = spec 

791 self._prereleases = prereleases 

792 return 

793 if isinstance(state, dict): 

794 # Old format (packaging <= 25.x, no __slots__): state is a plain dict. 

795 spec = state.get("_spec") 

796 prereleases = state.get("_prereleases", "invalid") 

797 if _validate_spec(spec) and _validate_pre(prereleases): 

798 self._spec = spec 

799 self._prereleases = prereleases 

800 return 

801 

802 raise TypeError(f"Cannot restore Specifier from {state!r}") 

803 

804 @property 

805 def operator(self) -> str: 

806 """The operator of this specifier. 

807 

808 >>> Specifier("==1.2.3").operator 

809 '==' 

810 """ 

811 return self._spec[0] 

812 

813 @property 

814 def version(self) -> str: 

815 """The version of this specifier. 

816 

817 >>> Specifier("==1.2.3").version 

818 '1.2.3' 

819 """ 

820 return self._spec[1] 

821 

822 def __repr__(self) -> str: 

823 """A representation of the Specifier that shows all internal state. 

824 

825 >>> Specifier('>=1.0.0') 

826 <Specifier('>=1.0.0')> 

827 >>> Specifier('>=1.0.0', prereleases=False) 

828 <Specifier('>=1.0.0', prereleases=False)> 

829 >>> Specifier('>=1.0.0', prereleases=True) 

830 <Specifier('>=1.0.0', prereleases=True)> 

831 """ 

832 pre = ( 

833 f", prereleases={self.prereleases!r}" 

834 if self._prereleases is not None 

835 else "" 

836 ) 

837 

838 return f"<{self.__class__.__name__}({str(self)!r}{pre})>" 

839 

840 def __str__(self) -> str: 

841 """A string representation of the Specifier that can be round-tripped. 

842 

843 >>> str(Specifier('>=1.0.0')) 

844 '>=1.0.0' 

845 >>> str(Specifier('>=1.0.0', prereleases=False)) 

846 '>=1.0.0' 

847 """ 

848 return "{}{}".format(*self._spec) 

849 

850 @property 

851 def _canonical_spec(self) -> tuple[str, str]: 

852 operator, version = self._spec 

853 if operator == "===" or version.endswith(".*"): 

854 return operator, version 

855 

856 spec_version = self._require_spec_version(version) 

857 

858 canonical_version = canonicalize_version( 

859 spec_version, strip_trailing_zero=(operator != "~=") 

860 ) 

861 

862 return operator, canonical_version 

863 

864 def __hash__(self) -> int: 

865 return hash(self._canonical_spec) 

866 

867 def __eq__(self, other: object) -> bool: 

868 """Whether or not the two Specifier-like objects are equal. 

869 

870 :param other: The other object to check against. 

871 

872 The value of :attr:`prereleases` is ignored. 

873 

874 >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") 

875 True 

876 >>> (Specifier("==1.2.3", prereleases=False) == 

877 ... Specifier("==1.2.3", prereleases=True)) 

878 True 

879 >>> Specifier("==1.2.3") == "==1.2.3" 

880 True 

881 >>> Specifier("==1.2.3") == Specifier("==1.2.4") 

882 False 

883 >>> Specifier("==1.2.3") == Specifier("~=1.2.3") 

884 False 

885 """ 

886 if isinstance(other, str): 

887 try: 

888 other = self.__class__(str(other)) 

889 except InvalidSpecifier: 

890 return NotImplemented 

891 elif not isinstance(other, self.__class__): 

892 return NotImplemented 

893 

894 return self._canonical_spec == other._canonical_spec 

895 

896 def _get_operator(self, op: str) -> CallableOperator: 

897 operator_callable: CallableOperator = getattr( 

898 self, f"_compare_{self._operators[op]}" 

899 ) 

900 return operator_callable 

901 

902 def _compare_compatible(self, prospective: Version, spec: str) -> bool: 

903 # Compatible releases have an equivalent combination of >= and ==. That 

904 # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to 

905 # implement this in terms of the other specifiers instead of 

906 # implementing it ourselves. The only thing we need to do is construct 

907 # the other specifiers. 

908 

909 # We want everything but the last item in the version, but we want to 

910 # ignore suffix segments. 

911 prefix = _version_join( 

912 list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] 

913 ) 

914 

915 # Add the prefix notation to the end of our string 

916 prefix += ".*" 

917 

918 return (self._compare_greater_than_equal(prospective, spec)) and ( 

919 self._compare_equal(prospective, prefix) 

920 ) 

921 

922 def _get_wildcard_split(self, spec: str) -> tuple[list[str], int]: 

923 """Cached split of a wildcard spec into components and numeric length. 

924 

925 >>> Specifier("==1.*")._get_wildcard_split("1.*") 

926 (['0', '1'], 2) 

927 >>> Specifier("==3.10.*")._get_wildcard_split("3.10.*") 

928 (['0', '3', '10'], 3) 

929 """ 

930 wildcard_split = self._wildcard_split 

931 if wildcard_split is None: 

932 normalized = canonicalize_version(spec[:-2], strip_trailing_zero=False) 

933 split_spec = _version_split(normalized) 

934 wildcard_split = (split_spec, _numeric_prefix_len(split_spec)) 

935 self._wildcard_split = wildcard_split 

936 return wildcard_split 

937 

938 def _compare_equal(self, prospective: Version, spec: str) -> bool: 

939 # We need special logic to handle prefix matching 

940 if spec.endswith(".*"): 

941 split_spec, spec_numeric_len = self._get_wildcard_split(spec) 

942 

943 # In the case of prefix matching we want to ignore local segment. 

944 normalized_prospective = canonicalize_version( 

945 _public_version(prospective), strip_trailing_zero=False 

946 ) 

947 # Split the prospective version out by bangs and dots, and pretend 

948 # that there is an implicit dot in between a release segment and 

949 # a pre-release segment. 

950 split_prospective = _version_split(normalized_prospective) 

951 

952 # 0-pad the prospective version before shortening it to get the correct 

953 # shortened version. 

954 padded_prospective = _left_pad(split_prospective, spec_numeric_len) 

955 

956 # Shorten the prospective version to be the same length as the spec 

957 # so that we can determine if the specifier is a prefix of the 

958 # prospective version or not. 

959 shortened_prospective = padded_prospective[: len(split_spec)] 

960 

961 return shortened_prospective == split_spec 

962 else: 

963 # Convert our spec string into a Version 

964 spec_version = self._require_spec_version(spec) 

965 

966 # If the specifier does not have a local segment, then we want to 

967 # act as if the prospective version also does not have a local 

968 # segment. 

969 if not spec_version.local: 

970 prospective = _public_version(prospective) 

971 

972 return prospective == spec_version 

973 

974 def _compare_not_equal(self, prospective: Version, spec: str) -> bool: 

975 return not self._compare_equal(prospective, spec) 

976 

977 def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: 

978 # NB: Local version identifiers are NOT permitted in the version 

979 # specifier, so local version labels can be universally removed from 

980 # the prospective version. 

981 return _public_version(prospective) <= self._require_spec_version(spec) 

982 

983 def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: 

984 # NB: Local version identifiers are NOT permitted in the version 

985 # specifier, so local version labels can be universally removed from 

986 # the prospective version. 

987 return _public_version(prospective) >= self._require_spec_version(spec) 

988 

989 def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: 

990 # Convert our spec to a Version instance, since we'll want to work with 

991 # it as a version. 

992 spec = self._require_spec_version(spec_str) 

993 

994 # Check to see if the prospective version is less than the spec 

995 # version. If it's not we can short circuit and just return False now 

996 # instead of doing extra unneeded work. 

997 if not prospective < spec: 

998 return False 

999 

1000 # The spec says: "<V MUST NOT allow a pre-release of the specified 

1001 # version unless the specified version is itself a pre-release." 

1002 if ( 

1003 not spec.is_prerelease 

1004 and prospective.is_prerelease 

1005 and prospective >= _earliest_prerelease(spec) 

1006 ): 

1007 return False 

1008 

1009 # If we've gotten to here, it means that prospective version is both 

1010 # less than the spec version *and* it's not a pre-release of the same 

1011 # version in the spec. 

1012 return True 

1013 

1014 def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: 

1015 # Convert our spec to a Version instance, since we'll want to work with 

1016 # it as a version. 

1017 spec = self._require_spec_version(spec_str) 

1018 

1019 # Check to see if the prospective version is greater than the spec 

1020 # version. If it's not we can short circuit and just return False now 

1021 # instead of doing extra unneeded work. 

1022 if not prospective > spec: 

1023 return False 

1024 

1025 # The spec says: ">V MUST NOT allow a post-release of the specified 

1026 # version unless the specified version is itself a post-release." 

1027 if ( 

1028 not spec.is_postrelease 

1029 and prospective.is_postrelease 

1030 and _post_base(prospective) == spec 

1031 ): 

1032 return False 

1033 

1034 # Per the spec: ">V MUST NOT match a local version of the specified 

1035 # version". A "local version of V" is any version whose public part 

1036 # equals V. So >1.0a1 must not match 1.0a1+local, but must still 

1037 # match 1.0a2+local. 

1038 if prospective.local is not None and _public_version(prospective) == spec: 

1039 return False 

1040 

1041 # If we've gotten to here, it means that prospective version is both 

1042 # greater than the spec version *and* it's not a pre-release of the 

1043 # same version in the spec. 

1044 return True 

1045 

1046 def _compare_arbitrary(self, prospective: Version | str, spec: str) -> bool: 

1047 return str(prospective).lower() == str(spec).lower() 

1048 

1049 def __contains__(self, item: str | Version) -> bool: 

1050 """Return whether or not the item is contained in this specifier. 

1051 

1052 :param item: The item to check for. 

1053 

1054 This is used for the ``in`` operator and behaves the same as 

1055 :meth:`contains` with no ``prereleases`` argument passed. 

1056 

1057 >>> "1.2.3" in Specifier(">=1.2.3") 

1058 True 

1059 >>> Version("1.2.3") in Specifier(">=1.2.3") 

1060 True 

1061 >>> "1.0.0" in Specifier(">=1.2.3") 

1062 False 

1063 >>> "1.3.0a1" in Specifier(">=1.2.3") 

1064 True 

1065 >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) 

1066 True 

1067 """ 

1068 return self.contains(item) 

1069 

1070 def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool: 

1071 """Return whether or not the item is contained in this specifier. 

1072 

1073 :param item: 

1074 The item to check for, which can be a version string or a 

1075 :class:`Version` instance. 

1076 :param prereleases: 

1077 Whether or not to match prereleases with this Specifier. If set to 

1078 ``None`` (the default), it will follow the recommendation from 

1079 :pep:`440` and match prereleases, as there are no other versions. 

1080 

1081 >>> Specifier(">=1.2.3").contains("1.2.3") 

1082 True 

1083 >>> Specifier(">=1.2.3").contains(Version("1.2.3")) 

1084 True 

1085 >>> Specifier(">=1.2.3").contains("1.0.0") 

1086 False 

1087 >>> Specifier(">=1.2.3").contains("1.3.0a1") 

1088 True 

1089 >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1") 

1090 False 

1091 >>> Specifier(">=1.2.3").contains("1.3.0a1") 

1092 True 

1093 """ 

1094 

1095 return bool(list(self.filter([item], prereleases=prereleases))) 

1096 

1097 @typing.overload 

1098 def filter( 

1099 self, 

1100 iterable: Iterable[UnparsedVersionVar], 

1101 prereleases: bool | None = None, 

1102 key: None = ..., 

1103 ) -> Iterator[UnparsedVersionVar]: ... 

1104 

1105 @typing.overload 

1106 def filter( 

1107 self, 

1108 iterable: Iterable[T], 

1109 prereleases: bool | None = None, 

1110 key: Callable[[T], UnparsedVersion] = ..., 

1111 ) -> Iterator[T]: ... 

1112 

1113 def filter( 

1114 self, 

1115 iterable: Iterable[Any], 

1116 prereleases: bool | None = None, 

1117 key: Callable[[Any], UnparsedVersion] | None = None, 

1118 ) -> Iterator[Any]: 

1119 """Filter items in the given iterable, that match the specifier. 

1120 

1121 :param iterable: 

1122 An iterable that can contain version strings and :class:`Version` instances. 

1123 The items in the iterable will be filtered according to the specifier. 

1124 :param prereleases: 

1125 Whether or not to allow prereleases in the returned iterator. If set to 

1126 ``None`` (the default), it will follow the recommendation from :pep:`440` 

1127 and match prereleases if there are no other versions. 

1128 :param key: 

1129 A callable that takes a single argument (an item from the iterable) and 

1130 returns a version string or :class:`Version` instance to be used for 

1131 filtering. 

1132 

1133 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) 

1134 ['1.3'] 

1135 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) 

1136 ['1.2.3', '1.3', <Version('1.4')>] 

1137 >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) 

1138 ['1.5a1'] 

1139 >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) 

1140 ['1.3', '1.5a1'] 

1141 >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) 

1142 ['1.3', '1.5a1'] 

1143 >>> list(Specifier(">=1.2.3").filter( 

1144 ... [{"ver": "1.2"}, {"ver": "1.3"}], 

1145 ... key=lambda x: x["ver"])) 

1146 [{'ver': '1.3'}] 

1147 """ 

1148 prereleases_versions = [] 

1149 found_non_prereleases = False 

1150 

1151 # Determine if to include prereleases by default 

1152 include_prereleases = ( 

1153 prereleases if prereleases is not None else self.prereleases 

1154 ) 

1155 

1156 # Get the matching operator 

1157 operator_callable = self._get_operator(self.operator) 

1158 

1159 # Filter versions 

1160 for version in iterable: 

1161 parsed_version = _coerce_version(version if key is None else key(version)) 

1162 match = False 

1163 if parsed_version is None: 

1164 # === operator can match arbitrary (non-version) strings 

1165 if self.operator == "===" and self._compare_arbitrary( 

1166 version, self.version 

1167 ): 

1168 yield version 

1169 elif self.operator == "===": 

1170 match = self._compare_arbitrary( 

1171 version if key is None else key(version), self.version 

1172 ) 

1173 else: 

1174 match = operator_callable(parsed_version, self.version) 

1175 

1176 if match and parsed_version is not None: 

1177 # If it's not a prerelease or prereleases are allowed, yield it directly 

1178 if not parsed_version.is_prerelease or include_prereleases: 

1179 found_non_prereleases = True 

1180 yield version 

1181 # Otherwise collect prereleases for potential later use 

1182 elif prereleases is None and self._prereleases is not False: 

1183 prereleases_versions.append(version) 

1184 

1185 # If no non-prereleases were found and prereleases weren't 

1186 # explicitly forbidden, yield the collected prereleases 

1187 if ( 

1188 not found_non_prereleases 

1189 and prereleases is None 

1190 and self._prereleases is not False 

1191 ): 

1192 yield from prereleases_versions 

1193 

1194 

1195_prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)") 

1196 

1197 

1198def _pep440_filter_prereleases( 

1199 iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None 

1200) -> Iterator[Any]: 

1201 """Filter per PEP 440: exclude prereleases unless no finals exist.""" 

1202 # Two lists used: 

1203 # * all_nonfinal to preserve order if no finals exist 

1204 # * arbitrary_strings for streaming when first final found 

1205 all_nonfinal: list[Any] = [] 

1206 arbitrary_strings: list[Any] = [] 

1207 

1208 found_final = False 

1209 for item in iterable: 

1210 parsed = _coerce_version(item if key is None else key(item)) 

1211 

1212 if parsed is None: 

1213 # Arbitrary strings are always included as it is not 

1214 # possible to determine if they are prereleases, 

1215 # and they have already passed all specifiers. 

1216 if found_final: 

1217 yield item 

1218 else: 

1219 arbitrary_strings.append(item) 

1220 all_nonfinal.append(item) 

1221 continue 

1222 

1223 if not parsed.is_prerelease: 

1224 # Final release found - flush arbitrary strings, then yield 

1225 if not found_final: 

1226 yield from arbitrary_strings 

1227 found_final = True 

1228 yield item 

1229 continue 

1230 

1231 # Prerelease - buffer if no finals yet, otherwise skip 

1232 if not found_final: 

1233 all_nonfinal.append(item) 

1234 

1235 # No finals found - yield all buffered items 

1236 if not found_final: 

1237 yield from all_nonfinal 

1238 

1239 

1240def _version_split(version: str) -> list[str]: 

1241 """Split version into components. 

1242 

1243 The split components are intended for version comparison. The logic does 

1244 not attempt to retain the original version string, so joining the 

1245 components back with :func:`_version_join` may not produce the original 

1246 version string. 

1247 """ 

1248 result: list[str] = [] 

1249 

1250 epoch, _, rest = version.rpartition("!") 

1251 result.append(epoch or "0") 

1252 

1253 for item in rest.split("."): 

1254 match = _prefix_regex.fullmatch(item) 

1255 if match: 

1256 result.extend(match.groups()) 

1257 else: 

1258 result.append(item) 

1259 return result 

1260 

1261 

1262def _version_join(components: list[str]) -> str: 

1263 """Join split version components into a version string. 

1264 

1265 This function assumes the input came from :func:`_version_split`, where the 

1266 first component must be the epoch (either empty or numeric), and all other 

1267 components numeric. 

1268 """ 

1269 epoch, *rest = components 

1270 return f"{epoch}!{'.'.join(rest)}" 

1271 

1272 

1273def _is_not_suffix(segment: str) -> bool: 

1274 return not any( 

1275 segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") 

1276 ) 

1277 

1278 

1279def _numeric_prefix_len(split: list[str]) -> int: 

1280 """Count leading numeric components in a :func:`_version_split` result. 

1281 

1282 >>> _numeric_prefix_len(["0", "1", "2", "a1"]) 

1283 3 

1284 """ 

1285 count = 0 

1286 for segment in split: 

1287 if not segment.isdigit(): 

1288 break 

1289 count += 1 

1290 return count 

1291 

1292 

1293def _left_pad(split: list[str], target_numeric_len: int) -> list[str]: 

1294 """Pad a :func:`_version_split` result with ``"0"`` segments to reach 

1295 ``target_numeric_len`` numeric components. Suffix segments are preserved. 

1296 

1297 >>> _left_pad(["0", "1", "a1"], 4) 

1298 ['0', '1', '0', '0', 'a1'] 

1299 """ 

1300 numeric_len = _numeric_prefix_len(split) 

1301 pad_needed = target_numeric_len - numeric_len 

1302 if pad_needed <= 0: 

1303 return split 

1304 return [*split[:numeric_len], *(["0"] * pad_needed), *split[numeric_len:]] 

1305 

1306 

1307def _operator_cost(op_entry: tuple[CallableOperator, str, str]) -> int: 

1308 """Sort key for Cost Based Ordering of specifier operators in _filter_versions. 

1309 

1310 Operators run sequentially on a shrinking candidate set, so operators that 

1311 reject the most versions should run first to minimize work for later ones. 

1312 

1313 Tier 0: Exact equality (==, ===), likely to narrow candidates to one version 

1314 Tier 1: Range checks (>=, <=, >, <), cheap and usually reject a large portion 

1315 Tier 2: Wildcard equality (==.*) and compatible release (~=), more expensive 

1316 Tier 3: Exact !=, cheap but rarely rejects 

1317 Tier 4: Wildcard !=.*, expensive and rarely rejects 

1318 """ 

1319 _, ver, op = op_entry 

1320 if op == "==": 

1321 return 0 if not ver.endswith(".*") else 2 

1322 if op in (">=", "<=", ">", "<"): 

1323 return 1 

1324 if op == "~=": 

1325 return 2 

1326 if op == "!=": 

1327 return 3 if not ver.endswith(".*") else 4 

1328 if op == "===": 

1329 return 0 

1330 

1331 raise ValueError(f"Unknown operator: {op!r}") # pragma: no cover 

1332 

1333 

1334class SpecifierSet(BaseSpecifier): 

1335 """This class abstracts handling of a set of version specifiers. 

1336 

1337 It can be passed a single specifier (``>=3.0``), a comma-separated list of 

1338 specifiers (``>=3.0,!=3.1``), or no specifier at all. 

1339 

1340 Instances are safe to serialize with :mod:`pickle`. They use a stable 

1341 format so the same pickle can be loaded in future packaging 

1342 releases. 

1343 

1344 .. versionchanged:: 26.2 

1345 

1346 Added a stable pickle format. Pickles created with 

1347 packaging 26.2+ can be unpickled with future releases. 

1348 Backward compatibility with pickles from 

1349 packaging < 26.2 is supported but may be removed in a future 

1350 release. 

1351 """ 

1352 

1353 __slots__ = ( 

1354 "_canonicalized", 

1355 "_has_arbitrary", 

1356 "_is_unsatisfiable", 

1357 "_prereleases", 

1358 "_resolved_ops", 

1359 "_specs", 

1360 ) 

1361 

1362 def __init__( 

1363 self, 

1364 specifiers: str | Iterable[Specifier] = "", 

1365 prereleases: bool | None = None, 

1366 ) -> None: 

1367 """Initialize a SpecifierSet instance. 

1368 

1369 :param specifiers: 

1370 The string representation of a specifier or a comma-separated list of 

1371 specifiers which will be parsed and normalized before use. 

1372 May also be an iterable of ``Specifier`` instances, which will be used 

1373 as is. 

1374 :param prereleases: 

1375 This tells the SpecifierSet if it should accept prerelease versions if 

1376 applicable or not. The default of ``None`` will autodetect it from the 

1377 given specifiers. 

1378 

1379 :raises InvalidSpecifier: 

1380 If the given ``specifiers`` are not parseable than this exception will be 

1381 raised. 

1382 """ 

1383 

1384 if isinstance(specifiers, str): 

1385 # Split on `,` to break each individual specifier into its own item, and 

1386 # strip each item to remove leading/trailing whitespace. 

1387 split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] 

1388 

1389 self._specs: tuple[Specifier, ...] = tuple(map(Specifier, split_specifiers)) 

1390 # Fast substring check; avoids iterating parsed specs. 

1391 self._has_arbitrary = "===" in specifiers 

1392 else: 

1393 self._specs = tuple(specifiers) 

1394 # Substring check works for both Specifier objects and plain 

1395 # strings (setuptools passes lists of strings). 

1396 self._has_arbitrary = any("===" in str(s) for s in self._specs) 

1397 

1398 self._canonicalized = len(self._specs) <= 1 

1399 self._resolved_ops: list[tuple[CallableOperator, str, str]] | None = None 

1400 

1401 # Store our prereleases value so we can use it later to determine if 

1402 # we accept prereleases or not. 

1403 self._prereleases = prereleases 

1404 

1405 self._is_unsatisfiable: bool | None = None 

1406 

1407 def _canonical_specs(self) -> tuple[Specifier, ...]: 

1408 """Deduplicate, sort, and cache specs for order-sensitive operations.""" 

1409 if not self._canonicalized: 

1410 self._specs = tuple(dict.fromkeys(sorted(self._specs, key=str))) 

1411 self._canonicalized = True 

1412 self._resolved_ops = None 

1413 self._is_unsatisfiable = None 

1414 return self._specs 

1415 

1416 @property 

1417 def prereleases(self) -> bool | None: 

1418 # If we have been given an explicit prerelease modifier, then we'll 

1419 # pass that through here. 

1420 if self._prereleases is not None: 

1421 return self._prereleases 

1422 

1423 # If we don't have any specifiers, and we don't have a forced value, 

1424 # then we'll just return None since we don't know if this should have 

1425 # pre-releases or not. 

1426 if not self._specs: 

1427 return None 

1428 

1429 # Otherwise we'll see if any of the given specifiers accept 

1430 # prereleases, if any of them do we'll return True, otherwise False. 

1431 if any(s.prereleases for s in self._specs): 

1432 return True 

1433 

1434 return None 

1435 

1436 @prereleases.setter 

1437 def prereleases(self, value: bool | None) -> None: 

1438 self._prereleases = value 

1439 self._is_unsatisfiable = None 

1440 

1441 def __getstate__(self) -> tuple[tuple[Specifier, ...], bool | None]: 

1442 # Return state as a 2-item tuple for compactness: 

1443 # (specs, prereleases) 

1444 # Cache members are excluded and will be recomputed on demand. 

1445 return (self._specs, self._prereleases) 

1446 

1447 def __setstate__(self, state: object) -> None: 

1448 # Always discard cached values - they will be recomputed on demand. 

1449 self._resolved_ops = None 

1450 self._is_unsatisfiable = None 

1451 

1452 if isinstance(state, tuple): 

1453 if len(state) == 2: 

1454 # New format (26.2+): (specs, prereleases) 

1455 specs, prereleases = state 

1456 if ( 

1457 isinstance(specs, tuple) 

1458 and all(isinstance(s, Specifier) for s in specs) 

1459 and _validate_pre(prereleases) 

1460 ): 

1461 self._specs = specs 

1462 self._prereleases = prereleases 

1463 self._canonicalized = len(specs) <= 1 

1464 self._has_arbitrary = any("===" in str(s) for s in specs) 

1465 return 

1466 if len(state) == 2 and isinstance(state[1], dict): 

1467 # Format (packaging 26.0-26.1): (None, {slot: value}). 

1468 _, slot_dict = state 

1469 specs = slot_dict.get("_specs", ()) 

1470 prereleases = slot_dict.get("_prereleases") 

1471 # Convert frozenset to tuple (26.0 stored as frozenset) 

1472 if isinstance(specs, frozenset): 

1473 specs = tuple(sorted(specs, key=str)) 

1474 if ( 

1475 isinstance(specs, tuple) 

1476 and all(isinstance(s, Specifier) for s in specs) 

1477 and _validate_pre(prereleases) 

1478 ): 

1479 self._specs = specs 

1480 self._prereleases = prereleases 

1481 self._canonicalized = len(self._specs) <= 1 

1482 self._has_arbitrary = any("===" in str(s) for s in self._specs) 

1483 return 

1484 if isinstance(state, dict): 

1485 # Old format (packaging <= 25.x, no __slots__): state is a plain dict. 

1486 specs = state.get("_specs", ()) 

1487 prereleases = state.get("_prereleases") 

1488 # Convert frozenset to tuple (26.0 stored as frozenset) 

1489 if isinstance(specs, frozenset): 

1490 specs = tuple(sorted(specs, key=str)) 

1491 if ( 

1492 isinstance(specs, tuple) 

1493 and all(isinstance(s, Specifier) for s in specs) 

1494 and _validate_pre(prereleases) 

1495 ): 

1496 self._specs = specs 

1497 self._prereleases = prereleases 

1498 self._canonicalized = len(self._specs) <= 1 

1499 self._has_arbitrary = any("===" in str(s) for s in self._specs) 

1500 return 

1501 

1502 raise TypeError(f"Cannot restore SpecifierSet from {state!r}") 

1503 

1504 def __repr__(self) -> str: 

1505 """A representation of the specifier set that shows all internal state. 

1506 

1507 Note that the ordering of the individual specifiers within the set may not 

1508 match the input string. 

1509 

1510 >>> SpecifierSet('>=1.0.0,!=2.0.0') 

1511 <SpecifierSet('!=2.0.0,>=1.0.0')> 

1512 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) 

1513 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=False)> 

1514 >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) 

1515 <SpecifierSet('!=2.0.0,>=1.0.0', prereleases=True)> 

1516 """ 

1517 pre = ( 

1518 f", prereleases={self.prereleases!r}" 

1519 if self._prereleases is not None 

1520 else "" 

1521 ) 

1522 

1523 return f"<{self.__class__.__name__}({str(self)!r}{pre})>" 

1524 

1525 def __str__(self) -> str: 

1526 """A string representation of the specifier set that can be round-tripped. 

1527 

1528 Note that the ordering of the individual specifiers within the set may not 

1529 match the input string. 

1530 

1531 >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) 

1532 '!=1.0.1,>=1.0.0' 

1533 >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) 

1534 '!=1.0.1,>=1.0.0' 

1535 """ 

1536 return ",".join(str(s) for s in self._canonical_specs()) 

1537 

1538 def __hash__(self) -> int: 

1539 return hash(self._canonical_specs()) 

1540 

1541 def __and__(self, other: SpecifierSet | str) -> SpecifierSet: 

1542 """Return a SpecifierSet which is a combination of the two sets. 

1543 

1544 :param other: The other object to combine with. 

1545 

1546 >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' 

1547 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')> 

1548 >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') 

1549 <SpecifierSet('!=1.0.1,!=2.0.1,<=2.0.0,>=1.0.0')> 

1550 """ 

1551 if isinstance(other, str): 

1552 other = SpecifierSet(other) 

1553 elif not isinstance(other, SpecifierSet): 

1554 return NotImplemented 

1555 

1556 specifier = SpecifierSet() 

1557 specifier._specs = self._specs + other._specs 

1558 specifier._canonicalized = len(specifier._specs) <= 1 

1559 specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary 

1560 specifier._resolved_ops = None 

1561 

1562 # Combine prerelease settings: use common or non-None value 

1563 if self._prereleases is None or self._prereleases == other._prereleases: 

1564 specifier._prereleases = other._prereleases 

1565 elif other._prereleases is None: 

1566 specifier._prereleases = self._prereleases 

1567 else: 

1568 raise ValueError( 

1569 "Cannot combine SpecifierSets with True and False prerelease overrides." 

1570 ) 

1571 

1572 return specifier 

1573 

1574 def __eq__(self, other: object) -> bool: 

1575 """Whether or not the two SpecifierSet-like objects are equal. 

1576 

1577 :param other: The other object to check against. 

1578 

1579 The value of :attr:`prereleases` is ignored. 

1580 

1581 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") 

1582 True 

1583 >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == 

1584 ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) 

1585 True 

1586 >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" 

1587 True 

1588 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") 

1589 False 

1590 >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") 

1591 False 

1592 """ 

1593 if isinstance(other, (str, Specifier)): 

1594 other = SpecifierSet(str(other)) 

1595 elif not isinstance(other, SpecifierSet): 

1596 return NotImplemented 

1597 

1598 return self._canonical_specs() == other._canonical_specs() 

1599 

1600 def __len__(self) -> int: 

1601 """Returns the number of specifiers in this specifier set.""" 

1602 return len(self._specs) 

1603 

1604 def __iter__(self) -> Iterator[Specifier]: 

1605 """ 

1606 Returns an iterator over all the underlying :class:`Specifier` instances 

1607 in this specifier set. 

1608 

1609 >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) 

1610 [<Specifier('!=1.0.1')>, <Specifier('>=1.0.0')>] 

1611 """ 

1612 return iter(self._specs) 

1613 

1614 def _get_ranges(self) -> Sequence[_VersionRange]: 

1615 """Intersect all specifiers into a single list of version ranges. 

1616 

1617 Returns an empty list when unsatisfiable. ``===`` specs are 

1618 modeled as full range; string matching is checked separately 

1619 by :meth:`_check_arbitrary_unsatisfiable`. 

1620 """ 

1621 specs = self._specs 

1622 

1623 result: Sequence[_VersionRange] | None = None 

1624 for s in specs: 

1625 if result is None: 

1626 result = s._to_ranges() 

1627 else: 

1628 result = _intersect_ranges(result, s._to_ranges()) 

1629 if not result: 

1630 break 

1631 

1632 if result is None: # pragma: no cover 

1633 raise RuntimeError("_get_ranges called with no specs") 

1634 return result 

1635 

1636 def is_unsatisfiable(self) -> bool: 

1637 """Check whether this specifier set can never be satisfied. 

1638 

1639 Returns True if no version can satisfy all specifiers simultaneously. 

1640 

1641 >>> SpecifierSet(">=2.0,<1.0").is_unsatisfiable() 

1642 True 

1643 >>> SpecifierSet(">=1.0,<2.0").is_unsatisfiable() 

1644 False 

1645 >>> SpecifierSet("").is_unsatisfiable() 

1646 False 

1647 >>> SpecifierSet("==1.0,!=1.0").is_unsatisfiable() 

1648 True 

1649 """ 

1650 cached = self._is_unsatisfiable 

1651 if cached is not None: 

1652 return cached 

1653 

1654 if not self._specs: 

1655 self._is_unsatisfiable = False 

1656 return False 

1657 

1658 result = not self._get_ranges() 

1659 

1660 if not result: 

1661 result = self._check_arbitrary_unsatisfiable() 

1662 

1663 if not result and self.prereleases is False: 

1664 result = self._check_prerelease_only_ranges() 

1665 

1666 self._is_unsatisfiable = result 

1667 return result 

1668 

1669 def _check_prerelease_only_ranges(self) -> bool: 

1670 """With prereleases=False, check if every range contains only 

1671 pre-release versions (which would be excluded from matching).""" 

1672 for lower, upper in self._get_ranges(): 

1673 nearest = _nearest_non_prerelease(lower.version) 

1674 if nearest is None: 

1675 return False 

1676 if upper.version is None or nearest < upper.version: 

1677 return False 

1678 if nearest == upper.version and upper.inclusive: 

1679 return False 

1680 return True 

1681 

1682 def _check_arbitrary_unsatisfiable(self) -> bool: 

1683 """Check === (arbitrary equality) specs for unsatisfiability. 

1684 

1685 === uses case-insensitive string comparison, so the only candidate 

1686 that can match ``===V`` is the literal string V. This method 

1687 checks whether that candidate is excluded by other specifiers. 

1688 """ 

1689 arbitrary = [s for s in self._specs if s.operator == "==="] 

1690 if not arbitrary: 

1691 return False 

1692 

1693 # Multiple === must agree on the same string (case-insensitive). 

1694 first = arbitrary[0].version.lower() 

1695 if any(s.version.lower() != first for s in arbitrary[1:]): 

1696 return True 

1697 

1698 # The sole candidate is the === version string. Check whether 

1699 # it can satisfy every standard spec. 

1700 candidate = _coerce_version(arbitrary[0].version) 

1701 

1702 # With prereleases=False, a prerelease candidate is excluded 

1703 # by contains() before the === string check even runs. 

1704 if ( 

1705 self.prereleases is False 

1706 and candidate is not None 

1707 and candidate.is_prerelease 

1708 ): 

1709 return True 

1710 

1711 standard = [s for s in self._specs if s.operator != "==="] 

1712 if not standard: 

1713 return False 

1714 

1715 if candidate is None: 

1716 # Unparsable string cannot satisfy any standard spec. 

1717 return True 

1718 

1719 return not all(s.contains(candidate) for s in standard) 

1720 

1721 def __contains__(self, item: UnparsedVersion) -> bool: 

1722 """Return whether or not the item is contained in this specifier. 

1723 

1724 :param item: The item to check for. 

1725 

1726 This is used for the ``in`` operator and behaves the same as 

1727 :meth:`contains` with no ``prereleases`` argument passed. 

1728 

1729 >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") 

1730 True 

1731 >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") 

1732 True 

1733 >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") 

1734 False 

1735 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") 

1736 True 

1737 >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) 

1738 True 

1739 """ 

1740 return self.contains(item) 

1741 

1742 def contains( 

1743 self, 

1744 item: UnparsedVersion, 

1745 prereleases: bool | None = None, 

1746 installed: bool | None = None, 

1747 ) -> bool: 

1748 """Return whether or not the item is contained in this SpecifierSet. 

1749 

1750 :param item: 

1751 The item to check for, which can be a version string or a 

1752 :class:`Version` instance. 

1753 :param prereleases: 

1754 Whether or not to match prereleases with this SpecifierSet. If set to 

1755 ``None`` (the default), it will follow the recommendation from :pep:`440` 

1756 and match prereleases, as there are no other versions. 

1757 :param installed: 

1758 Whether or not the item is installed. If set to ``True``, it will 

1759 accept prerelease versions even if the specifier does not allow them. 

1760 

1761 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") 

1762 True 

1763 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) 

1764 True 

1765 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") 

1766 False 

1767 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") 

1768 True 

1769 >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1") 

1770 False 

1771 >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) 

1772 True 

1773 """ 

1774 version = _coerce_version(item) 

1775 

1776 if version is not None and installed and version.is_prerelease: 

1777 prereleases = True 

1778 

1779 # When item is a string and === is involved, keep it as-is 

1780 # so the comparison isn't done against the normalized form. 

1781 if version is None or (self._has_arbitrary and not isinstance(item, Version)): 

1782 check_item = item 

1783 else: 

1784 check_item = version 

1785 return bool(list(self.filter([check_item], prereleases=prereleases))) 

1786 

1787 @typing.overload 

1788 def filter( 

1789 self, 

1790 iterable: Iterable[UnparsedVersionVar], 

1791 prereleases: bool | None = None, 

1792 key: None = ..., 

1793 ) -> Iterator[UnparsedVersionVar]: ... 

1794 

1795 @typing.overload 

1796 def filter( 

1797 self, 

1798 iterable: Iterable[T], 

1799 prereleases: bool | None = None, 

1800 key: Callable[[T], UnparsedVersion] = ..., 

1801 ) -> Iterator[T]: ... 

1802 

1803 def filter( 

1804 self, 

1805 iterable: Iterable[Any], 

1806 prereleases: bool | None = None, 

1807 key: Callable[[Any], UnparsedVersion] | None = None, 

1808 ) -> Iterator[Any]: 

1809 """Filter items in the given iterable, that match the specifiers in this set. 

1810 

1811 :param iterable: 

1812 An iterable that can contain version strings and :class:`Version` instances. 

1813 The items in the iterable will be filtered according to the specifier. 

1814 :param prereleases: 

1815 Whether or not to allow prereleases in the returned iterator. If set to 

1816 ``None`` (the default), it will follow the recommendation from :pep:`440` 

1817 and match prereleases if there are no other versions. 

1818 :param key: 

1819 A callable that takes a single argument (an item from the iterable) and 

1820 returns a version string or :class:`Version` instance to be used for 

1821 filtering. 

1822 

1823 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) 

1824 ['1.3'] 

1825 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) 

1826 ['1.3', <Version('1.4')>] 

1827 >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) 

1828 ['1.5a1'] 

1829 >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) 

1830 ['1.3', '1.5a1'] 

1831 >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) 

1832 ['1.3', '1.5a1'] 

1833 >>> list(SpecifierSet(">=1.2.3").filter( 

1834 ... [{"ver": "1.2"}, {"ver": "1.3"}], 

1835 ... key=lambda x: x["ver"])) 

1836 [{'ver': '1.3'}] 

1837 

1838 An "empty" SpecifierSet will filter items based on the presence of prerelease 

1839 versions in the set. 

1840 

1841 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) 

1842 ['1.3'] 

1843 >>> list(SpecifierSet("").filter(["1.5a1"])) 

1844 ['1.5a1'] 

1845 >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) 

1846 ['1.3', '1.5a1'] 

1847 >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) 

1848 ['1.3', '1.5a1'] 

1849 """ 

1850 # Determine if we're forcing a prerelease or not, if we're not forcing 

1851 # one for this particular filter call, then we'll use whatever the 

1852 # SpecifierSet thinks for whether or not we should support prereleases. 

1853 if prereleases is None and self.prereleases is not None: 

1854 prereleases = self.prereleases 

1855 

1856 # Filter versions that match all specifiers using Cost Based Ordering. 

1857 if self._specs: 

1858 # When prereleases is None, we need to let all versions through 

1859 # the individual filters, then decide about prereleases at the end 

1860 # based on whether any non-prereleases matched ALL specs. 

1861 

1862 # Fast path: single specifier, delegate directly. 

1863 if len(self._specs) == 1: 

1864 filtered = self._specs[0].filter( 

1865 iterable, 

1866 prereleases=True if prereleases is None else prereleases, 

1867 key=key, 

1868 ) 

1869 else: 

1870 filtered = self._filter_versions( 

1871 iterable, 

1872 key, 

1873 prereleases=True if prereleases is None else prereleases, 

1874 ) 

1875 

1876 if prereleases is not None: 

1877 return filtered 

1878 

1879 return _pep440_filter_prereleases(filtered, key) 

1880 

1881 # Handle Empty SpecifierSet. 

1882 if prereleases is True: 

1883 return iter(iterable) 

1884 

1885 if prereleases is False: 

1886 return ( 

1887 item 

1888 for item in iterable 

1889 if ( 

1890 (version := _coerce_version(item if key is None else key(item))) 

1891 is None 

1892 or not version.is_prerelease 

1893 ) 

1894 ) 

1895 

1896 # PEP 440: exclude prereleases unless no final releases matched 

1897 return _pep440_filter_prereleases(iterable, key) 

1898 

1899 def _filter_versions( 

1900 self, 

1901 iterable: Iterable[Any], 

1902 key: Callable[[Any], UnparsedVersion] | None, 

1903 prereleases: bool | None = None, 

1904 ) -> Iterator[Any]: 

1905 """Filter versions against all specifiers in a single pass. 

1906 

1907 Uses Cost Based Ordering: specifiers are sorted by _operator_cost so 

1908 that cheap range operators reject versions early, avoiding expensive 

1909 wildcard or compatible operators on versions that would have been 

1910 rejected anyway. 

1911 """ 

1912 # Pre-resolve operators and sort (cached after first call). 

1913 if self._resolved_ops is None: 

1914 self._resolved_ops = sorted( 

1915 ( 

1916 (spec._get_operator(spec.operator), spec.version, spec.operator) 

1917 for spec in self._specs 

1918 ), 

1919 key=_operator_cost, 

1920 ) 

1921 ops = self._resolved_ops 

1922 exclude_prereleases = prereleases is False 

1923 

1924 for item in iterable: 

1925 parsed = _coerce_version(item if key is None else key(item)) 

1926 

1927 if parsed is None: 

1928 # Only === can match non-parseable versions. 

1929 if all( 

1930 op == "===" and str(item).lower() == ver.lower() 

1931 for _, ver, op in ops 

1932 ): 

1933 yield item 

1934 elif exclude_prereleases and parsed.is_prerelease: 

1935 pass 

1936 elif all( 

1937 str(item if key is None else key(item)).lower() == ver.lower() 

1938 if op == "===" 

1939 else op_fn(parsed, ver) 

1940 for op_fn, ver, op in ops 

1941 ): 

1942 # Short-circuits on the first failing operator. 

1943 yield item