Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/setuptools/_vendor/wheel/vendored/packaging/specifiers.py: 15%

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

255 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 

11import abc 

12import itertools 

13import re 

14from typing import Callable, Iterable, Iterator, List, Optional, Tuple, TypeVar, Union 

15 

16from .utils import canonicalize_version 

17from .version import Version 

18 

19UnparsedVersion = Union[Version, str] 

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

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

22 

23 

24def _coerce_version(version: UnparsedVersion) -> Version: 

25 if not isinstance(version, Version): 

26 version = Version(version) 

27 return version 

28 

29 

30class InvalidSpecifier(ValueError): 

31 """ 

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

33 string that is invalid. 

34 

35 >>> Specifier("lolwat") 

36 Traceback (most recent call last): 

37 ... 

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

39 """ 

40 

41 

42class BaseSpecifier(metaclass=abc.ABCMeta): 

43 @abc.abstractmethod 

44 def __str__(self) -> str: 

45 """ 

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

47 should be representative of the Specifier itself. 

48 """ 

49 

50 @abc.abstractmethod 

51 def __hash__(self) -> int: 

52 """ 

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

54 """ 

55 

56 @abc.abstractmethod 

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

58 """ 

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

60 objects are equal. 

61 

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

63 """ 

64 

65 @property 

66 @abc.abstractmethod 

67 def prereleases(self) -> Optional[bool]: 

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

69 

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

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

72 """ 

73 

74 @prereleases.setter 

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

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

77 

78 :param value: The value to set. 

79 """ 

80 

81 @abc.abstractmethod 

82 def contains(self, item: str, prereleases: Optional[bool] = None) -> bool: 

83 """ 

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

85 """ 

86 

87 @abc.abstractmethod 

88 def filter( 

89 self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None 

90 ) -> Iterator[UnparsedVersionVar]: 

91 """ 

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

93 are contained within this specifier are allowed in it. 

94 """ 

95 

96 

97class Specifier(BaseSpecifier): 

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

99 

100 .. tip:: 

101 

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

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

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

105 """ 

106 

107 _operator_regex_str = r""" 

108 (?P<operator>(~=|==|!=|<=|>=|<|>|===)) 

109 """ 

110 _version_regex_str = r""" 

111 (?P<version> 

112 (?: 

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

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

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

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

117 # but included entirely as an escape hatch. 

118 (?<====) # Only match for the identity operator 

119 \s* 

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

121 # we match everything except for whitespace, a 

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

123 # since versions can be enclosed in them. 

124 ) 

125 | 

126 (?: 

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

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

129 # operators separately to enable that. 

130 (?<===|!=) # Only match for equals and not equals 

131 

132 \s* 

133 v? 

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

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

136 

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

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

139 (?: 

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

141 | 

142 (?: # pre release 

143 [-_\.]? 

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

145 [-_\.]? 

146 [0-9]* 

147 )? 

148 (?: # post release 

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

150 )? 

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

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

153 )? 

154 ) 

155 | 

156 (?: 

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

158 # release segment. 

159 (?<=~=) # Only match for the compatible operator 

160 

161 \s* 

162 v? 

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

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

165 (?: # pre release 

166 [-_\.]? 

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

168 [-_\.]? 

169 [0-9]* 

170 )? 

171 (?: # post release 

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

173 )? 

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

175 ) 

176 | 

177 (?: 

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

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

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

181 # matching wild cards. 

182 (?<!==|!=|~=) # We have special cases for these 

183 # operators so we want to make sure they 

184 # don't match here. 

185 

186 \s* 

187 v? 

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

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

190 (?: # pre release 

191 [-_\.]? 

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

193 [-_\.]? 

194 [0-9]* 

195 )? 

196 (?: # post release 

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

198 )? 

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

200 ) 

201 ) 

