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

433 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 re 

15import typing 

16from typing import ( 

17 TYPE_CHECKING, 

18 Any, 

19 Callable, 

20 Final, 

21 TypeVar, 

22 Union, 

23) 

24 

25from ._ranges import ( 

26 FULL_RANGE, 

27 bounds_for_spec, 

28 coerce_version, 

29 filter_by_ranges, 

30 intersect_specifier_bounds, 

31 matches_bounds_only, 

32 ranges_are_prerelease_only, 

33 resolve_prereleases, 

34 trim_release, 

35) 

36from .utils import canonicalize_version 

37from .version import Version 

38 

39if TYPE_CHECKING: 

40 import sys 

41 from collections.abc import Iterable, Iterator, Sequence 

42 

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

44 from typing import TypeGuard 

45 else: 

46 from typing_extensions import TypeGuard 

47 

48 from . import ranges 

49 from ._ranges import Interval 

50 

51 

52__all__ = [ 

53 "BaseSpecifier", 

54 "InvalidSpecifier", 

55 "Specifier", 

56 "SpecifierSet", 

57] 

58 

59 

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

61 return __all__ 

62 

63 

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

65 return ( 

66 isinstance(spec, tuple) 

67 and len(spec) == 2 

68 and isinstance(spec[0], str) 

69 and isinstance(spec[1], str) 

70 ) 

71 

72 

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

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

75 

76 

77T = TypeVar("T") 

78UnparsedVersion = Union[Version, str] 

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

80 

81 

82# Operators whose result is just a direct Version comparison, given a parsed 

83# item with no local. ``<=``/``==``/``!=`` need that no-local guard because 

84# PEP 440 strips locals on those; ``>=`` works regardless. 

85_DIRECT_COMPARE_OPS: dict[str, Callable[[Version, Version], bool]] = { 

86 ">=": Version.__ge__, 

87 "<=": Version.__le__, 

88 "==": Version.__eq__, 

89 "!=": Version.__ne__, 

90} 

91 

92 

93def _fast_match(specifier: Specifier, parsed: Version) -> bool | None: 

94 """Match ``parsed`` against ``specifier`` without building a range. 

95 

96 Handles ``>=``, ``<=``, ``==``, ``!=``, ``<``, ``>`` when the spec is 

97 not a wildcard and ``parsed`` has no local. Returns ``None`` when the 

98 range path must be used. Pre-release policy is left to the caller. 

99 """ 

100 op_str, ver_str = specifier._spec 

101 if ver_str.endswith(".*") or parsed.local is not None: 

102 return None 

103 

104 direct_compare = _DIRECT_COMPARE_OPS.get(op_str) 

105 if direct_compare is not None: 

106 return direct_compare(parsed, specifier._require_spec_version(ver_str)) 

107 

108 if op_str in ("<", ">"): 

109 spec_v = specifier._require_spec_version(ver_str) 

110 # ``<V``/``>V`` carve out V's family (pre/dev/post); that only 

111 # matters when parsed shares V's epoch and trimmed release. 

112 # Otherwise a direct cmpkey comparison is correct. 

113 if parsed.epoch != spec_v.epoch or trim_release(parsed.release) != trim_release( 

114 spec_v.release 

115 ): 

116 return parsed < spec_v if op_str == "<" else parsed > spec_v 

117 return None 

118 

119 return None 

120 

121 

122class InvalidSpecifier(ValueError): 

123 """ 

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

125 string that is invalid. 

126 

127 >>> Specifier("lolwat") 

128 Traceback (most recent call last): 

129 ... 

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

131 """ 

132 

133 

134class BaseSpecifier(metaclass=abc.ABCMeta): 

135 """ 

136 Abstract base class for :class:`Specifier` and :class:`SpecifierSet`. 

137 """ 

138 

139 __slots__ = () 

140 __match_args__ = ("_str",) 

141 

142 @property 

143 def _str(self) -> str: 

144 """Internal property for match_args""" 

145 return str(self) 

146 

147 @abc.abstractmethod 

148 def __str__(self) -> str: 

149 """ 

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

151 should be representative of the Specifier itself. 

152 """ 

153 

