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

253 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:25 +0000

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 ( 

15 Callable, 

16 Iterable, 

17 Iterator, 

18 List, 

19 Optional, 

20 Set, 

21 Tuple, 

22 TypeVar, 

23 Union, 

24) 

25 

26from .utils import canonicalize_version 

27from .version import Version 

28 

29UnparsedVersion = Union[Version, str] 

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

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

32 

33 

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

35 if not isinstance(version, Version): 

36 version = Version(version) 

37 return version 

38 

39 

40class InvalidSpecifier(ValueError): 

41 """ 

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

43 string that is invalid. 

44 

45 >>> Specifier("lolwat") 

46 Traceback (most recent call last): 

47 ... 

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

49 """ 

50 

51 

52class BaseSpecifier(metaclass=abc.ABCMeta): 

53 @abc.abstractmethod 

54 def __str__(self) -> str: 

55 """ 

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

57 should be representative of the Specifier itself. 

58 """ 

59 

60 @abc.abstractmethod 

61 def __hash__(self) -> int: 

62 """ 

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

64 """ 

65 

66 @abc.abstractmethod 

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

68 """ 

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

70 objects are equal. 

71 

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

73 """ 

74 

75 @property 

76 @abc.abstractmethod 

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

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

79 

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

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

82 """ 

83 

84 @prereleases.setter 

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

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

87 

88 :param value: The value to set. 

89 """ 

90 

91 @abc.abstractmethod 

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

93 """ 

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

95 """ 

96 

97 @abc.abstractmethod 

98 def filter( 

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

100 ) -> Iterator[UnparsedVersionVar]: 

101 """ 

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

103 are contained within this specifier are allowed in it. 

104 """ 

105 

106 

107class Specifier(BaseSpecifier): 

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

109 

110 .. tip:: 

111 

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

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

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

115 """ 

116 

117 _operator_regex_str = r""" 

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

119 """ 

120 _version_regex_str = r""" 

121 (?P<version> 

122 (?: 

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

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

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

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

127 # but included entirely as an escape hatch. 

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

129 \s* 

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

131 # we match everything except for whitespace, a 

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

133 # since versions can be enclosed in them. 

134 ) 

135 | 

136 (?: 

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

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

139 # operators separately to enable that. 

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

141 

142 \s* 

143 v? 

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

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

146 

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

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

149 (?: 

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

151 | 

152 (?: # pre release 

153 [-_\.]? 

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

155 [-_\.]? 

156 [0-9]* 

157 )? 

158 (?: # post release 

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

160 )? 

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

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

163 )? 

164 ) 

165 | 

166 (?: 

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

168 # release segment. 

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

170 

171 \s* 

172 v? 

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

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

175 (?: # pre release 

176 [-_\.]? 

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

178 [-_\.]? 

179 [0-9]* 

180 )? 

181 (?: # post release 

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

183 )? 

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

185 ) 

186 | 

187 (?: 

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

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

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

191 # matching wild cards. 

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

193 # operators so we want to make sure they 

194 # don't match here. 

195 

196 \s* 

197 v? 

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

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

200 (?: # pre release 

201 [-_\.]? 

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

203 [-_\.]? 

204 [0-9]* 

205 )? 

206 (?: # post release 

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

208 )? 

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

210 ) 

211 ) 

212 """ 

213 

214 _regex = re.compile( 

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

216 re.VERBOSE | re.IGNORECASE, 

217 ) 

218 

219 _operators = { 

220 "~=": "compatible", 

221 "==": "equal", 

222 "!=": "not_equal", 

223 "<=": "less_than_equal", 

224 ">=": "greater_than_equal", 

225 "<": "less_than", 

226 ">": "greater_than", 

227 "===": "arbitrary", 

228 } 

229 

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

231 """Initialize a Specifier instance. 

232 

233 :param spec: 

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

235 normalized before use. 

236 :param prereleases: 

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

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

239 given specifiers. 

240 :raises InvalidSpecifier: 

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

