Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/packaging/specifiers.py: 13%

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

430 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 packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier 

8 from 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 Final, 

20 TypeVar, 

21) 

22 

23from ._ranges import ( 

24 FULL_RANGE, 

25 bounds_for_spec, 

26 coerce_version, 

27 filter_by_ranges, 

28 intersect_specifier_bounds, 

29 matches_bounds_only, 

30 ranges_are_prerelease_only, 

31 resolve_prereleases, 

32 trim_release, 

33) 

34from .utils import canonicalize_version 

35from .version import Version 

36 

37if TYPE_CHECKING: 

38 from collections.abc import Callable, Iterable, Iterator, Sequence 

39 from typing import TypeGuard 

40 

41 from . import ranges 

42 from ._ranges import Interval 

43 

44 

45__all__ = [ 

46 "BaseSpecifier", 

47 "InvalidSpecifier", 

48 "Specifier", 

49 "SpecifierSet", 

50] 

51 

52 

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

54 return __all__ 

55 

56 

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

58 return ( 

59 isinstance(spec, tuple) 

60 and len(spec) == 2 

61 and isinstance(spec[0], str) 

62 and isinstance(spec[1], str) 

63 ) 

64 

65 

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

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

68 

69 

70T = TypeVar("T") 

71UnparsedVersion = Version | str 

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

73 

74 

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

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

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

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

79 ">=": Version.__ge__, 

80 "<=": Version.__le__, 

81 "==": Version.__eq__, 

82 "!=": Version.__ne__, 

83} 

84 

85 

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

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

88 

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

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

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

92 """ 

93 op_str, ver_str = specifier._spec 

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

95 return None 

96 

97 direct_compare = _DIRECT_COMPARE_OPS.get(op_str) 

98 if direct_compare is not None: 

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

100 

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

102 spec_v = specifier._require_spec_version(ver_str) 

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

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

105 # Otherwise a direct cmpkey comparison is correct. 

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

107 spec_v.release 

108 ): 

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

110 return None 

111 

112 return None 

113 

114 

115class InvalidSpecifier(ValueError): 

116 """ 

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

118 string that is invalid. 

119 

120 >>> Specifier("lolwat") 

121 Traceback (most recent call last): 

122 ... 

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

124 """ 

125 

126 

127class BaseSpecifier(metaclass=abc.ABCMeta): 

128 """ 

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

130 """ 

131 

132 __slots__ = () 

133 __match_args__ = ("_str",) 

134 

135 @property 

136 def _str(self) -> str: 

137 """Internal property for match_args""" 

138 return str(self) 

139 

140 @abc.abstractmethod 

141 def __str__(self) -> str: 

142 """ 

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

144 should be representative of the Specifier itself. 

145 """ 

146 

147 @abc.abstractmethod 

148 def __hash__(self) -> int: 

149 """ 

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

151 """ 

152 

153 @abc.abstractmethod 

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

155 """ 

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

157 objects are equal. 

158 

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

160 """ 

161 

162 @property 

163 @abc.abstractmethod 

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

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

166 

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

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

169 """ 

170 

171 @prereleases.setter # noqa: B027 

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

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

174 

175 :param value: The value to set. 

176 """ 

177 

178 @abc.abstractmethod 

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

180 """ 

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

182 """ 

183 

184 @typing.overload 

185 def filter( 

186 self, 

187 iterable: Iterable[UnparsedVersionVar], 

188 prereleases: bool | None = None, 

189 key: None = ..., 

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

191 

192 @typing.overload 

193 def filter( 

194 self, 

195 iterable: Iterable[T], 

196 prereleases: bool | None = None, 

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

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

199 

200 @abc.abstractmethod 

201 def filter( 

202 self, 

203 iterable: Iterable[Any], 

204 prereleases: bool | None = None, 

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

206 ) -> Iterator[Any]: 

207 """ 

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

209 are contained within this specifier are allowed in it. 

210 """ 

211 

212 

213class Specifier(BaseSpecifier): 

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

215 

216 .. tip:: 

217 

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

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

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

221 

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

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

224 

225 .. versionchanged:: 26.2 

226 

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

228 be unpickled with future releases. Backward compatibility with pickles 

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

230 release. 

231 """ 

232 

233 __slots__ = ( 

234 "_prereleases", 

235 "_ranges", 

236 "_spec", 

237 "_spec_version", 

238 ) 

239 

