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

454 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 

13__lazy_modules__ = {f"{__spec__.parent}._ranges", f"{__spec__.parent}.utils"} 

14 

15import abc 

16import builtins 

17import re 

18import sys 

19import typing 

20from typing import ( 

21 TYPE_CHECKING, 

22 Any, 

23 Final, 

24 TypeVar, 

25) 

26 

27from ._ranges import ( 

28 FULL_RANGE, 

29 bounds_for_spec, 

30 coerce_version, 

31 filter_by_ranges, 

32 intersect_specifier_bounds, 

33 matches_bounds_only, 

34 ranges_are_prerelease_only, 

35 resolve_prereleases, 

36 trim_release, 

37) 

38from .utils import canonicalize_version 

39from .version import Version 

40 

41if TYPE_CHECKING: 

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

43 from typing import TypeGuard 

44 

45 from typing_extensions import Self 

46 

47 from . import ranges 

48 from ._ranges import Interval 

49 

50 

51__all__ = [ 

52 "BaseSpecifier", 

53 "InvalidSpecifier", 

54 "Specifier", 

55 "SpecifierSet", 

56] 

57 

58 

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

60 return __all__ 

61 

62 

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

64 return ( 

65 isinstance(spec, tuple) 

66 and len(spec) == 2 

67 and isinstance(spec[0], str) 

68 and isinstance(spec[1], str) 

69 ) 

70 

71 

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

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

74 

75 

76T = TypeVar("T") 

77UnparsedVersion = Version | str 

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

79 

80 

81# Sentinel for __replace__, typed as Any so it can be the default for any field. 

82# mypy is missing sentinel currently 

83_UNSET: Any = object() if sys.version_info < (3, 15) else builtins.sentinel("_UNSET") # type: ignore[attr-defined] 

84 

85 

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

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

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

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

90 ">=": Version.__ge__, 

91 "<=": Version.__le__, 

92 "==": Version.__eq__, 

93 "!=": Version.__ne__, 

94} 

95 

96 

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

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

99 

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

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

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

103 """ 

104 op_str, ver_str = specifier._spec 

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

106 return None 

107 

108 direct_compare = _DIRECT_COMPARE_OPS.get(op_str) 

109 if direct_compare is not None: 

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

111 

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

113 spec_v = specifier._require_spec_version(ver_str) 

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

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

116 # Otherwise a direct cmpkey comparison is correct. 

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

118 spec_v.release 

119 ): 

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

121 return None 

122 

123 return None 

124 

125 

126class InvalidSpecifier(ValueError): 

127 """ 

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

129 string that is invalid. 

130 

131 >>> Specifier("lolwat") 

132 Traceback (most recent call last): 

133 ... 

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

135 """ 

136 

137 

138class BaseSpecifier(metaclass=abc.ABCMeta): 

139 """ 

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

141 """ 

142 

143 __slots__ = () 

144 __match_args__ = ("_str",) 

145 

146 @property 

147 def _str(self) -> str: 

148 """Internal property for match_args""" 

149 return str(self) 

150 

151 @abc.abstractmethod 

152 def __str__(self) -> str: 

153 """ 

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

155 should be representative of the Specifier itself. 

156 """ 

157 

158 @abc.abstractmethod 

159 def __hash__(self) -> int: 

160 """ 

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

162 """ 

163 

164 @abc.abstractmethod 

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

166 """ 

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

168 objects are equal. 

169 

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

171 """ 

172 

173 @property 

174 @abc.abstractmethod 

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

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

177 

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

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

180 """ 

181 

182 @prereleases.setter # noqa: B027 

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

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

185 

186 :param value: The value to set. 

187 """ 

188 

189 @abc.abstractmethod 

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

191 """ 

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