242 """ 

243 match = self._regex.search(spec) 

244 if not match: 

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

246 

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

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

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

250 ) 

251 

252 # Store whether or not this Specifier should accept prereleases 

253 self._prereleases = prereleases 

254 

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

256 @property # type: ignore[override] 

257 def prereleases(self) -> bool: 

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

259 # blindly use that. 

260 if self._prereleases is not None: 

261 return self._prereleases 

262 

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

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

265 # prerelease. 

266 operator, version = self._spec 

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

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

269 # want to remove before parsing. 

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

271 version = version[:-2] 

272 

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

274 # specifier allows pre-releases. 

275 if Version(version).is_prerelease: 

276 return True 

277 

278 return False 

279 

280 @prereleases.setter 

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

282 self._prereleases = value 

283 

284 @property 

285 def operator(self) -> str: 

286 """The operator of this specifier. 

287 

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

289 '==' 

290 """ 

291 return self._spec[0] 

292 

293 @property 

294 def version(self) -> str: 

295 """The version of this specifier. 

296 

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

298 '1.2.3' 

299 """ 

300 return self._spec[1] 

301 

302 def __repr__(self) -> str: 

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

304 

305 >>> Specifier('>=1.0.0') 

306 <Specifier('>=1.0.0')> 

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

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

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

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

311 """ 

312 pre = ( 

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

314 if self._prereleases is not None 

315 else "" 

316 ) 

317 

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

319 

320 def __str__(self) -> str: 

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

322 

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

324 '>=1.0.0' 

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

326 '>=1.0.0' 

327 """ 

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

329 

330 @property 

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

332 canonical_version = canonicalize_version( 

333 self._spec[1], 

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

335 ) 

336 return self._spec[0], canonical_version 

337 

338 def __hash__(self) -> int: 

339 return hash(self._canonical_spec) 

340 

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

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

343 

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

345 

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

347 

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

349 True 

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

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

352 True 

353 >>> Specifier("==1.2.3") == "==1.2.3" 

354 True 

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

356 False 

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

358 False 

359 """ 

360 if isinstance(other, str): 

361 try: 

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

363 except InvalidSpecifier: 

364 return NotImplemented 

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

366 return NotImplemented 

367 

368 return self._canonical_spec == other._canonical_spec 

369 

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

371 operator_callable: CallableOperator = getattr( 

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

373 ) 

374 return operator_callable 

375 

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

377 

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

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

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

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

382 # the other specifiers. 

383 

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

385 # ignore suffix segments. 

386 prefix = ".".join( 

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

388 ) 

389 

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

391 prefix += ".*" 

392 

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

394 prospective, prefix 

395 ) 

396 

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

398 

399 # We need special logic to handle prefix matching 

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

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

402 normalized_prospective = canonicalize_version( 

403 prospective.public, strip_trailing_zero=False 

404 ) 

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

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

407 # Split the spec out by dots, and pretend that there is an implicit 

408 # dot in between a release segment and a pre-release segment. 

409 split_spec = _version_split(normalized_spec) 

410 

411 # Split the prospective version out by dots, and pretend that there 

412 # is an implicit dot in between a release segment and a pre-release 

413 # segment. 

414 split_prospective = _version_split(normalized_prospective) 

415 

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

417 # shortened version. 

418 padded_prospective, _ = _pad_version(split_prospective, split_spec) 

419 

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

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

422 # prospective version or not. 

423 shortened_prospective = padded_prospective[: len(split_spec)] 

424 

425 return shortened_prospective == split_spec 

426 else: 

427 # Convert our spec string into a Version 

428 spec_version = Version(spec) 

429 

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

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

432 # segment. 

433 if not spec_version.local: 

434 prospective = Version(prospective.public) 

435 

436 return prospective == spec_version 

437 

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

439 return not self._compare_equal(prospective, spec) 

440 

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

442 

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

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

445 # the prospective version. 

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

447 

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

449 

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

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

452 # the prospective version. 

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

454 

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

456 

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

458 # it as a version. 

459 spec = Version(spec_str) 

460 

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

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

463 # instead of doing extra unneeded work. 

464 if not prospective < spec: 

465 return False 

466 

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

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

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

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

471 if not spec.is_prerelease and prospective.is_prerelease: 

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

473 return False 

474 

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

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

477 # version in the spec. 

478 return True 

479 

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

481 

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

483 # it as a version. 

484 spec = Version(spec_str) 

485 

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

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

488 # instead of doing extra unneeded work. 

489 if not prospective > spec: 

490 return False 

491 

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

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

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

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

496 if not spec.is_postrelease and prospective.is_postrelease: 

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