240 _specifier_regex_str = r""" 

241 (?: 

242 (?: 

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

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

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

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

247 # but included entirely as an escape hatch. 

248 === # Only match for the identity operator 

249 \s* 

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

251 # we match everything except for whitespace, a 

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

253 # since versions can be enclosed in them. 

254 ) 

255 | 

256 (?: 

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

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

259 # operators separately to enable that. 

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

261 

262 \s* 

263 v? 

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

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

266 

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

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

269 (?: 

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

271 | 

272 (?a: # pre release 

273 [-_\.]? 

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

275 [-_\.]? 

276 [0-9]* 

277 )? 

278 (?a: # post release 

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

280 )? 

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

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

283 )? 

284 ) 

285 | 

286 (?: 

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

288 # release segment. 

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

290 

291 \s* 

292 v? 

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

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

295 (?a: # pre release 

296 [-_\.]? 

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

298 [-_\.]? 

299 [0-9]* 

300 )? 

301 (?a: # post release 

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

303 )? 

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

305 ) 

306 | 

307 (?: 

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

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

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

311 # matching wild cards. 

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

313 

314 \s* 

315 v? 

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

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

318 (?a: # pre release 

319 [-_\.]? 

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

321 [-_\.]? 

322 [0-9]* 

323 )? 

324 (?a: # post release 

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

326 )? 

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

328 ) 

329 ) 

330 """ 

331 

332 _regex = re.compile( 

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

334 ) 

335 

336 # Legacy unused attribute, kept for backward compatibility 

337 _operators: Final = { 

338 "~=": "compatible", 

339 "==": "equal", 

340 "!=": "not_equal", 

341 "<=": "less_than_equal", 

342 ">=": "greater_than_equal", 

343 "<": "less_than", 

344 ">": "greater_than", 

345 "===": "arbitrary", 

346 } 

347 

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

349 """Initialize a Specifier instance. 

350 

351 :param spec: 

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

353 normalized before use. 

354 :param prereleases: 

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

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

357 given specifiers. 

358 :raises InvalidSpecifier: 

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

360 """ 

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

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

363 

364 spec = spec.strip() 

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

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

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

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

369 else: 

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

371 

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

373 

374 # Store whether or not this Specifier should accept prereleases 

375 self._prereleases = prereleases 

376 

377 # Specifier version cache 

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

379 

380 # Version range cache (populated by _to_ranges) 

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

382 

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

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

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

386 return self._spec_version[1] 

387 

388 version_specifier = coerce_version(version) 

389 if version_specifier is None: 

390 return None 

391 

392 self._spec_version = (version, version_specifier) 

393 return version_specifier 

394 

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

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

397 

398 This method should only be called for operators where version 

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

400 """ 

401 spec_version = self._get_spec_version(version) 

402 assert spec_version is not None 

403 return spec_version 

404 

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

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

407 

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

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

410 """ 

411 if self._ranges is not None: 

412 return self._ranges 

413 

414 op = self.operator 

415 ver_str = self.version 

416 

417 if op == "===": 

418 result: Sequence[Interval] = FULL_RANGE 

419 else: 

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

421 result = bounds_for_spec(op, ver_str, version) 

422 

423 self._ranges = result 

424 return result 

425 

426 @property 

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

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

429 # blindly use that. 

430 if self._prereleases is not None: 

431 return self._prereleases 

432 

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

434 # the version in the specifier is a prerelease. 

435 operator, version_str = self._spec 

436 if operator == "!=": 

437 return False 

438 

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

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

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

442 return False 

443 

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

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

446 version = self._get_spec_version(version_str) 

447 if version is None: 

448 return None 

449 

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

451 # object implies pre-releases. 

452 return version.is_prerelease 

453 

454 @prereleases.setter 

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

456 self._prereleases = value 

457 

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

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

460 # ((operator, version), prereleases) 

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

462 return (self._spec, self._prereleases) 

463 

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

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

466 self._spec_version = None 

467 self._ranges = None 

468 

469 if isinstance(state, tuple): 

470 if len(state) == 2: 

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

472 spec, prereleases = state 

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

474 self._spec = spec 

475 self._prereleases = prereleases 

476 return 

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

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

479 _, slot_dict = state 

480 spec = slot_dict.get("_spec") 

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

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

483 self._spec = spec 

484 self._prereleases = prereleases 

485 return 

486 if isinstance(state, dict): 

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

488 spec = state.get("_spec") 

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

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

491 self._spec = spec 

492 self._prereleases = prereleases 

493 return 

494 

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

496 

497 @property 

498 def operator(self) -> str: 