202 """ 

203 

204 _regex = re.compile( 

205 r"^\s*" + _operator_regex_str + _version_regex_str + r"\s*$", 

206 re.VERBOSE | re.IGNORECASE, 

207 ) 

208 

209 _operators = { 

210 "~=": "compatible", 

211 "==": "equal", 

212 "!=": "not_equal", 

213 "<=": "less_than_equal", 

214 ">=": "greater_than_equal", 

215 "<": "less_than", 

216 ">": "greater_than", 

217 "===": "arbitrary", 

218 } 

219 

220 def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None: 

221 """Initialize a Specifier instance. 

222 

223 :param spec: 

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

225 normalized before use. 

226 :param prereleases: 

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

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

229 given specifiers. 

230 :raises InvalidSpecifier: 

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

232 """ 

233 match = self._regex.search(spec) 

234 if not match: 

235 raise InvalidSpecifier(f"Invalid specifier: '{spec}'") 

236 

237 self._spec: Tuple[str, str] = ( 

238 match.group("operator").strip(), 

239 match.group("version").strip(), 

240 ) 

241 

242 # Store whether or not this Specifier should accept prereleases 

243 self._prereleases = prereleases 

244 

245 # https://github.com/python/mypy/pull/13475#pullrequestreview-1079784515 

246 @property # type: ignore[override] 

247 def prereleases(self) -> bool: 

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

249 # blindly use that. 

250 if self._prereleases is not None: 

251 return self._prereleases 

252 

253 # Look at all of our specifiers and determine if they are inclusive 

254 # operators, and if they are if they are including an explicit 

255 # prerelease. 

256 operator, version = self._spec 

257 if operator in ["==", ">=", "<=", "~=", "==="]: 

258 # The == specifier can include a trailing .*, if it does we 

259 # want to remove before parsing. 

260 if operator == "==" and version.endswith(".*"): 

261 version = version[:-2] 

262 

263 # Parse the version, and if it is a pre-release than this 

264 # specifier allows pre-releases. 

265 if Version(version).is_prerelease: 

266 return True 

267 

268 return False 

269 

270 @prereleases.setter 

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

272 self._prereleases = value 

273 

274 @property 

275 def operator(self) -> str: 

276 """The operator of this specifier. 

277 

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

279 '==' 

280 """ 

281 return self._spec[0] 

282 

283 @property 

284 def version(self) -> str: 

285 """The version of this specifier. 

286 

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

288 '1.2.3' 

289 """ 

290 return self._spec[1] 

291 

292 def __repr__(self) -> str: 

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

294 

295 >>> Specifier('>=1.0.0') 

296 <Specifier('>=1.0.0')> 

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

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

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

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

301 """ 

302 pre = ( 

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

304 if self._prereleases is not None 

305 else "" 

306 ) 

307 

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

309 

310 def __str__(self) -> str: 

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

312 

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

314 '>=1.0.0' 

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

316 '>=1.0.0' 

317 """ 

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

319 

320 @property 

321 def _canonical_spec(self) -> Tuple[str, str]: 

322 canonical_version = canonicalize_version( 

323 self._spec[1], 

324 strip_trailing_zero=(self._spec[0] != "~="), 

325 ) 

326 return self._spec[0], canonical_version 

327 

328 def __hash__(self) -> int: 

329 return hash(self._canonical_spec) 

330 

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

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

333 

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

335 

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

337 

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

339 True 

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

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

342 True 

343 >>> Specifier("==1.2.3") == "==1.2.3" 

344 True 

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

346 False 

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

348 False 

349 """ 

350 if isinstance(other, str): 

351 try: 

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

353 except InvalidSpecifier: 

354 return NotImplemented 

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

356 return NotImplemented 

357 

358 return self._canonical_spec == other._canonical_spec 

359 

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

361 operator_callable: CallableOperator = getattr( 

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

363 ) 

364 return operator_callable 

365 

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

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

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

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

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

371 # the other specifiers. 

372 

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

374 # ignore suffix segments. 

375 prefix = _version_join( 

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

377 ) 

378 

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

380 prefix += ".*" 

381 