498 return False 

499 

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

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

502 if prospective.local is not None: 

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

504 return False 

505 

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

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

508 # same version in the spec. 

509 return True 

510 

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

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

513 

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

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

516 

517 :param item: The item to check for. 

518 

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

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

521 

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

523 True 

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

525 True 

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

527 False 

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

529 False 

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

531 True 

532 """ 

533 return self.contains(item) 

534 

535 def contains( 

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

537 ) -> bool: 

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

539 

540 :param item: 

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

542 :class:`Version` instance. 

543 :param prereleases: 

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

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

546 whether or not prereleases are allowed. 

547 

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

549 True 

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

551 True 

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

553 False 

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

555 False 

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

557 True 

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

559 True 

560 """ 

561 

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

563 if prereleases is None: 

564 prereleases = self.prereleases 

565 

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

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

568 normalized_item = _coerce_version(item) 

569 

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

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

572 # logic if this version is a prereleases. 

573 if normalized_item.is_prerelease and not prereleases: 

574 return False 

575 

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

577 # within this Specifier or not. 

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

579 return operator_callable(normalized_item, self.version) 

580 

581 def filter( 

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

583 ) -> Iterator[UnparsedVersionVar]: 

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

585 

586 :param iterable: 

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

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

589 :param prereleases: 

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

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

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

593 whether the only versions matching are prereleases). 

594 

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

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

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

598 

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

600 ['1.3'] 

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

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

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

604 ['1.5a1'] 

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

606 ['1.3', '1.5a1'] 

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

608 ['1.3', '1.5a1'] 

609 """ 

610 

611 yielded = False 

612 found_prereleases = [] 

613 

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

615 

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

617 # them match, yield them. 

618 for version in iterable: 

619 parsed_version = _coerce_version(version) 

620 

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

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

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

624 # else matches this specifier. 

625 if parsed_version.is_prerelease and not ( 

626 prereleases or self.prereleases 

627 ): 

628 found_prereleases.append(version) 

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

630 # accepting prereleases from the beginning. 

631 else: 

632 yielded = True 

633 yield version 

634 

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

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

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

638 if not yielded and found_prereleases: 

639 for version in found_prereleases: 

640 yield version 

641 

642 

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

644 

645 

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

647 result: List[str] = [] 

648 for item in version.split("."): 

649 match = _prefix_regex.search(item) 

650 if match: 

651 result.extend(match.groups()) 

652 else: 

653 result.append(item) 

654 return result 

655 

656 

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

658 return not any( 

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

660 ) 

661 

662 

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

664 left_split, right_split = [], [] 

665 

666 # Get the release segment of our versions 

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

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

669 

670 # Get the rest of our versions 

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

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

673 

674 # Insert our padding 

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

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

677 

678 return (list(itertools.chain(*left_split)), list(itertools.chain(*right_split))) 

679 

680 

681class SpecifierSet(BaseSpecifier): 

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

683 

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

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

686 """ 

687 

688 def __init__( 

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

690 ) -> None: 

691 """Initialize a SpecifierSet instance. 

692 

693 :param specifiers: 

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

695 specifiers which will be parsed and normalized before use. 

696 :param prereleases: 

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

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

699 given specifiers. 

700 

701 :raises InvalidSpecifier: 

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

703 raised. 

704 """ 

705 

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

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

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

709 

710 # Parsed each individual specifier, attempting first to make it a 

711 # Specifier. 

712 parsed: Set[Specifier] = set() 

713 for specifier in split_specifiers: 

714 parsed.add(Specifier(specifier)) 

715 

716 # Turn our parsed specifiers into a frozen set and save them for later. 

717 self._specs = frozenset(parsed) 

718 

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

720 # we accept prereleases or not. 

721 self._prereleases = prereleases 

722 

723 @property 

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

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

726 # pass that through here. 

727 if self._prereleases is not None: 

728 return self._prereleases 

729 

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

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

732 # pre-releases or not. 

733 if not self._specs: 

734 return None 

735 

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

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

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

739 

740 @prereleases.setter 

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

742 self._prereleases = value 

743 

744 def __repr__(self) -> str: 

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

746 

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

748 match the input string. 

749 

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

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

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

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

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

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