499 """The operator of this specifier. 

500 

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

502 '==' 

503 """ 

504 return self._spec[0] 

505 

506 @property 

507 def version(self) -> str: 

508 """The version of this specifier. 

509 

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

511 '1.2.3' 

512 """ 

513 return self._spec[1] 

514 

515 def __repr__(self) -> str: 

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

517 

518 >>> Specifier('>=1.0.0') 

519 <Specifier('>=1.0.0')> 

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

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

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

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

524 """ 

525 pre = ( 

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

527 if self._prereleases is not None 

528 else "" 

529 ) 

530 

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

532 

533 def __str__(self) -> str: 

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

535 

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

537 '>=1.0.0' 

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

539 '>=1.0.0' 

540 """ 

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

542 

543 @property 

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

545 operator, version = self._spec 

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

547 return operator, version 

548 

549 spec_version = self._require_spec_version(version) 

550 

551 canonical_version = canonicalize_version( 

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

553 ) 

554 

555 return operator, canonical_version 

556 

557 def __hash__(self) -> int: 

558 return hash(self._canonical_spec) 

559 

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

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

562 

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

564 

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

566 

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

568 True 

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

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

571 True 

572 >>> Specifier("==1.2.3") == "==1.2.3" 

573 True 

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

575 False 

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

577 False 

578 """ 

579 if isinstance(other, str): 

580 try: 

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

582 except InvalidSpecifier: 

583 return NotImplemented 

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

585 return NotImplemented 

586 

587 return self._canonical_spec == other._canonical_spec 

588 

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

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

591 

592 :param item: The item to check for. 

593 

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

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

596 

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

598 True 

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

600 True 

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

602 False 

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

604 True 

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

606 True 

607 """ 

608 return self.contains(item) 

609 

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

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

612 

613 :param item: 

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

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

616 :param prereleases: 

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

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

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

620 

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

622 True 

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

624 True 

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

626 False 

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

628 True 

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

630 False 

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

632 True 

633 

634 .. versionchanged:: 26.0 

635 

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

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

638 prereleases when nothing else satisfies the specifier applies. 

639 Earlier versions rejected it. An unparsable version now returns 

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

641 """ 

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

643 # be wasted. 

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

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

646 

647 parsed = coerce_version(item) 

648 if parsed is None: 

649 # Standard operators never match an unparsable input. 

650 return False 

651 

652 if prereleases is None: 

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

654 

655 if prereleases is False and parsed.is_prerelease: 

656 return False 

657 

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

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

660 match = _fast_match(self, parsed) 

661 if match is not None: 

662 return match 

663 

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

665 

666 @typing.overload 

667 def filter( 

668 self, 

669 iterable: Iterable[UnparsedVersionVar], 

670 prereleases: bool | None = None, 

671 key: None = ..., 

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

673 

674 @typing.overload 

675 def filter( 

676 self, 

677 iterable: Iterable[T], 

678 prereleases: bool | None = None, 

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

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

681 

682 def filter( 

683 self, 

684 iterable: Iterable[Any], 

685 prereleases: bool | None = None, 

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

687 ) -> Iterator[Any]: 

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

689 

690 :param iterable: 

691 An iterable that can contain version strings and 

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

693 iterable will be filtered according to the specifier. 

694 :param prereleases: 

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

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

697 and match prereleases if there are no other versions. 

698 :param key: 

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

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

701 instance to be used for filtering. 

702 

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

704 ['1.3'] 

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

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

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

708 ['1.5a1'] 

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

710 ['1.3', '1.5a1'] 

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

712 ['1.3', '1.5a1'] 

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

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

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

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

717 

718 .. versionchanged:: 26.1 

719 

720 Added the ``key`` parameter. 

721 """ 

722 if prereleases is None: 

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

724 

725 if self.operator == "===": 

726 spec_lower = self.version.lower() 

727 matches = ( 

728 item 

729 for item in iterable 

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

731 ) 

732 return _apply_prereleases_filter(matches, key, prereleases) 

733 

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

735 

736 

737def _apply_prereleases_filter( 

738 matches: Iterable[Any], 

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

740 prereleases: bool | None, 

741) -> Iterator[Any]: 

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

743 

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

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

746 """ 

747 if prereleases is None: 

748 return _pep440_filter_prereleases(matches, key) 

749 if prereleases: 

750 return iter(matches) 

751 return ( 

752 item 

753 for item in matches 

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

755 or not parsed.is_prerelease 

756 ) 

757 

758 

759class SpecifierSet(BaseSpecifier): 

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

761 

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

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

764 

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

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

767 releases. 

768 

769 .. versionchanged:: 26.2 

770 

771 Added a stable pickle format. Pickles created with 

772 packaging 26.2+ can be unpickled with future releases. 

773 Backward compatibility with pickles from 

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

775 release. 

776 """ 