193 """ 

194 

195 @typing.overload 

196 def filter( 

197 self, 

198 iterable: Iterable[UnparsedVersionVar], 

199 prereleases: bool | None = None, 

200 key: None = ..., 

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

202 

203 @typing.overload 

204 def filter( 

205 self, 

206 iterable: Iterable[T], 

207 prereleases: bool | None = None, 

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

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

210 

211 @abc.abstractmethod 

212 def filter( 

213 self, 

214 iterable: Iterable[Any], 

215 prereleases: bool | None = None, 

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

217 ) -> Iterator[Any]: 

218 """ 

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

220 are contained within this specifier are allowed in it. 

221 """ 

222 

223 

224class Specifier(BaseSpecifier): 

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

226 

227 .. tip:: 

228 

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

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

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

232 

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

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

235 

236 .. versionchanged:: 26.2 

237 

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

239 be unpickled with future releases. Backward compatibility with pickles 

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

241 release. 

242 """ 

243 

244 __slots__ = ( 

245 "_canonical_spec_cache", 

246 "_prereleases", 

247 "_ranges", 

248 "_spec", 

249 "_spec_version", 

250 ) 

251 

252 _specifier_regex_str = r""" 

253 (?: 

254 (?: 

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

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

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

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

259 # but included entirely as an escape hatch. 

260 === # Only match for the identity operator 

261 \s* 

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

263 # we match everything except for whitespace, a 

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

265 # since versions can be enclosed in them. 

266 ) 

267 | 

268 (?: 

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

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

271 # operators separately to enable that. 

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

273 

274 \s* 

275 v? 

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

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

278 

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

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

281 (?: 

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

283 | 

284 (?a: # pre release 

285 [-_\.]? 

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

287 [-_\.]? 

288 [0-9]* 

289 )? 

290 (?a: # post release 

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

292 )? 

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

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

295 )? 

296 ) 

297 | 

298 (?: 

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

300 # release segment. 

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

302 

303 \s* 

304 v? 

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

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

307 (?a: # pre release 

308 [-_\.]? 

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

310 [-_\.]? 

311 [0-9]* 

312 )? 

313 (?a: # post release 

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

315 )? 

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

317 ) 

318 | 

319 (?: 

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

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

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

323 # matching wild cards. 

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

325 

326 \s* 

327 v? 

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

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

330 (?a: # pre release 

331 [-_\.]? 

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

333 [-_\.]? 

334 [0-9]* 

335 )? 

336 (?a: # post release 

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

338 )? 

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

340 ) 

341 ) 

342 """ 

343 

344 _regex = re.compile( 

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

346 ) 

347 

348 # Legacy unused attribute, kept for backward compatibility 

349 _operators: Final = { 

350 "~=": "compatible", 

351 "==": "equal", 

352 "!=": "not_equal", 

353 "<=": "less_than_equal", 

354 ">=": "greater_than_equal", 

355 "<": "less_than", 

356 ">": "greater_than", 

357 "===": "arbitrary", 

358 } 

359 

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

361 """Initialize a Specifier instance. 

362 

363 :param spec: 

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

365 normalized before use. 

366 :param prereleases: 

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

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

369 given specifiers. 

370 :raises InvalidSpecifier: 

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

372 """ 

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

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

375 

376 spec = spec.strip() 

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

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

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

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

381 else: 

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

383 

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

385 

386 # Store whether or not this Specifier should accept prereleases 

387 self._prereleases = prereleases 

388 

389 # Specifier version cache 

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

391 

392 # Version range cache (populated by _to_ranges) 

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

394 

395 # Canonical (operator, version) cache (populated by _canonical_spec) 

396 self._canonical_spec_cache: tuple[str, str] | None = None 

397 

398 def __replace__( 

399 self, 

400 *, 

401 operator: str = _UNSET, 

402 version: str = _UNSET, 

403 prereleases: bool | None = _UNSET, 

404 ) -> Self: 