756 """ 

757 pre = ( 

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

759 if self._prereleases is not None 

760 else "" 

761 ) 

762 

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

764 

765 def __str__(self) -> str: 

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

767 

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

769 match the input string. 

770 

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

772 '!=1.0.1,>=1.0.0' 

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

774 '!=1.0.1,>=1.0.0' 

775 """ 

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

777 

778 def __hash__(self) -> int: 

779 return hash(self._specs) 

780 

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

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

783 

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

785 

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

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

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

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

790 """ 

791 if isinstance(other, str): 

792 other = SpecifierSet(other) 

793 elif not isinstance(other, SpecifierSet): 

794 return NotImplemented 

795 

796 specifier = SpecifierSet() 

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

798 

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

800 specifier._prereleases = other._prereleases 

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

802 specifier._prereleases = self._prereleases 

803 elif self._prereleases == other._prereleases: 

804 specifier._prereleases = self._prereleases 

805 else: 

806 raise ValueError( 

807 "Cannot combine SpecifierSets with True and False prerelease " 

808 "overrides." 

809 ) 

810 

811 return specifier 

812 

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

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

815 

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

817 

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

819 

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

821 True 

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

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

824 True 

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

826 True 

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

828 False 

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

830 False 

831 """ 

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

833 other = SpecifierSet(str(other)) 

834 elif not isinstance(other, SpecifierSet): 

835 return NotImplemented 

836 

837 return self._specs == other._specs 

838 

839 def __len__(self) -> int: 

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

841 return len(self._specs) 

842 

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

844 """ 

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

846 in this specifier set. 

847 

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

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

850 """ 

851 return iter(self._specs) 

852 

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

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

855 

856 :param item: The item to check for. 

857 

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

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

860 

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

862 True 

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

864 True 

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

866 False 

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

868 False 

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

870 True 

871 """ 

872 return self.contains(item) 

873 

874 def contains( 

875 self, 

876 item: UnparsedVersion, 

877 prereleases: Optional[bool] = None, 

878 installed: Optional[bool] = None, 

879 ) -> bool: 

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

881 

882 :param item: 

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

884 :class:`Version` instance. 

885 :param prereleases: 

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

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

888 whether or not prereleases are allowed. 

889 

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

891 True 

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

893 True 

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

895 False 

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

897 False 

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

899 True 

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

901 True 

902 """ 

903 # Ensure that our item is a Version instance. 

904 if not isinstance(item, Version): 

905 item = Version(item) 

906 

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

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

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

910 if prereleases is None: 

911 prereleases = self.prereleases 

912 

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

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

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

916 # short circuit that here. 

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

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

919 if not prereleases and item.is_prerelease: 

920 return False 

921 

922 if installed and item.is_prerelease: 

923 item = Version(item.base_version) 

924 

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

926 # given version is contained within all of them. 

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

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

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

930 

931 def filter( 

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

933 ) -> Iterator[UnparsedVersionVar]: 

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

935 

936 :param iterable: 

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

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

939 :param prereleases: 

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

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

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

943 whether the only versions matching are prereleases). 

944 

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

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

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

948 

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

950 ['1.3'] 

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

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

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

954 [] 

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

956 ['1.3', '1.5a1'] 

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

958 ['1.3', '1.5a1'] 

959 

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

961 versions in the set. 

962 

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

964 ['1.3'] 

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

966 ['1.5a1'] 

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

968 ['1.3', '1.5a1'] 

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

970 ['1.3', '1.5a1'] 

971 """ 

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

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

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

975 if prereleases is None: 

976 prereleases = self.prereleases 

977 

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

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

980 # each specifier. 

981 if self._specs: 

982 for spec in self._specs: 

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

984 return iter(iterable) 

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

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

987 # releases. 

988 else: 

989 filtered: List[UnparsedVersionVar] = [] 

990 found_prereleases: List[UnparsedVersionVar] = [] 

991 

992 for item in iterable: 

993 parsed_version = _coerce_version(item) 

994 

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

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

997 if parsed_version.is_prerelease and not prereleases: 

998 if not filtered: 

999 found_prereleases.append(item) 

1000 else: 

1001 filtered.append(item) 

1002 

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

1004 # ahead and use the pre-releases 

1005 if not filtered and found_prereleases and prereleases is None: 

1006 return iter(found_prereleases) 

1007 

1008 return iter(filtered)