777 

778 __slots__ = ( 

779 "_canonicalized", 

780 "_has_arbitrary", 

781 "_is_unsatisfiable", 

782 "_prereleases", 

783 "_ranges", 

784 "_specs", 

785 ) 

786 

787 def __init__( 

788 self, 

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

790 prereleases: bool | None = None, 

791 ) -> None: 

792 """Initialize a SpecifierSet instance. 

793 

794 :param specifiers: 

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

796 specifiers which will be parsed and normalized before use. 

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

798 as is. 

799 :param prereleases: 

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

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

802 given specifiers. 

803 

804 :raises InvalidSpecifier: 

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

806 raised. 

807 """ 

808 

809 if isinstance(specifiers, str): 

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

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

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

813 

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

815 # Fast substring check; avoids iterating parsed specs. 

816 self._has_arbitrary = "===" in specifiers 

817 else: 

818 self._specs = tuple(specifiers) 

819 # Substring check works for both Specifier objects and plain 

820 # strings (setuptools passes lists of strings). 

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

822 

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

824 self._is_unsatisfiable: bool | None = None 

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

826 

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

828 # we accept prereleases or not. 

829 self._prereleases = prereleases 

830 

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

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

833 if not self._canonicalized: 

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

835 self._canonicalized = True 

836 return self._specs 

837 

838 @property 

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

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

841 # pass that through here. 

842 if self._prereleases is not None: 

843 return self._prereleases 

844 

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

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

847 # pre-releases or not. 

848 if not self._specs: 

849 return None 

850 

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

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

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

854 return True 

855 

856 return None 

857 

858 @prereleases.setter 

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

860 self._prereleases = value 

861 self._is_unsatisfiable = None 

862 

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

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

865 # (specs, prereleases) 

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

867 return (self._specs, self._prereleases) 

868 

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

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

871 self._ranges = None 

872 self._is_unsatisfiable = None 

873 

874 if isinstance(state, tuple): 

875 if len(state) == 2: 

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

877 specs, prereleases = state 

878 if ( 

879 isinstance(specs, tuple) 

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

881 and _validate_pre(prereleases) 

882 ): 

883 self._specs = specs 

884 self._prereleases = prereleases 

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

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

887 return 

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

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

890 _, slot_dict = state 

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

892 prereleases = slot_dict.get("_prereleases") 

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

894 if isinstance(specs, frozenset): 

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

896 if ( 

897 isinstance(specs, tuple) 

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

899 and _validate_pre(prereleases) 

900 ): 

901 self._specs = specs 

902 self._prereleases = prereleases 

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

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

905 return 

906 if isinstance(state, dict): 

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

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

909 prereleases = state.get("_prereleases") 

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

911 if isinstance(specs, frozenset): 

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

913 if ( 

914 isinstance(specs, tuple) 

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

916 and _validate_pre(prereleases) 

917 ): 

918 self._specs = specs 

919 self._prereleases = prereleases 

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

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

922 return 

923 

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

925 

926 def __repr__(self) -> str: 

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

928 

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

930 match the input string. 

931 

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

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

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

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

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

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

938 """ 

939 pre = ( 

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

941 if self._prereleases is not None 

942 else "" 

943 ) 

944 

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

946 

947 def __str__(self) -> str: 

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

949 

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

951 match the input string. 

952 

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

954 '!=1.0.1,>=1.0.0' 

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

956 '!=1.0.1,>=1.0.0' 

957 """ 

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

959 

960 def __hash__(self) -> int: 

961 return hash(self._canonical_specs()) 

962 

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

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

965 

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

967 

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

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

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

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

972 """ 

973 if isinstance(other, str): 

974 other = SpecifierSet(other) 

975 elif not isinstance(other, SpecifierSet): 

976 return NotImplemented 

977 

978 specifier = SpecifierSet() 

979 specifier._specs = self._specs + other._specs 

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

981 specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary 

982 

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

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

985 specifier._prereleases = other._prereleases 

986 elif other._prereleases is None: 

987 specifier._prereleases = self._prereleases 

988 else: 

989 raise ValueError( 

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

991 ) 

992 

993 return specifier 

994 

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

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

997 

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

999 

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

1001 

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

1003 True 

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

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

1006 True 

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

1008 True 

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

1010 False 

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

1012 False 

1013 """ 

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