382 return self._get_operator(">=")(prospective, spec) and self._get_operator("==")( 

383 prospective, prefix 

384 ) 

385 

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

387 # We need special logic to handle prefix matching 

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

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

390 normalized_prospective = canonicalize_version( 

391 prospective.public, strip_trailing_zero=False 

392 ) 

393 # Get the normalized version string ignoring the trailing .* 

394 normalized_spec = canonicalize_version(spec[:-2], strip_trailing_zero=False) 

395 # Split the spec out by bangs and dots, and pretend that there is 

396 # an implicit dot in between a release segment and a pre-release segment. 

397 split_spec = _version_split(normalized_spec) 

398 

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

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

401 # a pre-release segment. 

402 split_prospective = _version_split(normalized_prospective) 

403 

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

405 # shortened version. 

406 padded_prospective, _ = _pad_version(split_prospective, split_spec) 

407 

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

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

410 # prospective version or not. 

411 shortened_prospective = padded_prospective[: len(split_spec)] 

412 

413 return shortened_prospective == split_spec 

414 else: 

415 # Convert our spec string into a Version 

416 spec_version = Version(spec) 

417 

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

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

420 # segment. 

421 if not spec_version.local: 

422 prospective = Version(prospective.public) 

423 

424 return prospective == spec_version 

425 

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

427 return not self._compare_equal(prospective, spec) 

428 

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

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

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

432 # the prospective version. 

433 return Version(prospective.public) <= Version(spec) 

434 

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

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

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

438 # the prospective version. 

439 return Version(prospective.public) >= Version(spec) 

440 

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

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

443 # it as a version. 

444 spec = Version(spec_str) 

445 

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

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

448 # instead of doing extra unneeded work. 

449 if not prospective < spec: 

450 return False 

451 

452 # This special case is here so that, unless the specifier itself 

453 # includes is a pre-release version, that we do not accept pre-release 

454 # versions for the version mentioned in the specifier (e.g. <3.1 should 

455 # not match 3.1.dev0, but should match 3.0.dev0). 

456 if not spec.is_prerelease and prospective.is_prerelease: 

457 if Version(prospective.base_version) == Version(spec.base_version): 

458 return False 

459 

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

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

462 # version in the spec. 

463 return True 

464 

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

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

467 # it as a version. 

468 spec = Version(spec_str) 

469 

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

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

472 # instead of doing extra unneeded work. 

473 if not prospective > spec: 

474 return False 

475 

476 # This special case is here so that, unless the specifier itself 

477 # includes is a post-release version, that we do not accept 

478 # post-release versions for the version mentioned in the specifier 

479 # (e.g. >3.1 should not match 3.0.post0, but should match 3.2.post0). 

480 if not spec.is_postrelease and prospective.is_postrelease: 

481 if Version(prospective.base_version) == Version(spec.base_version): 

482 return False 

483 

484 # Ensure that we do not allow a local version of the version mentioned 

485 # in the specifier, which is technically greater than, to match. 

486 if prospective.local is not None: 

487 if Version(prospective.base_version) == Version(spec.base_version): 

488 return False 

489 

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

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

492 # same version in the spec. 

493 return True 

494 

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

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

497 

498 def __contains__(self, item: Union[str, Version]) -> bool: 

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

500 

501 :param item: The item to check for. 

502 

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

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

505 

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

507 True 

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

509 True 

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

511 False 

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

513 False 

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

515 True 

516 """ 

517 return self.contains(item) 

518 

519 def contains( 

520 self, item: UnparsedVersion, prereleases: Optional[bool] = None 

521 ) -> bool: 

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

523 

524 :param item: 

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

526 :class:`Version` instance. 

527 :param prereleases: 

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

529 ``None`` (the default), it uses :attr:`prereleases` to determine 

530 whether or not prereleases are allowed. 

531 

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

533 True 

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

535 True 

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

537 False 

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

539 False 

540 >>> Specifier(">=1.2.3", prereleases=True).contains("1.3.0a1") 

541 True 

542 >>> Specifier(">=1.2.3").contains("1.3.0a1", prereleases=True) 

543 True 

544 """ 