405 """__replace__(*, operator=..., version=..., prereleases=...) 

406 

407 Return a specifier with the given parts replaced. If no part changes, 

408 return ``self``. 

409 

410 :param str operator: 

411 Replacement comparison operator. Omit to retain the current operator. 

412 :param str version: 

413 Replacement version string. Omit to retain the current version. 

414 :param prereleases: 

415 Replacement prerelease override. Omit to retain the current override; 

416 ``None`` restores automatic detection. 

417 :type prereleases: bool | None 

418 

419 .. versionadded:: 26.4 

420 """ 

421 new_operator = self.operator if operator is _UNSET else operator 

422 new_version = self.version if version is _UNSET else version 

423 new_prereleases = self._prereleases if prereleases is _UNSET else prereleases 

424 if ( 

425 new_operator == self.operator 

426 and new_version == self.version 

427 and new_prereleases == self._prereleases 

428 ): 

429 return self 

430 

431 return self.__class__( 

432 f"{new_operator}{new_version}", prereleases=new_prereleases 

433 ) 

434 

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

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

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

438 return self._spec_version[1] 

439 

440 version_specifier = coerce_version(version) 

441 if version_specifier is None: 

442 return None 

443 

444 self._spec_version = (version, version_specifier) 

445 return version_specifier 

446 

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

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

449 

450 This method should only be called for operators where version 

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

452 """ 

453 spec_version = self._get_spec_version(version) 

454 assert spec_version is not None 

455 return spec_version 

456 

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

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

459 

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

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

462 """ 

463 if self._ranges is not None: 

464 return self._ranges 

465 

466 op = self.operator 

467 ver_str = self.version 

468 

469 if op == "===": 

470 result: Sequence[Interval] = FULL_RANGE 

471 else: 

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

473 result = bounds_for_spec(op, ver_str, version) 

474 

475 self._ranges = result 

476 return result 

477 

478 @property 

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

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

481 # blindly use that. 

482 if self._prereleases is not None: 

483 return self._prereleases 

484 

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

486 # the version in the specifier is a prerelease. 

487 operator, version_str = self._spec 

488 if operator == "!=": 

489 return False 

490 

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

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

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

494 return False 

495 

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

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

498 version = self._get_spec_version(version_str) 

499 if version is None: 

500 return None 

501 

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

503 # object implies pre-releases. 

504 return version.is_prerelease 

505 

506 @prereleases.setter 

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

508 self._prereleases = value 

509 

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

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

512 # ((operator, version), prereleases) 

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

514 return (self._spec, self._prereleases) 

515 

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

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

518 self._spec_version = None 

519 self._ranges = None 

520 self._canonical_spec_cache = None 

521 

522 if isinstance(state, tuple): 

523 if len(state) == 2: 

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

525 spec, prereleases = state 

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

527 self._spec = spec 

528 self._prereleases = prereleases 

529 return 

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

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

532 _, slot_dict = state 

533 spec = slot_dict.get("_spec") 

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

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

536 self._spec = spec 

537 self._prereleases = prereleases 

538 return 

539 if isinstance(state, dict): 

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

541 spec = state.get("_spec") 

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

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

544 self._spec = spec 

545 self._prereleases = prereleases 

546 return 

547 

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

549 

550 @property 

551 def operator(self) -> str: 

552 """The operator of this specifier. 

553 

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

555 '==' 

556 """ 

557 return self._spec[0] 

558 

559 @property 

560 def version(self) -> str: 

561 """The version of this specifier. 

562 

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

564 '1.2.3' 

565 """ 

566 return self._spec[1] 

567 

568 def __repr__(self) -> str: 

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

570 

571 >>> Specifier('>=1.0.0') 

572 <Specifier('>=1.0.0')> 

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

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

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

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

577 """ 

578 pre = ( 

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

580 if self._prereleases is not None 

581 else "" 

582 ) 

583 

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

585 

586 def __str__(self) -> str: 

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

588 

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

590 '>=1.0.0' 

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

592 '>=1.0.0' 

593 """ 

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