1015 other = SpecifierSet(str(other)) 

1016 elif not isinstance(other, SpecifierSet): 

1017 return NotImplemented 

1018 

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

1020 

1021 def __len__(self) -> int: 

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

1023 return len(self._specs) 

1024 

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

1026 """ 

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

1028 in this specifier set. 

1029 

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

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

1032 """ 

1033 return iter(self._specs) 

1034 

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

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

1037 

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

1039 is non-empty. 

1040 """ 

1041 if self._ranges is not None: 

1042 return self._ranges 

1043 

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

1045 return self._ranges 

1046 

1047 def is_unsatisfiable(self) -> bool: 

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

1049 

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

1051 

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

1053 True 

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

1055 False 

1056 >>> SpecifierSet("").is_unsatisfiable() 

1057 False 

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

1059 True 

1060 

1061 .. versionadded:: 26.1 

1062 """ 

1063 cached = self._is_unsatisfiable 

1064 if cached is not None: 

1065 return cached 

1066 

1067 if not self._specs: 

1068 self._is_unsatisfiable = False 

1069 return False 

1070 

1071 result = not self._get_ranges() 

1072 

1073 if not result: 

1074 result = self._check_arbitrary_unsatisfiable() 

1075 

1076 if not result and self.prereleases is False: 

1077 result = ranges_are_prerelease_only(self._get_ranges()) 

1078 

1079 self._is_unsatisfiable = result 

1080 return result 

1081 

1082 def _check_arbitrary_unsatisfiable(self) -> bool: 

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

1084 

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

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

1087 checks whether that candidate is excluded by other specifiers. 

1088 """ 

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

1090 if not arbitrary: 

1091 return False 

1092 

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

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

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

1096 return True 

1097 

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

1099 # it can satisfy every standard spec. 

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

1101 

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

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

1104 if ( 

1105 self.prereleases is False 

1106 and candidate is not None 

1107 and candidate.is_prerelease 

1108 ): 

1109 return True 

1110 

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

1112 if not standard: 

1113 return False 

1114 

1115 if candidate is None: 

1116 # Unparsable string cannot satisfy any standard spec. 

1117 return True 

1118 

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

1120 

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

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

1123 

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

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

1126 

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

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

1129 

1130 .. versionadded:: 26.3 

1131 """ 

1132 from .ranges import VersionRange # noqa: PLC0415 

1133 

1134 return VersionRange._from_specifier_set(self) 

1135 

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

1137 if not isinstance(other, SpecifierSet): 

1138 raise TypeError("expected a SpecifierSet") 

1139 if self._has_arbitrary or other._has_arbitrary: 

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

1141 

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

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

1144 

1145 :raises ValueError: 

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

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

1148 counts as different). 

1149 :raises TypeError: 

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

1151 

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

1153 True 

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

1155 False 

1156 

1157 .. versionadded:: 26.3 

1158 """ 

1159 self._check_relation_operand(other) 

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

1161 

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

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

1164 

1165 :raises ValueError: 

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

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

1168 counts as different). 

1169 :raises TypeError: 

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

1171 

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

1173 True 

1174 

1175 .. versionadded:: 26.3 

1176 """ 

1177 self._check_relation_operand(other) 

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

1179 

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

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

1182 

1183 :raises ValueError: 

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

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

1186 counts as different). 

1187 :raises TypeError: 

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

1189 

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

1191 True 

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

1193 False 

1194 

1195 .. versionadded:: 26.3 

1196 """ 

1197 self._check_relation_operand(other) 

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

1199 

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

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

1202 

1203 :param item: The item to check for. 

1204 

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

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

1207 

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

1209 True 

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

1211 True 

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

1213 False 

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

1215 True 

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

1217 True 

1218 """ 

1219 return self.contains(item) 

1220 

1221 def contains( 

1222 self, 

1223 item: UnparsedVersion, 

1224 prereleases: bool | None = None, 

1225 installed: bool | None = None, 

1226 ) -> bool: 

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

1228 

1229 :param item: 

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

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

1232 :param prereleases: 

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

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

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

1236 :param installed: 

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

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

1239 

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

1241 True 

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

1243 True 

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

1245 False 

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

1247 True 

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

1249 False 

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

1251 True 

1252 

1253 .. versionchanged:: 26.0 

1254 

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

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

1257 prereleases when nothing else satisfies the specifiers applies. 