545 

546 # Determine if prereleases are to be allowed or not. 

547 if prereleases is None: 

548 prereleases = self.prereleases 

549 

550 # Normalize item to a Version, this allows us to have a shortcut for 

551 # "2.0" in Specifier(">=2") 

552 normalized_item = _coerce_version(item) 

553 

554 # Determine if we should be supporting prereleases in this specifier 

555 # or not, if we do not support prereleases than we can short circuit 

556 # logic if this version is a prereleases. 

557 if normalized_item.is_prerelease and not prereleases: 

558 return False 

559 

560 # Actually do the comparison to determine if this item is contained 

561 # within this Specifier or not. 

562 operator_callable: CallableOperator = self._get_operator(self.operator) 

563 return operator_callable(normalized_item, self.version) 

564 

565 def filter( 

566 self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None 

567 ) -> Iterator[UnparsedVersionVar]: 

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

569 

570 :param iterable: 

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

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

573 :param prereleases: 

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

575 ``None`` (the default), it will be intelligently decide whether to allow 

576 prereleases or not (based on the :attr:`prereleases` attribute, and 

577 whether the only versions matching are prereleases). 

578 

579 This method is smarter than just ``filter(Specifier().contains, [...])`` 

580 because it implements the rule from :pep:`440` that a prerelease item 

581 SHOULD be accepted if no other versions match the given specifier. 

582 

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

584 ['1.3'] 

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

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

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

588 ['1.5a1'] 

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

590 ['1.3', '1.5a1'] 

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

592 ['1.3', '1.5a1'] 

593 """ 

594 

595 yielded = False 

596 found_prereleases = [] 

597 

598 kw = {"prereleases": prereleases if prereleases is not None else True} 

599 

600 # Attempt to iterate over all the values in the iterable and if any of 

601 # them match, yield them. 

602 for version in iterable: 

603 parsed_version = _coerce_version(version) 

604 

605 if self.contains(parsed_version, **kw): 

606 # If our version is a prerelease, and we were not set to allow 

607 # prereleases, then we'll store it for later in case nothing 

608 # else matches this specifier. 

609 if parsed_version.is_prerelease and not ( 

610 prereleases or self.prereleases 

611 ): 

612 found_prereleases.append(version) 

613 # Either this is not a prerelease, or we should have been 

614 # accepting prereleases from the beginning. 

615 else: 

616 yielded = True 

617 yield version 

618 

619 # Now that we've iterated over everything, determine if we've yielded 

620 # any values, and if we have not and we have any prereleases stored up 

621 # then we will go ahead and yield the prereleases. 

622 if not yielded and found_prereleases: 

623 for version in found_prereleases: 

624 yield version 

625 

626 

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

628 

629 

630def _version_split(version: str) -> List[str]: 

631 """Split version into components. 

632 

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

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

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

636 version string. 

637 """ 

638 result: List[str] = [] 

639 

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

641 result.append(epoch or "0") 

642 

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

644 match = _prefix_regex.search(item) 

645 if match: 

646 result.extend(match.groups()) 

647 else: 

648 result.append(item) 

649 return result 

650 

651 

652def _version_join(components: List[str]) -> str: 

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

654 

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

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

657 components numeric. 

658 """ 

659 epoch, *rest = components 

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

661 

662 

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

664 return not any( 

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

666 ) 

667 

668 

669def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]: 

670 left_split, right_split = [], [] 

671 

672 # Get the release segment of our versions 

673 left_split.append(list(itertools.takewhile(lambda x: x.isdigit(), left))) 

674 right_split.append(list(itertools.takewhile(lambda x: x.isdigit(), right))) 

675 

676 # Get the rest of our versions 

677 left_split.append(left[len(left_split[0]) :]) 

678 right_split.append(right[len(right_split[0]) :]) 

679 

680 # Insert our padding 