595 

596 @property 

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

598 cached = self._canonical_spec_cache 

599 if cached is not None: 

600 return cached 

601 

602 operator, version = self._spec 

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

604 result = self._spec 

605 else: 

606 canonical = canonicalize_version( 

607 self._require_spec_version(version), 

608 strip_trailing_zero=(operator != "~="), 

609 ) 

610 # Most versions are already canonical, so reuse the existing tuple rather 

611 # than retaining a second copy of a string equal to the one we hold. 

612 result = self._spec if canonical == version else (operator, canonical) 

613 

614 self._canonical_spec_cache = result 

615 return result 

616 

617 def __hash__(self) -> int: 

618 return hash(self._canonical_spec) 

619 

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

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

622 

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

624 

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

626 

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

628 True 

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

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

631 True 

632 >>> Specifier("==1.2.3") == "==1.2.3" 

633 True 

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

635 False 

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

637 False 

638 """ 

639 if isinstance(other, str): 

640 try: 

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

642 except InvalidSpecifier: 

643 return NotImplemented 

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

645 return NotImplemented 

646 

647 return self._canonical_spec == other._canonical_spec 

648 

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

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

651 

652 :param item: The item to check for. 

653 

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

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

656 

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

658 True 

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

660 True 

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

662 False 

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

664 True 

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

666 True 

667 """ 

668 return self.contains(item) 

669 

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

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

672 

673 :param item: 

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

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

676 :param prereleases: 

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

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

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

680 

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

682 True 

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

684 True 

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

686 False 

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

688 True 

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

690 False 

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

692 True 

693 

694 .. versionchanged:: 26.0 

695 

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

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

698 prereleases when nothing else satisfies the specifier applies. 

699 Earlier versions rejected it. An unparsable version now returns 

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

701 """ 

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

703 # be wasted. 

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

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

706 

707 parsed = coerce_version(item) 

708 if parsed is None: 

709 # Standard operators never match an unparsable input. 

710 return False 

711 

712 if prereleases is None: 

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

714 

715 if prereleases is False and parsed.is_prerelease: 

716 return False 

717 

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

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

720 match = _fast_match(self, parsed) 

721 if match is not None: 

722 return match 

723 

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

725 

726 @typing.overload 

727 def filter( 

728 self, 

729 iterable: Iterable[UnparsedVersionVar], 

730 prereleases: bool | None = None, 

731 key: None = ..., 

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

733 

734 @typing.overload 

735 def filter( 

736 self, 

737 iterable: Iterable[T], 

738 prereleases: bool | None = None, 

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

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

741 

742 def filter( 

743 self, 

744 iterable: Iterable[Any], 

745 prereleases: bool | None = None, 

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

747 ) -> Iterator[Any]: 

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

749 

750 :param iterable: 

751 An iterable that can contain version strings and 

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

753 iterable will be filtered according to the specifier. 

754 :param prereleases: 

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

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

757 and match prereleases if there are no other versions. 

758 :param key: 

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

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

761 instance to be used for filtering. 

762 

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

764 ['1.3'] 

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

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

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

768 ['1.5a1'] 

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

770 ['1.3', '1.5a1'] 

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

772 ['1.3', '1.5a1'] 

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

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

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

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

777 

778 .. versionchanged:: 26.1 

779 

780 Added the ``key`` parameter. 

781 """ 

782 if prereleases is None: 

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

784 

785 if self.operator == "===": 

786 spec_lower = self.version.lower() 

787 matches = ( 

788 item 

789 for item in iterable 

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

791 ) 

792 return _apply_prereleases_filter(matches, key, prereleases) 

793 

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

795 

796 

797def _apply_prereleases_filter( 

798 matches: Iterable[Any], 

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

800 prereleases: bool | None, 

801) -> Iterator[Any]: 

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

803 

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

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