154 @abc.abstractmethod 

155 def __hash__(self) -> int: 

156 """ 

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

158 """ 

159 

160 @abc.abstractmethod 

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

162 """ 

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

164 objects are equal. 

165 

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

167 """ 

168 

169 @property 

170 @abc.abstractmethod 

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

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

173 

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

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

176 """ 

177 

178 @prereleases.setter # noqa: B027 

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

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

181 

182 :param value: The value to set. 

183 """ 

184 

185 @abc.abstractmethod 

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

187 """ 

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

189 """ 

190 

191 @typing.overload 

192 def filter( 

193 self, 

194 iterable: Iterable[UnparsedVersionVar], 

195 prereleases: bool | None = None, 

196 key: None = ..., 

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

198 

199 @typing.overload 

200 def filter( 

201 self, 

202 iterable: Iterable[T], 

203 prereleases: bool | None = None, 

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

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

206 

207 @abc.abstractmethod 

208 def filter( 

209 self, 

210 iterable: Iterable[Any], 

211 prereleases: bool | None = None, 

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

213 ) -> Iterator[Any]: 

214 """ 

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

216 are contained within this specifier are allowed in it. 

217 """ 

218 

219 

220class Specifier(BaseSpecifier): 

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

222 

223 .. tip:: 

224 

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

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

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

228 

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

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

231 

232 .. versionchanged:: 26.2 

233 

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

235 be unpickled with future releases. Backward compatibility with pickles 

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

237 release. 

238 """ 

239 

240 __slots__ = ( 

241 "_prereleases", 

242 "_ranges", 

243 "_spec", 

244 "_spec_version", 

245 ) 

246 

247 _specifier_regex_str = r""" 

248 (?: 

249 (?: 

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

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

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

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

254 # but included entirely as an escape hatch. 

255 === # Only match for the identity operator 

256 \s* 

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

258 # we match everything except for whitespace, a 

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

260 # since versions can be enclosed in them. 

261 ) 

262 | 

263 (?: 

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

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

266 # operators separately to enable that. 

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

268 

269 \s* 

270 v? 

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

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

273 

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

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

276 (?: 

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

278 | 

279 (?a: # pre release 

280 [-_\.]? 

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

282 [-_\.]? 

283 [0-9]* 

284 )? 

285 (?a: # post release 

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

287 )? 

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

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

290 )? 

291 ) 

292 | 

293 (?: 

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

295 # release segment. 

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

297 

298 \s* 

299 v? 

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

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

302 (?: # pre release 

303 [-_\.]? 

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

305 [-_\.]? 

306 [0-9]* 

307 )? 

308 (?: # post release 

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

310 )? 

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

312 ) 

313 | 

314 (?: 

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

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

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

318 # matching wild cards. 

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

320 

321 \s* 

322 v? 

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

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

325 (?a: # pre release 

326 [-_\.]? 

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

328 [-_\.]? 

329 [0-9]* 

330 )? 

331 (?a: # post release 

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

333 )? 

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

335 ) 

336 ) 

337 """ 

338 

339 _regex = re.compile( 

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

341 ) 

342 

343 # Legacy unused attribute, kept for backward compatibility 

344 _operators: Final = { 

345 "~=": "compatible", 

346 "==": "equal", 

347 "!=": "not_equal", 

348 "<=": "less_than_equal", 

349 ">=": "greater_than_equal", 

350 "<": "less_than", 

351 ">": "greater_than", 

352 "===": "arbitrary", 

353 } 

354 

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

356 """Initialize a Specifier instance. 

357 

358 :param spec: 

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

360 normalized before use. 

361 :param prereleases: 

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

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

364 given specifiers. 

365 :raises InvalidSpecifier: 

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

367 """ 

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

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

370 

371 spec = spec.strip() 

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

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

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

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

376 else: 

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

378 

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

380 

381 # Store whether or not this Specifier should accept prereleases 

382 self._prereleases = prereleases 

383 

384 # Specifier version cache 

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

386 

387 # Version range cache (populated by _to_ranges) 

388 self._ranges: Sequence[Interval] | None = None 

389 

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

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

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

393 return self._spec_version[1] 

394 

395 version_specifier = coerce_version(version) 

396 if version_specifier is None: 

397 return None 

398 

399 self._spec_version = (version, version_specifier) 

400 return version_specifier 

401 

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

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

404 

405 This method should only be called for operators where version 

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

407 """ 

408 spec_version = self._get_spec_version(version) 

409 assert spec_version is not None 

410 return spec_version 

411 

412 def _to_ranges(self) -> Sequence[Interval]: 

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

414 

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

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

417 """ 

418 if self._ranges is not None: 

419 return self._ranges 

420 

421 op = self.operator 

422 ver_str = self.version 

423 

424 if op == "===": 

425 result: Sequence[Interval] = FULL_RANGE 

426 else: 

427 version = self._require_spec_version(ver_str.removesuffix(".*")) 

428 result = bounds_for_spec(op, ver_str, version) 

429 

430 self._ranges = result 

431 return result 

432 

433 @property 

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

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

436 # blindly use that. 

437 if self._prereleases is not None: 

438 return self._prereleases 

439 

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

441 # the version in the specifier is a prerelease. 

442 operator, version_str = self._spec 

443 if operator == "!=": 

444 return False 

445 

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

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

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

449 return False 

450 

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

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

453 version = self._get_spec_version(version_str) 

454 if version is None: 

455 return None 

456 

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

458 # object implies pre-releases. 

459 return version.is_prerelease 

460 

461 @prereleases.setter 

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

463 self._prereleases = value 

464 

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

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

467 # ((operator, version), prereleases) 

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

469 return (self._spec, self._prereleases) 

470 

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

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

473 self._spec_version = None 

474 self._ranges = None 

475 

476 if isinstance(state, tuple): 

477 if len(state) == 2: 

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

479 spec, prereleases = state 

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

481 self._spec = spec 

482 self._prereleases = prereleases 

483 return 

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

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

486 _, slot_dict = state 

487 spec = slot_dict.get("_spec") 

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

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

490 self._spec = spec 

491 self._prereleases = prereleases 

492 return 

493 if isinstance(state, dict): 

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

495 spec = state.get("_spec") 

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

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

498 self._spec = spec 

499 self._prereleases = prereleases 

500 return 

501 

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

503 

504 @property 

505 def operator(self) -> str: 

506 """The operator of this specifier. 

507 

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

509 '==' 

510 """ 

511 return self._spec[0] 

512 

513 @property 

514 def version(self) -> str: 

515 """The version of this specifier. 

516 

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

518 '1.2.3' 

519 """ 

520 return self._spec[1] 

521 

522 def __repr__(self) -> str: 

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

524 

525 >>> Specifier('>=1.0.0') 

526 <Specifier('>=1.0.0')> 

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

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

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

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

531 """ 

532 pre = ( 

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

534 if self._prereleases is not None 

535 else "" 

536 ) 

537 

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

539 

540 def __str__(self) -> str: 

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

542 

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

544 '>=1.0.0' 

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

546 '>=1.0.0' 

547 """ 

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

549 

550 @property 

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

552 operator, version = self._spec 

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

554 return operator, version 

555 

556 spec_version = self._require_spec_version(version) 

557 

558 canonical_version = canonicalize_version( 

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

560 ) 

561 

562 return operator, canonical_version 

563 

564 def __hash__(self) -> int: 

565 return hash(self._canonical_spec) 

566 

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

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

569 

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

571 

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

573 

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

575 True 

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

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

578 True 

579 >>> Specifier("==1.2.3") == "==1.2.3" 

580 True 

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

582 False 

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

584 False 

585 """ 

586 if isinstance(other, str): 

587 try: 

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

589 except InvalidSpecifier: 

590 return NotImplemented 

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

592 return NotImplemented 

593 

594 return self._canonical_spec == other._canonical_spec 

595 

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

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

598 

599 :param item: The item to check for. 

600 

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

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

603 

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

605 True 

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

607 True 

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

609 False 

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

611 True 

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

613 True 

614 """ 

615 return self.contains(item) 

616 

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

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

619 

620 :param item: 

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

622 :class:`~packaging.version.Version` instance. 

623 :param prereleases: 

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

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

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

627 

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

629 True 

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

631 True 

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

633 False 

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

635 True 

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

637 False 

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

639 True 

640 

641 .. versionchanged:: 26.0 

642 

643 With ``prereleases=None``, a prerelease now matches. A single 

644 version has no alternatives, so the :pep:`440` rule to accept 

645 prereleases when nothing else satisfies the specifier applies. 

646 Earlier versions rejected it. An unparsable version now returns 

647 ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`. 

648 """ 

649 # ``===`` compares the raw string, so a Version parse here would 

650 # be wasted. 

651 if self._spec[0] == "===": 

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

653 

654 parsed = coerce_version(item) 

655 if parsed is None: 

656 # Standard operators never match an unparsable input. 

657 return False 

658 

659 if prereleases is None: 

660 prereleases = resolve_prereleases(self._prereleases, self.prereleases) 

661 

662 if prereleases is False and parsed.is_prerelease: 

663 return False 

664 

665 # ``_fast_match`` answers the simple operators without building a 

666 # range; otherwise fall back to the engine's bounds membership. 

667 match = _fast_match(self, parsed) 

668 if match is not None: 

669 return match 

670 

671 return matches_bounds_only(self._to_ranges(), parsed) 

672 

673 @typing.overload 

674 def filter( 

675 self, 

676 iterable: Iterable[UnparsedVersionVar], 

677 prereleases: bool | None = None, 

678 key: None = ..., 

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

680 

681 @typing.overload 

682 def filter( 

683 self, 

684 iterable: Iterable[T], 

685 prereleases: bool | None = None, 

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

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

688 

689 def filter( 

690 self, 

691 iterable: Iterable[Any], 

692 prereleases: bool | None = None, 

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

694 ) -> Iterator[Any]: 

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

696 

697 :param iterable: 

698 An iterable that can contain version strings and 

699 :class:`~packaging.version.Version` instances. The items in the 

700 iterable will be filtered according to the specifier. 

701 :param prereleases: 

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

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

704 and match prereleases if there are no other versions. 

705 :param key: 

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

707 returns a version string or :class:`~packaging.version.Version` 

708 instance to be used for filtering. 

709 

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

711 ['1.3'] 

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

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

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

715 ['1.5a1'] 

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

717 ['1.3', '1.5a1'] 

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

719 ['1.3', '1.5a1'] 

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

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

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

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

724 

725 .. versionchanged:: 26.1 

726 

727 Added the ``key`` parameter. 

728 """ 

729 if prereleases is None: 

730 prereleases = resolve_prereleases(self._prereleases, self.prereleases) 

731 

732 if self.operator == "===": 

733 spec_lower = self.version.lower() 

734 matches = ( 

735 item 

736 for item in iterable 

737 if str(item if key is None else key(item)).lower() == spec_lower 

738 ) 

739 return _apply_prereleases_filter(matches, key, prereleases) 

740 

741 return filter_by_ranges(self._to_ranges(), iterable, key, prereleases) 

742 

743 

744def _apply_prereleases_filter( 

745 matches: Iterable[Any], 

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

747 prereleases: bool | None, 

748) -> Iterator[Any]: 

749 """Apply ``prereleases=`` handling to an already-matched iterable. 

750 

751 ``None`` means PEP 440 default (buffer pre-releases until a final 

752 appears); ``True`` yields everything; ``False`` drops pre-releases. 

753 """ 

754 if prereleases is None: 

755 return _pep440_filter_prereleases(matches, key) 

756 if prereleases: 

757 return iter(matches) 

758 return ( 

759 item 

760 for item in matches 

761 if (parsed := coerce_version(item if key is None else key(item))) is None 

762 or not parsed.is_prerelease 

763 ) 

764 

765 

766class SpecifierSet(BaseSpecifier): 

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

768 

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

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

771 

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

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

774 releases. 

775 

776 .. versionchanged:: 26.2 

777 

778 Added a stable pickle format. Pickles created with 

779 packaging 26.2+ can be unpickled with future releases. 

780 Backward compatibility with pickles from 

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

782 release. 

783 """ 

784 

785 __slots__ = ( 

786 "_canonicalized", 

787 "_has_arbitrary", 

788 "_is_unsatisfiable", 

789 "_prereleases", 

790 "_ranges", 

791 "_specs", 

792 ) 

793 

794 def __init__( 

795 self, 

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

797 prereleases: bool | None = None, 

798 ) -> None: 

799 """Initialize a SpecifierSet instance. 

800 

801 :param specifiers: 

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

803 specifiers which will be parsed and normalized before use. 

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

805 as is. 

806 :param prereleases: 

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

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

809 given specifiers. 

810 

811 :raises InvalidSpecifier: 

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

813 raised. 

814 """ 

815 

816 if isinstance(specifiers, str): 

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

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

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

820 

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

822 # Fast substring check; avoids iterating parsed specs. 

823 self._has_arbitrary = "===" in specifiers 

824 else: 

825 self._specs = tuple(specifiers) 

826 # Substring check works for both Specifier objects and plain 

827 # strings (setuptools passes lists of strings). 

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

829 

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

831 self._is_unsatisfiable: bool | None = None 

832 self._ranges: Sequence[Interval] | None = None 

833 

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

835 # we accept prereleases or not. 

836 self._prereleases = prereleases 

837 

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

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

840 if not self._canonicalized: 

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

842 self._canonicalized = True 

843 return self._specs 

844 

845 @property 

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

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

848 # pass that through here. 

849 if self._prereleases is not None: 

850 return self._prereleases 

851 

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

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

854 # pre-releases or not. 

855 if not self._specs: 

856 return None 

857 

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

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

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

861 return True 

862 

863 return None 

864 

865 @prereleases.setter 

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

867 self._prereleases = value 

868 self._is_unsatisfiable = None 

869 

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

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

872 # (specs, prereleases) 

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

874 return (self._specs, self._prereleases) 

875 

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

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

878 self._ranges = None 

879 self._is_unsatisfiable = None 

880 

881 if isinstance(state, tuple): 

882 if len(state) == 2: 

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

884 specs, prereleases = state 

885 if ( 

886 isinstance(specs, tuple) 

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

888 and _validate_pre(prereleases) 

889 ): 

890 self._specs = specs 

891 self._prereleases = prereleases 

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

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

894 return 

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

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

897 _, slot_dict = state 

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

899 prereleases = slot_dict.get("_prereleases") 

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

901 if isinstance(specs, frozenset): 

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

903 if ( 

904 isinstance(specs, tuple) 

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

906 and _validate_pre(prereleases) 

907 ): 

908 self._specs = specs 

909 self._prereleases = prereleases 

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

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

912 return 

913 if isinstance(state, dict): 

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

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

916 prereleases = state.get("_prereleases") 

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

918 if isinstance(specs, frozenset): 

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

920 if ( 

921 isinstance(specs, tuple) 

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

923 and _validate_pre(prereleases) 

924 ): 

925 self._specs = specs 

926 self._prereleases = prereleases 

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

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

929 return 

930 

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

932 

933 def __repr__(self) -> str: 

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

935 

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

937 match the input string. 

938 

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

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

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

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

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

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

945 """ 

946 pre = ( 

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

948 if self._prereleases is not None 

949 else "" 

950 ) 

951 

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

953 

954 def __str__(self) -> str: 

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

956 

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

958 match the input string. 

959 

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

961 '!=1.0.1,>=1.0.0' 

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

963 '!=1.0.1,>=1.0.0' 

964 """ 

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

966 

967 def __hash__(self) -> int: 

968 return hash(self._canonical_specs()) 

969 

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

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

972 

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

974 

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

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

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

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

979 """ 

980 if isinstance(other, str): 

981 other = SpecifierSet(other) 

982 elif not isinstance(other, SpecifierSet): 

983 return NotImplemented 

984 

985 specifier = SpecifierSet() 

986 specifier._specs = self._specs + other._specs 

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

988 specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary 

989 

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

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

992 specifier._prereleases = other._prereleases 

993 elif other._prereleases is None: 

994 specifier._prereleases = self._prereleases 

995 else: 

996 raise ValueError( 

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

998 ) 

999 

1000 return specifier 

1001 

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

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

1004 

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

1006 

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

1008 

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

1010 True 

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

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

1013 True 

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

1015 True 

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

1017 False 

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

1019 False 

1020 """ 

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

1022 other = SpecifierSet(str(other)) 

1023 elif not isinstance(other, SpecifierSet): 

1024 return NotImplemented 

1025 

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

1027 

1028 def __len__(self) -> int: 

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

1030 return len(self._specs) 

1031 

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

1033 """ 

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

1035 in this specifier set. 

1036 

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

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

1039 """ 

1040 return iter(self._specs) 

1041 

1042 def _get_ranges(self) -> Sequence[Interval]: 

1043 """Intersect all specifiers into a single sequence of version ranges. 

1044 

1045 Empty when unsatisfiable. Callers must ensure ``self._specs`` 

1046 is non-empty. 

1047 """ 

1048 if self._ranges is not None: 

1049 return self._ranges 

1050 

1051 self._ranges = intersect_specifier_bounds(s._to_ranges() for s in self._specs) 

1052 return self._ranges 

1053 

1054 def is_unsatisfiable(self) -> bool: 

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

1056 

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

1058 

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

1060 True 

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

1062 False 

1063 >>> SpecifierSet("").is_unsatisfiable() 

1064 False 

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

1066 True 

1067 

1068 .. versionadded:: 26.1 

1069 """ 

1070 cached = self._is_unsatisfiable 

1071 if cached is not None: 

1072 return cached 

1073 

1074 if not self._specs: 

1075 self._is_unsatisfiable = False 

1076 return False 

1077 

1078 result = not self._get_ranges() 

1079 

1080 if not result: 

1081 result = self._check_arbitrary_unsatisfiable() 

1082 

1083 if not result and self.prereleases is False: 

1084 result = ranges_are_prerelease_only(self._get_ranges()) 

1085 

1086 self._is_unsatisfiable = result 

1087 return result 

1088 

1089 def _check_arbitrary_unsatisfiable(self) -> bool: 

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

1091 

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

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

1094 checks whether that candidate is excluded by other specifiers. 

1095 """ 

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

1097 if not arbitrary: 

1098 return False 

1099 

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

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

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

1103 return True 

1104 

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

1106 # it can satisfy every standard spec. 

1107 candidate = coerce_version(arbitrary[0].version) 

1108 

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

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

1111 if ( 

1112 self.prereleases is False 

1113 and candidate is not None 

1114 and candidate.is_prerelease 

1115 ): 

1116 return True 

1117 

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

1119 if not standard: 

1120 return False 

1121 

1122 if candidate is None: 

1123 # Unparsable string cannot satisfy any standard spec. 

1124 return True 

1125 

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

1127 

1128 def to_range(self) -> ranges.VersionRange: 

1129 """Return the :class:`~packaging.ranges.VersionRange` this set accepts. 

1130 

1131 An empty set yields the full range; an unsatisfiable set yields the 

1132 empty range. ``===`` specifiers contribute literal-string admission. 

1133 

1134 >>> SpecifierSet(">=1.0,<2.0").to_range() 

1135 <VersionRange '[1.0, 2.0.dev0)'> 

1136 

1137 .. versionadded:: 26.3 

1138 """ 

1139 from .ranges import VersionRange # noqa: PLC0415 

1140 

1141 return VersionRange._from_specifier_set(self) 

1142 

1143 def _check_relation_operand(self, other: object) -> None: 

1144 if not isinstance(other, SpecifierSet): 

1145 raise TypeError("expected a SpecifierSet") 

1146 if self._has_arbitrary or other._has_arbitrary: 

1147 raise ValueError("set relations do not support === specifiers") 

1148 

1149 def is_subset(self, other: SpecifierSet) -> bool: 

1150 """Return whether every version matching this set also matches other. 

1151 

1152 :raises ValueError: 

1153 If either set uses ``===`` specifiers, or the two sets were 

1154 given different ``prereleases`` arguments (unset on one side 

1155 counts as different). 

1156 :raises TypeError: 

1157 If other is not a :class:`SpecifierSet`. 

1158 

1159 >>> SpecifierSet(">=3.12,<3.13").is_subset(SpecifierSet(">=3.12")) 

1160 True 

1161 >>> SpecifierSet(">=3.12").is_subset(SpecifierSet(">=3.12,<3.13")) 

1162 False 

1163 

1164 .. versionadded:: 26.3 

1165 """ 

1166 self._check_relation_operand(other) 

1167 return self.to_range().is_subset(other.to_range()) 

1168 

1169 def is_superset(self, other: SpecifierSet) -> bool: 

1170 """Return whether every version matching other also matches this set. 

1171 

1172 :raises ValueError: 

1173 If either set uses ``===`` specifiers, or the two sets were 

1174 given different ``prereleases`` arguments (unset on one side 

1175 counts as different). 

1176 :raises TypeError: 

1177 If other is not a :class:`SpecifierSet`. 

1178 

1179 >>> SpecifierSet(">=3.12").is_superset(SpecifierSet(">=3.12,<3.13")) 

1180 True 

1181 

1182 .. versionadded:: 26.3 

1183 """ 

1184 self._check_relation_operand(other) 

1185 return self.to_range().is_superset(other.to_range()) 

1186 

1187 def is_disjoint(self, other: SpecifierSet) -> bool: 

1188 """Return whether this set and other share no matching versions. 

1189 

1190 :raises ValueError: 

1191 If either set uses ``===`` specifiers, or the two sets were 

1192 given different ``prereleases`` arguments (unset on one side 

1193 counts as different). 

1194 :raises TypeError: 

1195 If other is not a :class:`SpecifierSet`. 

1196 

1197 >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.12")) 

1198 True 

1199 >>> SpecifierSet("<3.12").is_disjoint(SpecifierSet(">=3.11")) 

1200 False 

1201 

1202 .. versionadded:: 26.3 

1203 """ 

1204 self._check_relation_operand(other) 

1205 return self.to_range().is_disjoint(other.to_range()) 

1206 

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

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

1209 

1210 :param item: The item to check for. 

1211 

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

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

1214 

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

1216 True 

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

1218 True 

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

1220 False 

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

1222 True 

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

1224 True 

1225 """ 

1226 return self.contains(item) 

1227 

1228 def contains( 

1229 self, 

1230 item: UnparsedVersion, 

1231 prereleases: bool | None = None, 

1232 installed: bool | None = None, 

1233 ) -> bool: 

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

1235 

1236 :param item: 

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

1238 :class:`~packaging.version.Version` instance. 

1239 :param prereleases: 

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

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

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

1243 :param installed: 

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

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

1246 

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

1248 True 

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

1250 True 

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

1252 False 

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

1254 True 

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

1256 False 

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

1258 True 

1259 

1260 .. versionchanged:: 26.0 

1261 

1262 With ``prereleases=None``, a prerelease now matches. A single 

1263 version has no alternatives, so the :pep:`440` rule to accept 

1264 prereleases when nothing else satisfies the specifiers applies. 

1265 Earlier versions rejected it. An unparsable version now returns 

1266 ``False`` instead of raising :exc:`~packaging.version.InvalidVersion`. 

1267 """ 

1268 version = coerce_version(item) 

1269 

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

1271 prereleases = True 

1272 

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

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

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

1276 check_item = item 

1277 else: 

1278 check_item = version 

1279 

1280 # Fast path: a parseable, local-free version against a rangelike set. 

1281 # A local on ``version`` needs PEP 440 stripping that the range path 

1282 # applies. 

1283 if ( 

1284 version is not None 

1285 and not self._has_arbitrary 

1286 and version.local is None 

1287 and self._specs 

1288 ): 

1289 if version.is_prerelease and ( 

1290 prereleases is False 

1291 or (prereleases is None and self._prereleases is False) 

1292 ): 

1293 return False 

1294 

1295 bounds = self._ranges 

1296 if bounds is None: 

1297 # Per-spec ``_fast_match`` answers a set of simple specifiers 

1298 # without folding anything. If a spec needs the range path, 

1299 # fold the intersected bounds once and cache them so repeated 

1300 # checks on the same set stay cheap. 

1301 for spec in self._specs: 

1302 match = _fast_match(spec, version) 

1303 if match is None: 

1304 break 

1305 if not match: 

1306 return False 

1307 else: 

1308 return True 

1309 

1310 bounds = self._ranges = self._get_ranges() 

1311 

1312 return matches_bounds_only(bounds, version) 

1313 

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

1315 

1316 @typing.overload 

1317 def filter( 

1318 self, 

1319 iterable: Iterable[UnparsedVersionVar], 

1320 prereleases: bool | None = None, 

1321 key: None = ..., 

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

1323 

1324 @typing.overload 

1325 def filter( 

1326 self, 

1327 iterable: Iterable[T], 

1328 prereleases: bool | None = None, 

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

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

1331 

1332 def filter( 

1333 self, 

1334 iterable: Iterable[Any], 

1335 prereleases: bool | None = None, 

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

1337 ) -> Iterator[Any]: 

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

1339 

1340 :param iterable: 

1341 An iterable that can contain version strings and 

1342 :class:`~packaging.version.Version` instances. The items in the 

1343 iterable will be filtered according to the specifier. 

1344 :param prereleases: 

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

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

1347 and match prereleases if there are no other versions. 

1348 :param key: 

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

1350 returns a version string or :class:`~packaging.version.Version` 

1351 instance to be used for filtering. 

1352 

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

1354 ['1.3'] 

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

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

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

1358 ['1.5a1'] 

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

1360 ['1.3', '1.5a1'] 

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

1362 ['1.3', '1.5a1'] 

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

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

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

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

1367 

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

1369 versions in the set. 

1370 

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

1372 ['1.3'] 

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

1374 ['1.5a1'] 

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

1376 ['1.3', '1.5a1'] 

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

1378 ['1.3', '1.5a1'] 

1379 

1380 .. versionchanged:: 26.0 

1381 

1382 Prerelease filtering now follows the PEP 440 recommendation of 

1383 yielding prereleases only when no final release is present. 

1384 

1385 .. versionchanged:: 26.1 

1386 

1387 Added the ``key`` parameter. 

1388 """ 

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

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

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

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

1393 prereleases = self.prereleases 

1394 

1395 if self._specs: 

1396 if self._has_arbitrary: 

1397 # Slow path for === 

1398 specs = self._specs 

1399 matches = ( 

1400 item 

1401 for item in iterable 

1402 if all( 

1403 s.contains(item if key is None else key(item), prereleases=True) 

1404 for s in specs 

1405 ) 

1406 ) 

1407 return _apply_prereleases_filter(matches, key, prereleases) 

1408 

1409 ranges = self._ranges 

1410 if ranges is None: 

1411 ranges = self._get_ranges() 

1412 return filter_by_ranges(ranges, iterable, key, prereleases) 

1413 

1414 # Empty SpecifierSet. 

1415 return _apply_prereleases_filter(iterable, key, prereleases) 

1416 

1417 

1418def _pep440_filter_prereleases( 

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

1420) -> Iterator[Any]: 

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

1422 # Two lists used: 

1423 # * all_nonfinal to preserve order if no finals exist 

1424 # * arbitrary_strings for streaming when first final found 

1425 all_nonfinal: list[Any] = [] 

1426 arbitrary_strings: list[Any] = [] 

1427 

1428 found_final = False 

1429 for item in iterable: 

1430 parsed = coerce_version(item if key is None else key(item)) 

1431 

1432 if parsed is None: 

1433 # Arbitrary strings are always included as it is not 

1434 # possible to determine if they are prereleases, 

1435 # and they have already passed all specifiers. 

1436 if found_final: 

1437 yield item 

1438 else: 

1439 arbitrary_strings.append(item) 

1440 all_nonfinal.append(item) 

1441 continue 

1442 

1443 if not parsed.is_prerelease: 

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

1445 if not found_final: 

1446 yield from arbitrary_strings 

1447 found_final = True 

1448 yield item 

1449 continue 

1450 

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

1452 if not found_final: 

1453 all_nonfinal.append(item) 

1454 

1455 # No finals found - yield all buffered items 

1456 if not found_final: 

1457 yield from all_nonfinal