681 left_split.insert(1, ["0"] * max(0, len(right_split[0]) - len(left_split[0]))) 

682 right_split.insert(1, ["0"] * max(0, len(left_split[0]) - len(right_split[0]))) 

683 

684 return ( 

685 list(itertools.chain.from_iterable(left_split)), 

686 list(itertools.chain.from_iterable(right_split)), 

687 ) 

688 

689 

690class SpecifierSet(BaseSpecifier): 

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

692 

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

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

695 """ 

696 

697 def __init__( 

698 self, specifiers: str = "", prereleases: Optional[bool] = None 

699 ) -> None: 

700 """Initialize a SpecifierSet instance. 

701 

702 :param specifiers: 

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

704 specifiers which will be parsed and normalized before use. 

705 :param prereleases: 

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

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

708 given specifiers. 

709 

710 :raises InvalidSpecifier: 

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

712 raised. 

713 """ 

714 

715 # Split on `,` to break each individual specifier into it's own item, and 

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

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

718 

719 # Make each individual specifier a Specifier and save in a frozen set for later. 

720 self._specs = frozenset(map(Specifier, split_specifiers)) 

721 

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

723 # we accept prereleases or not. 

724 self._prereleases = prereleases 

725 

726 @property 

727 def prereleases(self) -> Optional[bool]: 

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

729 # pass that through here. 

730 if self._prereleases is not None: 

731 return self._prereleases 

732 

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

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

735 # pre-releases or not. 

736 if not self._specs: 

737 return None 

738 

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

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

741 return any(s.prereleases for s in self._specs) 

742 

743 @prereleases.setter 

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

745 self._prereleases = value 

746 

747 def __repr__(self) -> str: 

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

749 

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

751 match the input string. 

752 

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

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

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

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

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

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

759 """ 

760 pre = ( 

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

762 if self._prereleases is not None 

763 else "" 

764 ) 

765 

766 return f"<SpecifierSet({str(self)!r}{pre})>" 

767 

768 def __str__(self) -> str: 

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

770 

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

772 match the input string. 

773 

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

775 '!=1.0.1,>=1.0.0' 

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

777 '!=1.0.1,>=1.0.0' 

778 """ 

779 return ",".join(sorted(str(s) for s in self._specs)) 

780 

781 def __hash__(self) -> int: 

782 return hash(self._specs) 

783 

784 def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet": 

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

786 

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

788 

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

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

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

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

793 """ 

794 if isinstance(other, str): 

795 other = SpecifierSet(other) 

796 elif not isinstance(other, SpecifierSet): 

797 return NotImplemented 

798 

799 specifier = SpecifierSet() 

800 specifier._specs = frozenset(self._specs | other._specs) 

801 

802 if self._prereleases is None and other._prereleases is not None: 

803 specifier._prereleases = other._prereleases 

804 elif self._prereleases is not None and other._prereleases is None: 

805 specifier._prereleases = self._prereleases 

806 elif self._prereleases == other._prereleases: 

807 specifier._prereleases = self._prereleases 

808 else: 

809 raise ValueError( 

810 "Cannot combine SpecifierSets with True and False prerelease " 

811 "overrides." 

812 ) 

813 

814 return specifier 

815 

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

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

818 

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

820 

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

822 

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

824 True 

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

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

827 True 

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

829 True 

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

831 False 

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

833 False 

834 """ 

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

836 other = SpecifierSet(str(other)) 

837 elif not isinstance(other, SpecifierSet): 

838 return NotImplemented 

839 

840 return self._specs == other._specs 

841 

842 def __len__(self) -> int: 

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

844 return len(self._specs) 

845 

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

847 """ 

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

849 in this specifier set. 

850 

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

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

853 """ 

854 return iter(self._specs) 

855 

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

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

858 

859 :param item: The item to check for. 

860 

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

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

863 

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

865 True 

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

867 True 

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

869 False 

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

871 False 

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

873 True 

874 """ 

875 return self.contains(item) 

876 