806 """ 

807 if prereleases is None: 

808 return _pep440_filter_prereleases(matches, key) 

809 if prereleases: 

810 return iter(matches) 

811 return ( 

812 item 

813 for item in matches 

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

815 or not parsed.is_prerelease 

816 ) 

817 

818 

819class SpecifierSet(BaseSpecifier): 

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

821 

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

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

824 

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

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

827 releases. 

828 

829 .. versionchanged:: 26.2 

830 

831 Added a stable pickle format. Pickles created with 

832 packaging 26.2+ can be unpickled with future releases. 

833 Backward compatibility with pickles from 

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

835 release. 

836 """ 

837 

838 __slots__ = ( 

839 "_canonicalized", 

840 "_has_arbitrary", 

841 "_is_unsatisfiable", 

842 "_prereleases", 

843 "_ranges", 

844 "_specs", 

845 ) 

846 

847 def __init__( 

848 self, 

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

850 prereleases: bool | None = None, 

851 ) -> None: 

852 """Initialize a SpecifierSet instance. 

853 

854 :param specifiers: 

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

856 specifiers which will be parsed and normalized before use. 

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

858 as is. Plain ``str`` elements are parsed as if passed to 

859 :class:`Specifier`, so ``SpecifierSet([">=1.0"])`` equals 

860 ``SpecifierSet(">=1.0")``. 

861 :param prereleases: 

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

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

864 given specifiers. 

865 

866 :raises InvalidSpecifier: 

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

868 raised. 

869 :raises TypeError: 

870 If an element of the iterable form is neither a :class:`Specifier` 

871 nor a ``str``. 

872 """ 

873 

874 if isinstance(specifiers, str): 

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

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

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

878 

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

880 # Fast substring check; avoids iterating parsed specs. 

881 self._has_arbitrary = "===" in specifiers 

882 else: 

883 # List comprehension is faster than generator below 

884 self._specs = tuple( 

885 [s if isinstance(s, Specifier) else Specifier(s) for s in specifiers] 

886 ) 

887 self._has_arbitrary = any(s.operator == "===" for s in self._specs) 

888 

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

890 self._is_unsatisfiable: bool | None = None 

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

892 

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

894 # we accept prereleases or not. 

895 self._prereleases = prereleases 

896 

897 def __replace__( 

898 self, 

899 *, 

900 specifiers: str | Iterable[Specifier | str] = _UNSET, 

901 prereleases: bool | None = _UNSET, 

902 ) -> Self: 

903 """__replace__(*, specifiers=..., prereleases=...) 

904 

905 Return a specifier set with the given parts replaced. If ``specifiers`` 

906 is omitted and the prerelease override is unchanged, return ``self``. 

907 

908 :param specifiers: 

909 Replacement specifiers as a string or an iterable of ``Specifier`` 

910 and ``str`` instances. Omit to retain the current specifiers. 

911 :type specifiers: str | Iterable[Specifier | str] 

912 :param prereleases: 

913 Replacement prerelease override. Omit to retain the current override; 

914 ``None`` restores automatic detection. 

915 :type prereleases: bool | None 

916 

917 .. versionadded:: 26.4 

918 """ 

919 new_specifiers = self._specs if specifiers is _UNSET else specifiers 

920 new_prereleases = self._prereleases if prereleases is _UNSET else prereleases 

921 if specifiers is _UNSET and new_prereleases == self._prereleases: 

922 return self 

923 

924 return self.__class__(new_specifiers, prereleases=new_prereleases) 

925 

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

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

928 if not self._canonicalized: 

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

930 self._canonicalized = True 

931 return self._specs 

932 

933 @property 

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

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

936 # pass that through here. 

937 if self._prereleases is not None: 

938 return self._prereleases 

939 

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

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

942 # pre-releases or not. 

943 if not self._specs: 

944 return None 

945 

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

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

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

949 return True 

950 

951 return None 

952 

953 @prereleases.setter 

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

955 self._prereleases = value 