1258 Earlier versions rejected it. An unparsable version now returns 

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

1260 """ 

1261 version = coerce_version(item) 

1262 

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

1264 prereleases = True 

1265 

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

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

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

1269 check_item = item 

1270 else: 

1271 check_item = version 

1272 

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

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

1275 # applies. 

1276 if ( 

1277 version is not None 

1278 and not self._has_arbitrary 

1279 and version.local is None 

1280 and self._specs 

1281 ): 

1282 if version.is_prerelease and ( 

1283 prereleases is False 

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

1285 ): 

1286 return False 

1287 

1288 bounds = self._ranges 

1289 if bounds is None: 

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

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

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

1293 # checks on the same set stay cheap. 

1294 for spec in self._specs: 

1295 match = _fast_match(spec, version) 

1296 if match is None: 

1297 break 

1298 if not match: 

1299 return False 

1300 else: 

1301 return True 

1302 

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

1304 

1305 return matches_bounds_only(bounds, version) 

1306 

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

1308 

1309 @typing.overload 

1310 def filter( 

1311 self, 

1312 iterable: Iterable[UnparsedVersionVar], 

1313 prereleases: bool | None = None, 

1314 key: None = ..., 

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

1316 

1317 @typing.overload 

1318 def filter( 

1319 self, 

1320 iterable: Iterable[T], 

1321 prereleases: bool | None = None, 

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

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

1324 

1325 def filter( 

1326 self, 

1327 iterable: Iterable[Any], 

1328 prereleases: bool | None = None, 

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

1330 ) -> Iterator[Any]: 

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

1332 

1333 :param iterable: 

1334 An iterable that can contain version strings and 

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

1336 iterable will be filtered according to the specifier. 

1337 :param prereleases: 

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

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

1340 and match prereleases if there are no other versions. 

1341 :param key: 

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

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

1344 instance to be used for filtering. 

1345 

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

1347 ['1.3'] 

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

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

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

1351 ['1.5a1'] 

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

1353 ['1.3', '1.5a1'] 

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

1355 ['1.3', '1.5a1'] 

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

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

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

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

1360 

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

1362 versions in the set. 

1363 

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

1365 ['1.3'] 

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

1367 ['1.5a1'] 

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

1369 ['1.3', '1.5a1'] 

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

1371 ['1.3', '1.5a1'] 

1372 

1373 .. versionchanged:: 26.0 

1374 

1375 Prerelease filtering now follows the PEP 440 recommendation of 

1376 yielding prereleases only when no final release is present. 

1377 

1378 .. versionchanged:: 26.1 

1379 

1380 Added the ``key`` parameter. 

1381 """ 

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

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

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

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

1386 prereleases = self.prereleases 

1387 

1388 if self._specs: 

1389 if self._has_arbitrary: 

1390 # Slow path for === 

1391 specs = self._specs 

1392 matches = ( 

1393 item 

1394 for item in iterable 

1395 if all( 

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

1397 for s in specs 

1398 ) 

1399 ) 

1400 return _apply_prereleases_filter(matches, key, prereleases) 

1401 

1402 ranges = self._ranges 

1403 if ranges is None: 

1404 ranges = self._get_ranges() 

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

1406 

1407 # Empty SpecifierSet. 

1408 return _apply_prereleases_filter(iterable, key, prereleases) 

1409 

1410 

1411def _pep440_filter_prereleases( 

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

1413) -> Iterator[Any]: 

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

1415 # Two lists used: 

1416 # * all_nonfinal to preserve order if no finals exist 

1417 # * arbitrary_strings for streaming when first final found 

1418 all_nonfinal: list[Any] = [] 

1419 arbitrary_strings: list[Any] = [] 

1420 

1421 found_final = False 

1422 for item in iterable: 

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

1424 

1425 if parsed is None: 

1426 # Arbitrary strings are always included as it is not 

1427 # possible to determine if they are prereleases, 

1428 # and they have already passed all specifiers. 

1429 if found_final: 

1430 yield item 

1431 else: 

1432 arbitrary_strings.append(item) 

1433 all_nonfinal.append(item) 

1434 continue 

1435 

1436 if not parsed.is_prerelease: 

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

1438 if not found_final: 

1439 yield from arbitrary_strings 

1440 found_final = True 

1441 yield item 

1442 continue 

1443 

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

1445 if not found_final: 

1446 all_nonfinal.append(item) 

1447 

1448 # No finals found - yield all buffered items 

1449 if not found_final: 

1450 yield from all_nonfinal