877 def contains( 

878 self, 

879 item: UnparsedVersion, 

880 prereleases: Optional[bool] = None, 

881 installed: Optional[bool] = None, 

882 ) -> bool: 

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

884 

885 :param item: 

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

887 :class:`Version` instance. 

888 :param prereleases: 

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

890 ``None`` (the default), it uses :attr:`prereleases` to determine 

891 whether or not prereleases are allowed. 

892 

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

894 True 

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

896 True 

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

898 False 

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

900 False 

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

902 True 

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

904 True 

905 """ 

906 # Ensure that our item is a Version instance. 

907 if not isinstance(item, Version): 

908 item = Version(item) 

909 

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

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

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

913 if prereleases is None: 

914 prereleases = self.prereleases 

915 

916 # We can determine if we're going to allow pre-releases by looking to 

917 # see if any of the underlying items supports them. If none of them do 

918 # and this item is a pre-release then we do not allow it and we can 

919 # short circuit that here. 

920 # Note: This means that 1.0.dev1 would not be contained in something 

921 # like >=1.0.devabc however it would be in >=1.0.debabc,>0.0.dev0 

922 if not prereleases and item.is_prerelease: 

923 return False 

924 

925 if installed and item.is_prerelease: 

926 item = Version(item.base_version) 

927 

928 # We simply dispatch to the underlying specs here to make sure that the 

929 # given version is contained within all of them. 

930 # Note: This use of all() here means that an empty set of specifiers 

931 # will always return True, this is an explicit design decision. 

932 return all(s.contains(item, prereleases=prereleases) for s in self._specs) 

933 

934 def filter( 

935 self, iterable: Iterable[UnparsedVersionVar], prereleases: Optional[bool] = None 

936 ) -> Iterator[UnparsedVersionVar]: 

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

938 

939 :param iterable: 

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

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

942 :param prereleases: 

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

944 ``None`` (the default), it will be intelligently decide whether to allow 

945 prereleases or not (based on the :attr:`prereleases` attribute, and 

946 whether the only versions matching are prereleases). 

947 

948 This method is smarter than just ``filter(SpecifierSet(...).contains, [...])`` 

949 because it implements the rule from :pep:`440` that a prerelease item 

950 SHOULD be accepted if no other versions match the given specifier. 

951 

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

953 ['1.3'] 

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

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

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

957 [] 

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

959 ['1.3', '1.5a1'] 

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

961 ['1.3', '1.5a1'] 

962 

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

964 versions in the set. 

965 

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

967 ['1.3'] 

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

969 ['1.5a1'] 

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

971 ['1.3', '1.5a1'] 

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

973 ['1.3', '1.5a1'] 

974 """ 

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

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

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

978 if prereleases is None: 

979 prereleases = self.prereleases 

980 

981 # If we have any specifiers, then we want to wrap our iterable in the 

982 # filter method for each one, this will act as a logical AND amongst 

983 # each specifier. 

984 if self._specs: 

985 for spec in self._specs: 

986 iterable = spec.filter(iterable, prereleases=bool(prereleases)) 

987 return iter(iterable) 

988 # If we do not have any specifiers, then we need to have a rough filter 

989 # which will filter out any pre-releases, unless there are no final 

990 # releases. 

991 else: 

992 filtered: List[UnparsedVersionVar] = [] 

993 found_prereleases: List[UnparsedVersionVar] = [] 

994 

995 for item in iterable: 

996 parsed_version = _coerce_version(item) 

997 

998 # Store any item which is a pre-release for later unless we've 

999 # already found a final version or we are accepting prereleases 

1000 if parsed_version.is_prerelease and not prereleases: 

1001 if not filtered: 

1002 found_prereleases.append(item) 

1003 else: 

1004 filtered.append(item) 

1005 

1006 # If we've found no items except for pre-releases, then we'll go 

1007 # ahead and use the pre-releases 

1008 if not filtered and found_prereleases and prereleases is None: 

1009 return iter(found_prereleases) 

1010 

1011 return iter(filtered)