956 self._is_unsatisfiable = None 

957 

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

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

960 # (specs, prereleases) 

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

962 return (self._specs, self._prereleases) 

963 

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

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

966 self._ranges = None 

967 self._is_unsatisfiable = None 

968 

969 if isinstance(state, tuple): 

970 if len(state) == 2: 

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

972 specs, prereleases = state 

973 if ( 

974 isinstance(specs, tuple) 

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

976 and _validate_pre(prereleases) 

977 ): 

978 self._specs = specs 

979 self._prereleases = prereleases 

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

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

982 return 

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

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

985 _, slot_dict = state 

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

987 prereleases = slot_dict.get("_prereleases") 

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

989 if isinstance(specs, frozenset): 

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

991 if ( 

992 isinstance(specs, tuple) 

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

994 and _validate_pre(prereleases) 

995 ): 

996 self._specs = specs 

997 self._prereleases = prereleases 

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

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

1000 return 

1001 if isinstance(state, dict): 

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

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

1004 prereleases = state.get("_prereleases") 

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

1006 if isinstance(specs, frozenset): 

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

1008 if ( 

1009 isinstance(specs, tuple) 

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

1011 and _validate_pre(prereleases) 

1012 ): 

1013 self._specs = specs 

1014 self._prereleases = prereleases 

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

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

1017 return 

1018 

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

1020 

1021 def __repr__(self) -> str: 

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

1023 

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

1025 match the input string. 

1026 

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

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

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

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

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

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

1033 """ 

1034 pre = ( 

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

1036 if self._prereleases is not None 

1037 else "" 

1038 ) 

1039 

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

1041 

1042 def __str__(self) -> str: 

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

1044 

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

1046 match the input string. 

1047 

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

1049 '!=1.0.1,>=1.0.0' 

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

1051 '!=1.0.1,>=1.0.0' 

1052 """ 

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

1054 

1055 def __hash__(self) -> int: 

1056 return hash(self._canonical_specs()) 

1057 

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

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

1060 

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

1062 

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

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

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

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

1067 """ 

1068 if isinstance(other, str): 

1069 other = SpecifierSet(other) 

1070 elif not isinstance(other, SpecifierSet): 

1071 return NotImplemented 

1072 

1073 specifier = SpecifierSet() 

1074 specifier._specs = self._specs + other._specs 

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

1076 specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary 

1077 

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

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

1080 specifier._prereleases = other._prereleases 

1081 elif other._prereleases is None: 

1082 specifier._prereleases = self._prereleases 

1083 else: 

1084 raise ValueError( 

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

1086 ) 

1087 

1088 return specifier 

1089 

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

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

1092 

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

1094 

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

1096 

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

1098 True 

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

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

1101 True 

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

1103 True 

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

1105 False 

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

1107 False 

1108 """ 

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

1110 other = SpecifierSet(str(other)) 

1111 elif not isinstance(other, SpecifierSet): 

1112 return NotImplemented 

1113 

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

1115 

1116 def __len__(self) -> int: 

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

1118 return len(self._specs) 

1119 

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

1121 """ 

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

1123 in this specifier set. 

1124 

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

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

1127 """ 

1128 return iter(self._specs) 

1129 

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

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

1132 

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

1134 is non-empty. 

1135 """ 

1136 if self._ranges is not None: 

1137 return self._ranges 

1138 

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

1140 return self._ranges 

1141 

1142 def is_unsatisfiable(self) -> bool: 

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

1144 

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

1146 

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

1148 True 

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

1150 False 

1151 >>> SpecifierSet("").is_unsatisfiable() 

1152 False 

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

1154 True 

1155 

1156 .. versionadded:: 26.1 

1157 """ 

1158 cached = self._is_unsatisfiable 

1159 if cached is not None: 

1160 return cached 

1161 

1162 if not self._specs: 

1163 self._is_unsatisfiable = False 

1164 return False 

1165 

1166 result = not self._get_ranges() 

1167 

1168 if not result: 

1169 result = self._check_arbitrary_unsatisfiable() 

1170 

1171 if not result and self.prereleases is False: 

1172 result = ranges_are_prerelease_only(self._get_ranges()) 

1173 

1174 self._is_unsatisfiable = result 

1175 return result 

1176 

1177 def _check_arbitrary_unsatisfiable(self) -> bool: 

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

1179 

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

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

1182 checks whether that candidate is excluded by other specifiers. 

1183 """ 

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

1185 if not arbitrary: 

1186 return False 

1187 

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

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

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

1191 return True 

1192 

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

1194 # it can satisfy every standard spec. 

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

1196 

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

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

1199 if ( 

1200 self.prereleases is False 

1201 and candidate is not None 

1202 and candidate.is_prerelease 

1203 ): 

1204 return True 

1205 

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

1207 if not standard: 

1208 return False 

1209 

1210 if candidate is None: 

1211 # Unparsable string cannot satisfy any standard spec. 

1212 return True 

1213 

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

1215 

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

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

1218 

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

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

1221 

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

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

1224 

1225 .. versionadded:: 26.3 

1226 """ 

1227 from .ranges import VersionRange # noqa: PLC0415 

1228 

1229 return VersionRange._from_specifier_set(self) 

1230 

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

1232 if not isinstance(other, SpecifierSet): 

1233 raise TypeError("expected a SpecifierSet") 

1234 if self._has_arbitrary or other._has_arbitrary: 

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

1236 

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

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

1239 

1240 :raises ValueError: 

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

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

1243 counts as different). 

1244 :raises TypeError: 

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

1246 

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

1248 True 

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

1250 False 

1251 

1252 .. versionadded:: 26.3 

1253 """ 

1254 self._check_relation_operand(other) 

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

1256 

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

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

1259 

1260 :raises ValueError: 

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

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

1263 counts as different). 

1264 :raises TypeError: 

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

1266 

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

1268 True 

1269 

1270 .. versionadded:: 26.3 

1271 """ 

1272 self._check_relation_operand(other) 

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

1274 

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

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

1277 

1278 :raises ValueError: 

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

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

1281 counts as different). 

1282 :raises TypeError: 

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

1284 

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

1286 True 

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

1288 False 

1289 

1290 .. versionadded:: 26.3 

1291 """ 

1292 self._check_relation_operand(other) 

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

1294 

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

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

1297 

1298 :param item: The item to check for. 

1299 

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

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

1302 

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

1304 True 

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

1306 True 

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

1308 False 

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

1310 True 

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

1312 True 

1313 """ 

1314 return self.contains(item) 

1315 

1316 def contains( 

1317 self, 

1318 item: UnparsedVersion, 

1319 prereleases: bool | None = None, 

1320 installed: bool | None = None, 

1321 ) -> bool: 

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

1323 

1324 :param item: 

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

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

1327 :param prereleases: 

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

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

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

1331 :param installed: 

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

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

1334 

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

1336 True 

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

1338 True 

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

1340 False 

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

1342 True 

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

1344 False 

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

1346 True 

1347 

1348 .. versionchanged:: 26.0 

1349 

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

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

1352 prereleases when nothing else satisfies the specifiers applies. 

1353 Earlier versions rejected it. An unparsable version now returns 

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

1355 """ 

1356 version = coerce_version(item) 

1357 

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

1359 prereleases = True 

1360 

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

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

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

1364 check_item = item 

1365 else: 

1366 check_item = version 

1367 

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

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

1370 # applies. 

1371 if ( 

1372 version is not None 

1373 and not self._has_arbitrary 

1374 and version.local is None 

1375 and self._specs 

1376 ): 

1377 if version.is_prerelease and ( 

1378 prereleases is False 

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

1380 ): 

1381 return False 

1382 

1383 bounds = self._ranges 

1384 if bounds is None: 

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

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

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

1388 # checks on the same set stay cheap. 

1389 for spec in self._specs: 

1390 match = _fast_match(spec, version) 

1391 if match is None: 

1392 break 

1393 if not match: 

1394 return False 

1395 else: 

1396 return True 

1397 

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

1399 

1400 return matches_bounds_only(bounds, version) 

1401 

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

1403 

1404 @typing.overload 

1405 def filter( 

1406 self, 

1407 iterable: Iterable[UnparsedVersionVar], 

1408 prereleases: bool | None = None, 

1409 key: None = ..., 

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

1411 

1412 @typing.overload 

1413 def filter( 

1414 self, 

1415 iterable: Iterable[T], 

1416 prereleases: bool | None = None, 

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

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

1419 

1420 def filter( 

1421 self, 

1422 iterable: Iterable[Any], 

1423 prereleases: bool | None = None, 

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

1425 ) -> Iterator[Any]: 

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

1427 

1428 :param iterable: 

1429 An iterable that can contain version strings and 

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

1431 iterable will be filtered according to the specifier. 

1432 :param prereleases: 

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

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

1435 and match prereleases if there are no other versions. 

1436 :param key: 

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

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

1439 instance to be used for filtering. 

1440 

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

1442 ['1.3'] 

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

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

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

1446 ['1.5a1'] 

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

1448 ['1.3', '1.5a1'] 

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

1450 ['1.3', '1.5a1'] 

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

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

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

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

1455 

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

1457 versions in the set. 

1458 

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

1460 ['1.3'] 

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

1462 ['1.5a1'] 

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

1464 ['1.3', '1.5a1'] 

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

1466 ['1.3', '1.5a1'] 

1467 

1468 .. versionchanged:: 26.0 

1469 

1470 Prerelease filtering now follows the PEP 440 recommendation of 

1471 yielding prereleases only when no final release is present. 

1472 

1473 .. versionchanged:: 26.1 

1474 

1475 Added the ``key`` parameter. 

1476 """ 

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

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

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

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

1481 prereleases = self.prereleases 

1482 

1483 if self._specs: 

1484 if self._has_arbitrary: 

1485 # Slow path for === 

1486 specs = self._specs 

1487 matches = ( 

1488 item 

1489 for item in iterable 

1490 if all( 

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

1492 for s in specs 

1493 ) 

1494 ) 

1495 return _apply_prereleases_filter(matches, key, prereleases) 

1496 

1497 ranges = self._ranges 

1498 if ranges is None: 

1499 ranges = self._get_ranges() 

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

1501 

1502 # Empty SpecifierSet. 

1503 return _apply_prereleases_filter(iterable, key, prereleases) 

1504 

1505 

1506def _pep440_filter_prereleases( 

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

1508) -> Iterator[Any]: 

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

1510 # Two lists used: 

1511 # * all_nonfinal to preserve order if no finals exist 

1512 # * arbitrary_strings for streaming when first final found 

1513 all_nonfinal: list[Any] = [] 

1514 arbitrary_strings: list[Any] = [] 

1515 

1516 found_final = False 

1517 for item in iterable: 

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

1519 

1520 if parsed is None: 

1521 # Arbitrary strings are always included as it is not 

1522 # possible to determine if they are prereleases, 

1523 # and they have already passed all specifiers. 

1524 if found_final: 

1525 yield item 

1526 else: 

1527 arbitrary_strings.append(item) 

1528 all_nonfinal.append(item) 

1529 continue 

1530 

1531 if not parsed.is_prerelease: 

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

1533 if not found_final: 

1534 yield from arbitrary_strings 

1535 found_final = True 

1536 yield item 

1537 continue 

1538 

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

1540 if not found_final: 

1541 all_nonfinal.append(item) 

1542 

1543 # No finals found - yield all buffered items 

1544 if not found_final: 

1545 yield from all_nonfinal