Coverage for pyTooling/Versioning/__init__.py: 82%

975 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-31 22:23 +0000

1# ==================================================================================================================== # 

2# _____ _ _ __ __ _ _ # 

3# _ __ _ |_ _|__ ___ | (_)_ __ __ \ \ / /__ _ __ ___(_) ___ _ __ (_)_ __ __ _ # 

4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` \ \ / / _ \ '__/ __| |/ _ \| '_ \| | '_ \ / _` | # 

5# | |_) | |_| || | (_) | (_) | | | | | | (_| |\ V / __/ | \__ \ | (_) | | | | | | | | (_| | # 

6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)_/ \___|_| |___/_|\___/|_| |_|_|_| |_|\__, | # 

7# |_| |___/ |___/ |___/ # 

8# ==================================================================================================================== # 

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

13# ==================================================================================================================== # 

14# Copyright 2020-2025 Patrick Lehmann - Bötzingen, Germany # 

15# # 

16# Licensed under the Apache License, Version 2.0 (the "License"); # 

17# you may not use this file except in compliance with the License. # 

18# You may obtain a copy of the License at # 

19# # 

20# http://www.apache.org/licenses/LICENSE-2.0 # 

21# # 

22# Unless required by applicable law or agreed to in writing, software # 

23# distributed under the License is distributed on an "AS IS" BASIS, # 

24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # 

25# See the License for the specific language governing permissions and # 

26# limitations under the License. # 

27# # 

28# SPDX-License-Identifier: Apache-2.0 # 

29# ==================================================================================================================== # 

30# 

31""" 

32Implementation of semantic and date versioning version-numbers. 

33 

34.. hint:: See :ref:`high-level help <VERSIONING>` for explanations and usage examples. 

35""" 

36from collections.abc import Iterable as abc_Iterable 

37from enum import Flag, Enum 

38from re import compile as re_compile 

39from sys import version_info # needed for versions before Python 3.11 

40from typing import Optional as Nullable, Union, Callable, Any, Generic, TypeVar, Tuple, Iterable, Iterator, List 

41 

42try: 

43 from pyTooling.Decorators import export, readonly 

44 from pyTooling.MetaClasses import ExtendedType, abstractmethod, mustoverride 

45 from pyTooling.Exceptions import ToolingException 

46 from pyTooling.Common import getFullyQualifiedName 

47except (ImportError, ModuleNotFoundError): # pragma: no cover 

48 print("[pyTooling.Versioning] Could not import from 'pyTooling.*'!") 

49 

50 try: 

51 from Decorators import export, readonly 

52 from MetaClasses import ExtendedType, abstractmethod, mustoverride 

53 from Exceptions import ToolingException 

54 from Common import getFullyQualifiedName 

55 except (ImportError, ModuleNotFoundError) as ex: # pragma: no cover 

56 print("[pyTooling.Versioning] Could not import directly!") 

57 raise ex 

58 

59 

60@export 

61class Parts(Flag): 

62 """Enumeration describing parts of a version number that can be present.""" 

63 Unknown = 0 #: Undocumented 

64 Major = 1 #: Major number is present. (e.g. X in ``vX.0.0``). 

65 Year = 1 #: Year is present. (e.g. X in ``XXXX.10``). 

66 Minor = 2 #: Minor number is present. (e.g. Y in ``v0.Y.0``). 

67 Month = 2 #: Month is present. (e.g. X in ``2024.YY``). 

68 Week = 2 #: Week is present. (e.g. X in ``2024.YY``). 

69 Micro = 4 #: Patch number is present. (e.g. Z in ``v0.0.Z``). 

70 Patch = 4 #: Patch number is present. (e.g. Z in ``v0.0.Z``). 

71 Day = 4 #: Day is present. (e.g. X in ``2024.10.ZZ``). 

72 Level = 8 #: Release level is present. 

73 Dev = 16 #: Development part is present. 

74 Build = 32 #: Build number is present. (e.g. bbbb in ``v0.0.0.bbbb``) 

75 Post = 64 #: Post-release number is present. 

76 Prefix = 128 #: Prefix is present. 

77 Postfix = 256 #: Postfix is present. 

78 Hash = 512 #: Hash is present. 

79# AHead = 256 

80 

81 

82@export 

83class ReleaseLevel(Enum): 

84 """Enumeration describing the version's maturity level.""" 

85 Final = 0 #: 

86 ReleaseCandidate = -10 #: 

87 Development = -20 #: 

88 Gamma = -30 #: 

89 Beta = -40 #: 

90 Alpha = -50 #: 

91 

92 def __eq__(self, other: Any): 

93 """ 

94 Compare two release levels if the level is equal to the second operand. 

95 

96 :param other: Operand to compare against. 

97 :returns: ``True``, if release level is equal the second operand's release level. 

98 :raises TypeError: If parameter ``other`` is not of type :class:`ReleaseLevel` or :class:`str`. 

99 """ 

100 if isinstance(other, str): 100 ↛ 101line 100 didn't jump to line 101 because the condition on line 100 was never true

101 other = ReleaseLevel(other) 

102 

103 if not isinstance(other, ReleaseLevel): 103 ↛ 104line 103 didn't jump to line 104 because the condition on line 103 was never true

104 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.") 

105 ex.add_note(f"Supported types for second operand: {self.__class__.__name__} or 'str'.") 

106 raise ex 

107 

108 return self is other 

109 

110 def __ne__(self, other: Any): 

111 """ 

112 Compare two release levels if the level is unequal to the second operand. 

113 

114 :param other: Operand to compare against. 

115 :returns: ``True``, if release level is unequal the second operand's release level. 

116 :raises TypeError: If parameter ``other`` is not of type :class:`ReleaseLevel` or :class:`str`. 

117 """ 

118 if isinstance(other, str): 

119 other = ReleaseLevel(other) 

120 

121 if not isinstance(other, ReleaseLevel): 

122 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by != operator.") 

123 ex.add_note(f"Supported types for second operand: {self.__class__.__name__} or 'str'.") 

124 raise ex 

125 

126 return self is not other 

127 

128 def __lt__(self, other: Any): 

129 """ 

130 Compare two release levels if the level is less than the second operand. 

131 

132 :param other: Operand to compare against. 

133 :returns: ``True``, if release level is less than the second operand. 

134 :raises TypeError: If parameter ``other`` is not of type :class:`ReleaseLevel` or :class:`str`. 

135 """ 

136 if isinstance(other, str): 136 ↛ 137line 136 didn't jump to line 137 because the condition on line 136 was never true

137 other = ReleaseLevel(other) 

138 

139 if not isinstance(other, ReleaseLevel): 139 ↛ 140line 139 didn't jump to line 140 because the condition on line 139 was never true

140 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.") 

141 ex.add_note(f"Supported types for second operand: {self.__class__.__name__} or 'str'.") 

142 raise ex 

143 

144 return self.value < other.value 

145 

146 def __le__(self, other: Any): 

147 """ 

148 Compare two release levels if the level is less than or equal the second operand. 

149 

150 :param other: Operand to compare against. 

151 :returns: ``True``, if release level is less than or equal the second operand. 

152 :raises TypeError: If parameter ``other`` is not of type :class:`ReleaseLevel` or :class:`str`. 

153 """ 

154 if isinstance(other, str): 

155 other = ReleaseLevel(other) 

156 

157 if not isinstance(other, ReleaseLevel): 

158 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <=>= operator.") 

159 ex.add_note(f"Supported types for second operand: {self.__class__.__name__} or 'str'.") 

160 raise ex 

161 

162 return self.value <= other.value 

163 

164 def __gt__(self, other: Any): 

165 """ 

166 Compare two release levels if the level is greater than the second operand. 

167 

168 :param other: Operand to compare against. 

169 :returns: ``True``, if release level is greater than the second operand. 

170 :raises TypeError: If parameter ``other`` is not of type :class:`ReleaseLevel` or :class:`str`. 

171 """ 

172 if isinstance(other, str): 172 ↛ 173line 172 didn't jump to line 173 because the condition on line 172 was never true

173 other = ReleaseLevel(other) 

174 

175 if not isinstance(other, ReleaseLevel): 175 ↛ 176line 175 didn't jump to line 176 because the condition on line 175 was never true

176 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.") 

177 ex.add_note(f"Supported types for second operand: {self.__class__.__name__} or 'str'.") 

178 raise ex 

179 

180 return self.value > other.value 

181 

182 def __ge__(self, other: Any): 

183 """ 

184 Compare two release levels if the level is greater than or equal the second operand. 

185 

186 :param other: Operand to compare against. 

187 :returns: ``True``, if release level is greater than or equal the second operand. 

188 :raises TypeError: If parameter ``other`` is not of type :class:`ReleaseLevel` or :class:`str`. 

189 """ 

190 if isinstance(other, str): 

191 other = ReleaseLevel(other) 

192 

193 if not isinstance(other, ReleaseLevel): 

194 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.") 

195 ex.add_note(f"Supported types for second operand: {self.__class__.__name__} or 'str'.") 

196 raise ex 

197 

198 return self.value >= other.value 

199 

200 def __hash__(self) -> int: 

201 return hash(self.value) 

202 

203 def __str__(self) -> str: 

204 """ 

205 Returns the release level's string equivalent. 

206 

207 :returns: The string equivalent of the release level. 

208 """ 

209 if self is ReleaseLevel.Final: 

210 return "final" 

211 elif self is ReleaseLevel.ReleaseCandidate: 

212 return "rc" 

213 elif self is ReleaseLevel.Development: 213 ↛ 214line 213 didn't jump to line 214 because the condition on line 213 was never true

214 return "dev" 

215 elif self is ReleaseLevel.Beta: 215 ↛ 216line 215 didn't jump to line 216 because the condition on line 215 was never true

216 return "beta" 

217 elif self is ReleaseLevel.Alpha: 217 ↛ 220line 217 didn't jump to line 220 because the condition on line 217 was always true

218 return "alpha" 

219 

220 raise ToolingException(f"Unknown ReleaseLevel '{self.name}'.") 

221 

222 

223@export 

224class Flags(Flag): 

225 """State enumeration, if a (tagged) version is build from a clean or dirty working directory.""" 

226 NoVCS = 0 #: No Version Control System VCS 

227 Clean = 1 #: A versioned build was created from a *clean* working directory. 

228 Dirty = 2 #: A versioned build was created from a *dirty* working directory. 

229 

230 CVS = 16 #: Concurrent Versions System (CVS) 

231 SVN = 32 #: Subversion (SVN) 

232 Git = 64 #: Git 

233 Hg = 128 #: Mercurial (Hg) 

234 

235 

236@export 

237def WordSizeValidator( 

238 bits: Nullable[int] = None, 

239 majorBits: Nullable[int] = None, 

240 minorBits: Nullable[int] = None, 

241 microBits: Nullable[int] = None, 

242 buildBits: Nullable[int] = None 

243): 

244 """ 

245 A factory function to return a validator for Version instances for a positive integer range based on word-sizes in bits. 

246 

247 :param bits: Number of bits to encode any positive version number part. 

248 :param majorBits: Number of bits to encode a positive major number in a version. 

249 :param minorBits: Number of bits to encode a positive minor number in a version. 

250 :param microBits: Number of bits to encode a positive micro number in a version. 

251 :param buildBits: Number of bits to encode a positive build number in a version. 

252 :return: A validation function for Version instances. 

253 """ 

254 majorMax = minorMax = microMax = buildMax = -1 

255 if bits is not None: 

256 majorMax = minorMax = microMax = buildMax = 2**bits - 1 

257 

258 if majorBits is not None: 

259 majorMax = 2**majorBits - 1 

260 if minorBits is not None: 

261 minorMax = 2**minorBits - 1 

262 if microBits is not None: 

263 microMax = 2 ** microBits - 1 

264 if buildBits is not None: 264 ↛ 265line 264 didn't jump to line 265 because the condition on line 264 was never true

265 buildMax = 2**buildBits - 1 

266 

267 def validator(version: SemanticVersion) -> bool: 

268 if Parts.Major in version._parts and version._major > majorMax: 

269 raise ValueError(f"Field 'Version.Major' > {majorMax}.") 

270 

271 if Parts.Minor in version._parts and version._minor > minorMax: 

272 raise ValueError(f"Field 'Version.Minor' > {minorMax}.") 

273 

274 if Parts.Micro in version._parts and version._micro > microMax: 

275 raise ValueError(f"Field 'Version.Micro' > {microMax}.") 

276 

277 if Parts.Build in version._parts and version._build > buildMax: 277 ↛ 278line 277 didn't jump to line 278 because the condition on line 277 was never true

278 raise ValueError(f"Field 'Version.Build' > {buildMax}.") 

279 

280 return True 

281 

282 return validator 

283 

284 

285@export 

286def MaxValueValidator( 

287 max: Nullable[int] = None, 

288 majorMax: Nullable[int] = None, 

289 minorMax: Nullable[int] = None, 

290 microMax: Nullable[int] = None, 

291 buildMax: Nullable[int] = None 

292): 

293 """ 

294 A factory function to return a validator for Version instances checking for a positive integer range [0..max]. 

295 

296 :param max: The upper bound for any positive version number part. 

297 :param majorMax: The upper bound for the positive major number. 

298 :param minorMax: The upper bound for the positive minor number. 

299 :param microMax: The upper bound for the positive micro number. 

300 :param buildMax: The upper bound for the positive build number. 

301 :return: A validation function for Version instances. 

302 """ 

303 if max is not None: 303 ↛ 306line 303 didn't jump to line 306 because the condition on line 303 was always true

304 majorMax = minorMax = microMax = buildMax = max 

305 

306 def validator(version: SemanticVersion) -> bool: 

307 if Parts.Major in version._parts and version._major > majorMax: 

308 raise ValueError(f"Field 'Version.Major' > {majorMax}.") 

309 

310 if Parts.Minor in version._parts and version._minor > minorMax: 

311 raise ValueError(f"Field 'Version.Minor' > {minorMax}.") 

312 

313 if Parts.Micro in version._parts and version._micro > microMax: 

314 raise ValueError(f"Field 'Version.Micro' > {microMax}.") 

315 

316 if Parts.Build in version._parts and version._build > buildMax: 316 ↛ 317line 316 didn't jump to line 317 because the condition on line 316 was never true

317 raise ValueError(f"Field 'Version.Build' > {buildMax}.") 

318 

319 return True 

320 

321 return validator 

322 

323 

324@export 

325class Version(metaclass=ExtendedType, slots=True): 

326 """Base-class for a version representation.""" 

327 

328 __hash: Nullable[int] #: once computed hash of the object 

329 

330 _parts: Parts #: Integer flag enumeration of present parts in a version number. 

331 _prefix: str #: Prefix string 

332 _major: int #: Major number part of the version number. 

333 _minor: int #: Minor number part of the version number. 

334 _micro: int #: Micro number part of the version number. 

335 _releaseLevel: ReleaseLevel #: Release level (alpha, beta, rc, final, ...). 

336 _releaseNumber: int #: Release number (Python calls this a serial). 

337 _post: int #: Post-release version number part. 

338 _dev: int #: Development number 

339 _build: int #: Build number part of the version number. 

340 _postfix: str #: Postfix string 

341 _hash: str #: Hash from version control system. 

342 _flags: Flags #: State if the version in a working directory is clean or dirty compared to a tagged version. 

343 

344 def __init__( 

345 self, 

346 major: int, 

347 minor: Nullable[int] = None, 

348 micro: Nullable[int] = None, 

349 level: Nullable[ReleaseLevel] = ReleaseLevel.Final, 

350 number: Nullable[int] = None, 

351 post: Nullable[int] = None, 

352 dev: Nullable[int] = None, 

353 *, 

354 build: Nullable[int] = None, 

355 postfix: Nullable[str] = None, 

356 prefix: Nullable[str] = None, 

357 hash: Nullable[str] = None, 

358 flags: Flags = Flags.NoVCS 

359 ) -> None: 

360 """ 

361 Initializes a version number representation. 

362 

363 :param major: Major number part of the version number. 

364 :param minor: Minor number part of the version number. 

365 :param micro: Micro (patch) number part of the version number. 

366 :param level: Release level (alpha, beta, release candidate, final, ...) of the version number. 

367 :param number: Release number part (in combination with release level) of the version number. 

368 :param post: Post number part of the version number. 

369 :param dev: Development number part of the version number. 

370 :param build: Build number part of the version number. 

371 :param postfix: The version number's postfix. 

372 :param prefix: The version number's prefix. 

373 :param hash: Postfix string. 

374 :param flags: The version number's flags. 

375 :raises TypeError: If parameter 'major' is not of type int. 

376 :raises ValueError: If parameter 'major' is a negative number. 

377 :raises TypeError: If parameter 'minor' is not of type int. 

378 :raises ValueError: If parameter 'minor' is a negative number. 

379 :raises TypeError: If parameter 'micro' is not of type int. 

380 :raises ValueError: If parameter 'micro' is a negative number. 

381 :raises TypeError: If parameter 'build' is not of type int. 

382 :raises ValueError: If parameter 'build' is a negative number. 

383 :raises TypeError: If parameter 'prefix' is not of type str. 

384 :raises TypeError: If parameter 'postfix' is not of type str. 

385 """ 

386 self.__hash = None 

387 

388 if not isinstance(major, int): 

389 raise TypeError("Parameter 'major' is not of type 'int'.") 

390 elif major < 0: 

391 raise ValueError("Parameter 'major' is negative.") 

392 

393 self._parts = Parts.Major 

394 self._major = major 

395 

396 if minor is not None: 

397 if not isinstance(minor, int): 

398 raise TypeError("Parameter 'minor' is not of type 'int'.") 

399 elif minor < 0: 

400 raise ValueError("Parameter 'minor' is negative.") 

401 

402 self._parts |= Parts.Minor 

403 self._minor = minor 

404 else: 

405 self._minor = 0 

406 

407 if micro is not None: 

408 if not isinstance(micro, int): 

409 raise TypeError("Parameter 'micro' is not of type 'int'.") 

410 elif micro < 0: 

411 raise ValueError("Parameter 'micro' is negative.") 

412 

413 self._parts |= Parts.Micro 

414 self._micro = micro 

415 else: 

416 self._micro = 0 

417 

418 if level is None: 

419 raise ValueError("Parameter 'level' is None.") 

420 elif not isinstance(level, ReleaseLevel): 

421 raise TypeError("Parameter 'level' is not of type 'ReleaseLevel'.") 

422 elif level is ReleaseLevel.Final: 

423 if number is not None: 

424 raise ValueError("Parameter 'number' must be None, if parameter 'level' is 'Final'.") 

425 

426 self._parts |= Parts.Level 

427 self._releaseLevel = level 

428 self._releaseNumber = 0 

429 else: 

430 self._parts |= Parts.Level 

431 self._releaseLevel = level 

432 

433 if number is not None: 

434 if not isinstance(number, int): 

435 raise TypeError("Parameter 'number' is not of type 'int'.") 

436 elif number < 0: 

437 raise ValueError("Parameter 'number' is negative.") 

438 

439 self._releaseNumber = number 

440 else: 

441 self._releaseNumber = 0 

442 

443 if dev is not None: 

444 if not isinstance(dev, int): 

445 raise TypeError("Parameter 'dev' is not of type 'int'.") 

446 elif dev < 0: 

447 raise ValueError("Parameter 'dev' is negative.") 

448 

449 self._parts |= Parts.Dev 

450 self._dev = dev 

451 else: 

452 self._dev = 0 

453 

454 if post is not None: 

455 if not isinstance(post, int): 

456 raise TypeError("Parameter 'post' is not of type 'int'.") 

457 elif post < 0: 

458 raise ValueError("Parameter 'post' is negative.") 

459 

460 self._parts |= Parts.Post 

461 self._post = post 

462 else: 

463 self._post = 0 

464 

465 if build is not None: 

466 if not isinstance(build, int): 

467 raise TypeError("Parameter 'build' is not of type 'int'.") 

468 elif build < 0: 

469 raise ValueError("Parameter 'build' is negative.") 

470 

471 self._build = build 

472 self._parts |= Parts.Build 

473 else: 

474 self._build = 0 

475 

476 if postfix is not None: 

477 if not isinstance(postfix, str): 

478 raise TypeError("Parameter 'postfix' is not of type 'str'.") 

479 

480 self._parts |= Parts.Postfix 

481 self._postfix = postfix 

482 else: 

483 self._postfix = "" 

484 

485 if prefix is not None: 

486 if not isinstance(prefix, str): 

487 raise TypeError("Parameter 'prefix' is not of type 'str'.") 

488 

489 self._parts |= Parts.Prefix 

490 self._prefix = prefix 

491 else: 

492 self._prefix = "" 

493 

494 if hash is not None: 

495 if not isinstance(hash, str): 

496 raise TypeError("Parameter 'hash' is not of type 'str'.") 

497 

498 self._parts |= Parts.Hash 

499 self._hash = hash 

500 else: 

501 self._hash = "" 

502 

503 if flags is None: 

504 raise ValueError("Parameter 'flags' is None.") 

505 elif not isinstance(flags, Flags): 

506 raise TypeError("Parameter 'flags' is not of type 'Flags'.") 

507 

508 self._flags = flags 

509 

510 @classmethod 

511 @abstractmethod 

512 def Parse(cls, versionString: Nullable[str], validator: Nullable[Callable[["SemanticVersion"], bool]] = None) -> "Version": 

513 """Parse a version string and return a Version instance.""" 

514 

515 @readonly 

516 def Parts(self) -> Parts: 

517 """ 

518 Read-only property to access the used parts of this version number. 

519 

520 :return: A flag enumeration of used version number parts. 

521 """ 

522 return self._parts 

523 

524 @readonly 

525 def Prefix(self) -> str: 

526 """ 

527 Read-only property to access the version number's prefix. 

528 

529 :return: The prefix of the version number. 

530 """ 

531 return self._prefix 

532 

533 @readonly 

534 def Major(self) -> int: 

535 """ 

536 Read-only property to access the major number. 

537 

538 :return: The major number. 

539 """ 

540 return self._major 

541 

542 @readonly 

543 def Minor(self) -> int: 

544 """ 

545 Read-only property to access the minor number. 

546 

547 :return: The minor number. 

548 """ 

549 return self._minor 

550 

551 @readonly 

552 def Micro(self) -> int: 

553 """ 

554 Read-only property to access the micro number. 

555 

556 :return: The micro number. 

557 """ 

558 return self._micro 

559 

560 @readonly 

561 def ReleaseLevel(self) -> ReleaseLevel: 

562 """ 

563 Read-only property to access the release level. 

564 

565 :return: The release level. 

566 """ 

567 return self._releaseLevel 

568 

569 @readonly 

570 def ReleaseNumber(self) -> int: 

571 """ 

572 Read-only property to access the release number. 

573 

574 :return: The release number. 

575 """ 

576 return self._releaseNumber 

577 

578 @readonly 

579 def Post(self) -> int: 

580 """ 

581 Read-only property to access the post number. 

582 

583 :return: The post number. 

584 """ 

585 return self._post 

586 

587 @readonly 

588 def Dev(self) -> int: 

589 """ 

590 Read-only property to access the development number. 

591 

592 :return: The development number. 

593 """ 

594 return self._dev 

595 

596 @readonly 

597 def Build(self) -> int: 

598 """ 

599 Read-only property to access the build number. 

600 

601 :return: The build number. 

602 """ 

603 return self._build 

604 

605 @readonly 

606 def Postfix(self) -> str: 

607 """ 

608 Read-only property to access the version number's postfix. 

609 

610 :return: The postfix of the version number. 

611 """ 

612 return self._postfix 

613 

614 @readonly 

615 def Hash(self) -> str: 

616 """ 

617 Read-only property to access the version number's hash. 

618 

619 :return: The hash. 

620 """ 

621 return self._hash 

622 

623 @readonly 

624 def Flags(self) -> Flags: 

625 """ 

626 Read-only property to access the version number's flags. 

627 

628 :return: The flags of the version number. 

629 """ 

630 return self._flags 

631 

632 def _equal(self, left: "Version", right: "Version") -> Nullable[bool]: 

633 """ 

634 Private helper method to compute the equality of two :class:`Version` instances. 

635 

636 :param left: Left operand. 

637 :param right: Right operand. 

638 :returns: ``True``, if ``left`` is equal to ``right``, otherwise it's ``False``. 

639 """ 

640 return ( 

641 (left._major == right._major) and 

642 (left._minor == right._minor) and 

643 (left._micro == right._micro) and 

644 (left._releaseLevel == right._releaseLevel) and 

645 (left._releaseNumber == right._releaseNumber) and 

646 (left._post == right._post) and 

647 (left._dev == right._dev) and 

648 (left._build == right._build) and 

649 (left._postfix == right._postfix) 

650 ) 

651 

652 def _compare(self, left: "Version", right: "Version") -> Nullable[bool]: 

653 """ 

654 Private helper method to compute the comparison of two :class:`Version` instances. 

655 

656 :param left: Left operand. 

657 :param right: Right operand. 

658 :returns: ``True``, if ``left`` is smaller than ``right``. |br| 

659 False if ``left`` is greater than ``right``. |br| 

660 Otherwise it's None (both operands are equal). 

661 """ 

662 if left._major < right._major: 

663 return True 

664 elif left._major > right._major: 

665 return False 

666 

667 if left._minor < right._minor: 

668 return True 

669 elif left._minor > right._minor: 

670 return False 

671 

672 if left._micro < right._micro: 

673 return True 

674 elif left._micro > right._micro: 

675 return False 

676 

677 if left._releaseLevel < right._releaseLevel: 677 ↛ 678line 677 didn't jump to line 678 because the condition on line 677 was never true

678 return True 

679 elif left._releaseLevel > right._releaseLevel: 679 ↛ 680line 679 didn't jump to line 680 because the condition on line 679 was never true

680 return False 

681 

682 if left._releaseNumber < right._releaseNumber: 682 ↛ 683line 682 didn't jump to line 683 because the condition on line 682 was never true

683 return True 

684 elif left._releaseNumber > right._releaseNumber: 684 ↛ 685line 684 didn't jump to line 685 because the condition on line 684 was never true

685 return False 

686 

687 if left._post < right._post: 687 ↛ 688line 687 didn't jump to line 688 because the condition on line 687 was never true

688 return True 

689 elif left._post > right._post: 689 ↛ 690line 689 didn't jump to line 690 because the condition on line 689 was never true

690 return False 

691 

692 if left._dev < right._dev: 692 ↛ 693line 692 didn't jump to line 693 because the condition on line 692 was never true

693 return True 

694 elif left._dev > right._dev: 694 ↛ 695line 694 didn't jump to line 695 because the condition on line 694 was never true

695 return False 

696 

697 if left._build < right._build: 697 ↛ 698line 697 didn't jump to line 698 because the condition on line 697 was never true

698 return True 

699 elif left._build > right._build: 699 ↛ 700line 699 didn't jump to line 700 because the condition on line 699 was never true

700 return False 

701 

702 return None 

703 

704 def _minimum(self, actual: "Version", expected: "Version") -> Nullable[bool]: 

705 exactMajor = Parts.Minor in expected._parts 

706 exactMinor = Parts.Micro in expected._parts 

707 

708 if exactMajor and actual._major != expected._major: 708 ↛ 709line 708 didn't jump to line 709 because the condition on line 708 was never true

709 return False 

710 elif not exactMajor and actual._major < expected._major: 

711 return False 

712 

713 if exactMinor and actual._minor != expected._minor: 713 ↛ 714line 713 didn't jump to line 714 because the condition on line 713 was never true

714 return False 

715 elif not exactMinor and actual._minor < expected._minor: 

716 return False 

717 

718 if Parts.Micro in expected._parts: 

719 return actual._micro >= expected._micro 

720 

721 return True 

722 

723 def _format(self, formatSpec: str) -> str: 

724 """ 

725 Return a string representation of this version number according to the format specification. 

726 

727 .. topic:: Format Specifiers 

728 

729 * ``%p`` - prefix 

730 * ``%M`` - major number 

731 * ``%m`` - minor number 

732 * ``%u`` - micro number 

733 * ``%b`` - build number 

734 

735 :param formatSpec: The format specification. 

736 :return: Formatted version number. 

737 """ 

738 if formatSpec == "": 

739 return self.__str__() 

740 

741 result = formatSpec 

742 result = result.replace("%p", str(self._prefix)) 

743 result = result.replace("%M", str(self._major)) 

744 result = result.replace("%m", str(self._minor)) 

745 result = result.replace("%u", str(self._micro)) 

746 result = result.replace("%b", str(self._build)) 

747 result = result.replace("%r", str(self._releaseLevel)[0]) 

748 result = result.replace("%R", str(self._releaseLevel)) 

749 result = result.replace("%n", str(self._releaseNumber)) 

750 result = result.replace("%d", str(self._dev)) 

751 result = result.replace("%P", str(self._postfix)) 

752 

753 return result 

754 

755 @mustoverride 

756 def __eq__(self, other: Union["Version", str, int, None]) -> bool: 

757 """ 

758 Compare two version numbers for equality. 

759 

760 The second operand should be an instance of :class:`Version`, but ``str`` and ``int`` are accepted, too. |br| 

761 In case of ``str``, it's tried to parse the string as a version number. In case of ``int``, a single major 

762 number is assumed (all other parts are zero). 

763 

764 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

765 number. 

766 

767 :param other: Operand to compare against. 

768 :returns: ``True``, if both version numbers are equal. 

769 :raises ValueError: If parameter ``other`` is None. 

770 :raises TypeError: If parameter ``other`` is not of type :class:`Version`, :class:`str` or :class:`ìnt`. 

771 """ 

772 if other is None: 

773 raise ValueError(f"Second operand is None.") 

774 elif ((sC := self.__class__) is (oC := other.__class__) or issubclass(sC, oC) or issubclass(oC, sC)): 

775 pass 

776 elif isinstance(other, str): 

777 other = self.__class__.Parse(other) 

778 elif isinstance(other, int): 

779 other = self.__class__(major=other) 

780 else: 

781 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.") 

782 ex.add_note(f"Supported types for second operand: {self.__class__.__name__}, str, int") 

783 raise ex 

784 

785 return self._equal(self, other) 

786 

787 @mustoverride 

788 def __ne__(self, other: Union["Version", str, int, None]) -> bool: 

789 """ 

790 Compare two version numbers for inequality. 

791 

792 The second operand should be an instance of :class:`Version`, but ``str`` and ``int`` are accepted, too. |br| 

793 In case of ``str``, it's tried to parse the string as a version number. In case of ``int``, a single major 

794 number is assumed (all other parts are zero). 

795 

796 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

797 number. 

798 

799 :param other: Operand to compare against. 

800 :returns: ``True``, if both version numbers are not equal. 

801 :raises ValueError: If parameter ``other`` is None. 

802 :raises TypeError: If parameter ``other`` is not of type :class:`Version`, :class:`str` or :class:`ìnt`. 

803 """ 

804 if other is None: 

805 raise ValueError(f"Second operand is None.") 

806 elif ((sC := self.__class__) is (oC := other.__class__) or issubclass(sC, oC) or issubclass(oC, sC)): 

807 pass 

808 elif isinstance(other, str): 

809 other = self.__class__.Parse(other) 

810 elif isinstance(other, int): 

811 other = self.__class__(major=other) 

812 else: 

813 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by == operator.") 

814 ex.add_note(f"Supported types for second operand: {self.__class__.__name__}, str, int") 

815 raise ex 

816 

817 return not self._equal(self, other) 

818 

819 @mustoverride 

820 def __lt__(self, other: Union["Version", str, int, None]) -> bool: 

821 """ 

822 Compare two version numbers if the version is less than the second operand. 

823 

824 The second operand should be an instance of :class:`Version`, but :class:`VersionRange`, :class:`VersionSet`, 

825 ``str`` and ``int`` are accepted, too. |br| 

826 In case of ``str``, it's tried to parse the string as a version number. In case of ``int``, a single major 

827 number is assumed (all other parts are zero). 

828 

829 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

830 number. 

831 

832 :param other: Operand to compare against. 

833 :returns: ``True``, if version is less than the second operand. 

834 :raises ValueError: If parameter ``other`` is None. 

835 :raises TypeError: If parameter ``other`` is not of type :class:`Version`, :class:`VersionRange`, :class:`VersionSet`, :class:`str` or :class:`ìnt`. 

836 """ 

837 if other is None: 

838 raise ValueError(f"Second operand is None.") 

839 elif ((sC := self.__class__) is (oC := other.__class__) or issubclass(sC, oC) or issubclass(oC, sC)): 

840 pass 

841 elif isinstance(other, VersionRange): 

842 other = other._lowerBound 

843 elif isinstance(other, VersionSet): 

844 other = other._items[0] 

845 elif isinstance(other, str): 

846 other = self.__class__.Parse(other) 

847 elif isinstance(other, int): 

848 other = self.__class__(major=other) 

849 else: 

850 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by < operator.") 

851 ex.add_note(f"Supported types for second operand: {self.__class__.__name__}, VersionRange, VersionSet, str, int") 

852 raise ex 

853 

854 return self._compare(self, other) is True 

855 

856 @mustoverride 

857 def __le__(self, other: Union["Version", str, int, None]) -> bool: 

858 """ 

859 Compare two version numbers if the version is less than or equal the second operand. 

860 

861 The second operand should be an instance of :class:`Version`, :class:`VersionRange`, :class:`VersionSet`, but 

862 ``str`` and ``int`` are accepted, too. |br| 

863 In case of ``str``, it's tried to parse the string as a version number. In case of ``int``, a single major 

864 number is assumed (all other parts are zero). 

865 

866 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

867 number. 

868 

869 :param other: Operand to compare against. 

870 :returns: ``True``, if version is less than or equal the second operand. 

871 :raises ValueError: If parameter ``other`` is None. 

872 :raises TypeError: If parameter ``other`` is not of type :class:`Version`, :class:`VersionRange`, :class:`VersionSet`, :class:`str` or :class:`ìnt`. 

873 """ 

874 equalValue = True 

875 if other is None: 

876 raise ValueError(f"Second operand is None.") 

877 elif ((sC := self.__class__) is (oC := other.__class__) or issubclass(sC, oC) or issubclass(oC, sC)): 

878 pass 

879 elif isinstance(other, VersionRange): 

880 equalValue = RangeBoundHandling.LowerBoundExclusive not in other._boundHandling 

881 other = other._lowerBound 

882 elif isinstance(other, VersionSet): 

883 other = other._items[0] 

884 elif isinstance(other, str): 

885 other = self.__class__.Parse(other) 

886 elif isinstance(other, int): 

887 other = self.__class__(major=other) 

888 else: 

889 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by <= operator.") 

890 ex.add_note(f"Supported types for second operand: {self.__class__.__name__}, VersionRange, VersionSet, str, int") 

891 raise ex 

892 

893 result = self._compare(self, other) 

894 return result if result is not None else equalValue 

895 

896 @mustoverride 

897 def __gt__(self, other: Union["Version", str, int, None]) -> bool: 

898 """ 

899 Compare two version numbers if the version is greater than the second operand. 

900 

901 The second operand should be an instance of :class:`Version`, :class:`VersionRange`, :class:`VersionSet`, but 

902 ``str`` and ``int`` are accepted, too. |br| 

903 In case of ``str``, it's tried to parse the string as a version number. In case of ``int``, a single major 

904 number is assumed (all other parts are zero). 

905 

906 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

907 number. 

908 

909 :param other: Operand to compare against. 

910 :returns: ``True``, if version is greater than the second operand. 

911 :raises ValueError: If parameter ``other`` is None. 

912 :raises TypeError: If parameter ``other`` is not of type :class:`Version`, :class:`VersionRange`, :class:`VersionSet`, :class:`str` or :class:`ìnt`. 

913 """ 

914 if other is None: 

915 raise ValueError(f"Second operand is None.") 

916 elif ((sC := self.__class__) is (oC := other.__class__) or issubclass(sC, oC) or issubclass(oC, sC)): 

917 pass 

918 elif isinstance(other, VersionRange): 

919 other = other._upperBound 

920 elif isinstance(other, VersionSet): 

921 other = other._items[-1] 

922 elif isinstance(other, str): 

923 other = self.__class__.Parse(other) 

924 elif isinstance(other, int): 

925 other = self.__class__(major=other) 

926 else: 

927 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by > operator.") 

928 ex.add_note(f"Supported types for second operand: {self.__class__.__name__}, VersionRange, VersionSet, str, int") 

929 raise ex 

930 

931 return self._compare(self, other) is False 

932 

933 @mustoverride 

934 def __ge__(self, other: Union["Version", str, int, None]) -> bool: 

935 """ 

936 Compare two version numbers if the version is greater than or equal the second operand. 

937 

938 The second operand should be an instance of :class:`Version`, :class:`VersionRange`, :class:`VersionSet`, but 

939 ``str`` and ``int`` are accepted, too. |br| 

940 In case of ``str``, it's tried to parse the string as a version number. In case of ``int``, a single major 

941 number is assumed (all other parts are zero). 

942 

943 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

944 number. 

945 

946 :param other: Operand to compare against. 

947 :returns: ``True``, if version is greater than or equal the second operand. 

948 :raises ValueError: If parameter ``other`` is None. 

949 :raises TypeError: If parameter ``other`` is not of type :class:`Version`, :class:`VersionRange`, :class:`VersionSet`, :class:`str` or :class:`ìnt`. 

950 """ 

951 equalValue = True 

952 if other is None: 

953 raise ValueError(f"Second operand is None.") 

954 elif ((sC := self.__class__) is (oC := other.__class__) or issubclass(sC, oC) or issubclass(oC, sC)): 

955 pass 

956 elif isinstance(other, VersionRange): 

957 equalValue = RangeBoundHandling.UpperBoundExclusive not in other._boundHandling 

958 other = other._upperBound 

959 elif isinstance(other, VersionSet): 

960 other = other._items[-1] 

961 elif isinstance(other, str): 

962 other = self.__class__.Parse(other) 

963 elif isinstance(other, int): 

964 other = self.__class__(major=other) 

965 else: 

966 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by >= operator.") 

967 ex.add_note(f"Supported types for second operand: {self.__class__.__name__}, VersionRange, VersionSet, str, int") 

968 raise ex 

969 

970 result = self._compare(self, other) 

971 return not result if result is not None else equalValue 

972 

973 def __rshift__(self, other: Union["Version", str, int, None]) -> bool: 

974 if other is None: 

975 raise ValueError(f"Second operand is None.") 

976 elif isinstance(other, self.__class__): 

977 pass 

978 elif isinstance(other, str): 

979 other = self.__class__.Parse(other) 

980 elif isinstance(other, int): 

981 other = self.__class__(major=other) 

982 else: 

983 ex = TypeError(f"Second operand of type '{other.__class__.__name__}' is not supported by %= operator.") 

984 ex.add_note(f"Supported types for second operand: {self.__class__.__name__}, str, int") 

985 raise ex 

986 

987 return self._minimum(self, other) 

988 

989 def __hash__(self) -> int: 

990 if self.__hash is None: 990 ↛ 1005line 990 didn't jump to line 1005 because the condition on line 990 was always true

991 self.__hash = hash(( 

992 self._prefix, 

993 self._major, 

994 self._minor, 

995 self._micro, 

996 self._releaseLevel, 

997 self._releaseNumber, 

998 self._post, 

999 self._dev, 

1000 self._build, 

1001 self._postfix, 

1002 self._hash, 

1003 self._flags 

1004 )) 

1005 return self.__hash 

1006 

1007 

1008@export 

1009class SemanticVersion(Version): 

1010 """Representation of a semantic version number like ``3.7.12``.""" 

1011 

1012 _PATTERN = re_compile( 

1013 r"^" 

1014 r"(?P<prefix>[a-zA-Z]*)" 

1015 r"(?P<major>\d+)" 

1016 r"(?:\.(?P<minor>\d+))?" 

1017 r"(?:\.(?P<micro>\d+))?" 

1018 r"(?:" 

1019 r"(?:\.(?P<build>\d+))" 

1020 r"|" 

1021 r"(?:[-](?P<release>dev|final))" 

1022 r"|" 

1023 r"(?:(?P<delim1>[\.\-]?)(?P<level>alpha|beta|gamma|a|b|c|rc|pl)(?P<number>\d+))" 

1024 r")?" 

1025 r"(?:(?P<delim2>[\.\-]post)(?P<post>\d+))?" 

1026 r"(?:(?P<delim3>[\.\-]dev)(?P<dev>\d+))?" 

1027 r"(?:(?P<delim4>[\.\-\+])(?P<postfix>\w+))?" 

1028 r"$" 

1029 ) 

1030# QUESTION: was this how many commits a version is ahead of the last tagged version? 

1031# ahead: int = 0 

1032 

1033 def __init__( 

1034 self, 

1035 major: int, 

1036 minor: Nullable[int] = None, 

1037 micro: Nullable[int] = None, 

1038 level: Nullable[ReleaseLevel] = ReleaseLevel.Final, 

1039 number: Nullable[int] = None, 

1040 post: Nullable[int] = None, 

1041 dev: Nullable[int] = None, 

1042 *, 

1043 build: Nullable[int] = None, 

1044 postfix: Nullable[str] = None, 

1045 prefix: Nullable[str] = None, 

1046 hash: Nullable[str] = None, 

1047 flags: Flags = Flags.NoVCS 

1048 ) -> None: 

1049 """ 

1050 Initializes a semantic version number representation. 

1051 

1052 :param major: Major number part of the version number. 

1053 :param minor: Minor number part of the version number. 

1054 :param micro: Micro (patch) number part of the version number. 

1055 :param build: Build number part of the version number. 

1056 :param level: tbd 

1057 :param number: tbd 

1058 :param post: Post number part of the version number. 

1059 :param dev: Development number part of the version number. 

1060 :param prefix: The version number's prefix. 

1061 :param postfix: The version number's postfix. 

1062 :param flags: The version number's flags. 

1063 :param hash: tbd 

1064 :raises TypeError: If parameter 'major' is not of type int. 

1065 :raises ValueError: If parameter 'major' is a negative number. 

1066 :raises TypeError: If parameter 'minor' is not of type int. 

1067 :raises ValueError: If parameter 'minor' is a negative number. 

1068 :raises TypeError: If parameter 'micro' is not of type int. 

1069 :raises ValueError: If parameter 'micro' is a negative number. 

1070 :raises TypeError: If parameter 'build' is not of type int. 

1071 :raises ValueError: If parameter 'build' is a negative number. 

1072 :raises TypeError: If parameter 'post' is not of type int. 

1073 :raises ValueError: If parameter 'post' is a negative number. 

1074 :raises TypeError: If parameter 'dev' is not of type int. 

1075 :raises ValueError: If parameter 'dev' is a negative number. 

1076 :raises TypeError: If parameter 'prefix' is not of type str. 

1077 :raises TypeError: If parameter 'postfix' is not of type str. 

1078 """ 

1079 super().__init__(major, minor, micro, level, number, post, dev, build=build, postfix=postfix, prefix=prefix, hash=hash, flags=flags) 

1080 

1081 @classmethod 

1082 def Parse(cls, versionString: Nullable[str], validator: Nullable[Callable[["SemanticVersion"], bool]] = None) -> "SemanticVersion": 

1083 """ 

1084 Parse a version string and return a :class:`SemanticVersion` instance. 

1085 

1086 Allowed prefix characters: 

1087 

1088 * ``v|V`` - version, public version, public release 

1089 * ``i|I`` - internal version, internal release 

1090 * ``r|R`` - release, revision 

1091 * ``rev|REV`` - revision 

1092 

1093 :param versionString: The version string to parse. 

1094 :returns: An object representing a semantic version. 

1095 :raises TypeError: If parameter ``other`` is not a string. 

1096 :raises ValueError: If parameter ``other`` is None. 

1097 :raises ValueError: If parameter ``other`` is empty. 

1098 """ 

1099 if versionString is None: 

1100 raise ValueError("Parameter 'versionString' is None.") 

1101 elif not isinstance(versionString, str): 

1102 ex = TypeError(f"Parameter 'versionString' is not of type 'str'.") 

1103 ex.add_note(f"Got type '{getFullyQualifiedName(versionString)}'.") 

1104 raise ex 

1105 

1106 versionString = versionString.strip() 

1107 if versionString == "": 

1108 raise ValueError("Parameter 'versionString' is empty.") 

1109 

1110 match = cls._PATTERN.match(versionString) 

1111 if match is None: 

1112 raise ValueError(f"Syntax error in parameter 'versionString': '{versionString}'") 

1113 

1114 def toInt(value: Nullable[str]) -> Nullable[int]: 

1115 if value is None or value == "": 

1116 return None 

1117 try: 

1118 return int(value) 

1119 except ValueError as ex: # pragma: no cover 

1120 raise ValueError(f"Invalid part '{value}' in version number '{versionString}'.") from ex 

1121 

1122 release = match["release"] 

1123 if release is not None: 

1124 if release == "dev": 1124 ↛ 1126line 1124 didn't jump to line 1126 because the condition on line 1124 was always true

1125 releaseLevel = ReleaseLevel.Development 

1126 elif release == "final": 

1127 releaseLevel = ReleaseLevel.Final 

1128 else: # pragma: no cover 

1129 raise ValueError(f"Unknown release level '{release}' in version number '{versionString}'.") 

1130 else: 

1131 level = match["level"] 

1132 if level is not None: 

1133 level = level.lower() 

1134 if level == "a" or level == "alpha": 

1135 releaseLevel = ReleaseLevel.Alpha 

1136 elif level == "b" or level == "beta": 

1137 releaseLevel = ReleaseLevel.Beta 

1138 elif level == "c" or level == "gamma": 

1139 releaseLevel = ReleaseLevel.Gamma 

1140 elif level == "rc": 

1141 releaseLevel = ReleaseLevel.ReleaseCandidate 

1142 else: # pragma: no cover 

1143 raise ValueError(f"Unknown release level '{level}' in version number '{versionString}'.") 

1144 else: 

1145 releaseLevel = ReleaseLevel.Final 

1146 

1147 version = cls( 

1148 major=toInt(match["major"]), 

1149 minor=toInt(match["minor"]), 

1150 micro=toInt(match["micro"]), 

1151 level=releaseLevel, 

1152 number=toInt(match["number"]), 

1153 post=toInt(match["post"]), 

1154 dev=toInt(match["dev"]), 

1155 build=toInt(match["build"]), 

1156 postfix=match["postfix"], 

1157 prefix=match["prefix"], 

1158 # hash=match["hash"], 

1159 flags=Flags.Clean 

1160 ) 

1161 if validator is not None and not validator(version): 

1162 raise ValueError(f"Failed to validate version string '{versionString}'.") # pragma: no cover 

1163 

1164 return version 

1165 

1166 @readonly 

1167 def Patch(self) -> int: 

1168 """ 

1169 Read-only property to access the patch number. 

1170 

1171 The patch number is identical to the micro number. 

1172 

1173 :return: The patch number. 

1174 """ 

1175 return self._micro 

1176 

1177 def _equal(self, left: "SemanticVersion", right: "SemanticVersion") -> Nullable[bool]: 

1178 """ 

1179 Private helper method to compute the equality of two :class:`SemanticVersion` instances. 

1180 

1181 :param left: Left operand. 

1182 :param right: Right operand. 

1183 :returns: ``True``, if ``left`` is equal to ``right``, otherwise it's ``False``. 

1184 """ 

1185 return super()._equal(left, right) 

1186 

1187 def _compare(self, left: "SemanticVersion", right: "SemanticVersion") -> Nullable[bool]: 

1188 """ 

1189 Private helper method to compute the comparison of two :class:`SemanticVersion` instances. 

1190 

1191 :param left: Left operand. 

1192 :param right: Right operand. 

1193 :returns: ``True``, if ``left`` is smaller than ``right``. |br| 

1194 False if ``left`` is greater than ``right``. |br| 

1195 Otherwise it's None (both operands are equal). 

1196 """ 

1197 return super()._compare(left, right) 

1198 

1199 def __eq__(self, other: Union["SemanticVersion", str, int, None]) -> bool: 

1200 """ 

1201 Compare two version numbers for equality. 

1202 

1203 The second operand should be an instance of :class:`SemanticVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1204 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1205 number is assumed (all other parts are zero). 

1206 

1207 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1208 number. 

1209 

1210 :param other: Operand to compare against. 

1211 :returns: ``True``, if both version numbers are equal. 

1212 :raises ValueError: If parameter ``other`` is None. 

1213 :raises TypeError: If parameter ``other`` is not of type :class:`SemanticVersion`, :class:`str` or :class:`ìnt`. 

1214 """ 

1215 return super().__eq__(other) 

1216 

1217 def __ne__(self, other: Union["SemanticVersion", str, int, None]) -> bool: 

1218 """ 

1219 Compare two version numbers for inequality. 

1220 

1221 The second operand should be an instance of :class:`SemanticVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1222 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1223 number is assumed (all other parts are zero). 

1224 

1225 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1226 number. 

1227 

1228 :param other: Operand to compare against. 

1229 :returns: ``True``, if both version numbers are not equal. 

1230 :raises ValueError: If parameter ``other`` is None. 

1231 :raises TypeError: If parameter ``other`` is not of type :class:`SemanticVersion`, :class:`str` or :class:`ìnt`. 

1232 """ 

1233 return super().__ne__(other) 

1234 

1235 def __lt__(self, other: Union["SemanticVersion", str, int, None]) -> bool: 

1236 """ 

1237 Compare two version numbers if the version is less than the second operand. 

1238 

1239 The second operand should be an instance of :class:`SemanticVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1240 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1241 number is assumed (all other parts are zero). 

1242 

1243 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1244 number. 

1245 

1246 :param other: Operand to compare against. 

1247 :returns: ``True``, if version is less than the second operand. 

1248 :raises ValueError: If parameter ``other`` is None. 

1249 :raises TypeError: If parameter ``other`` is not of type :class:`SemanticVersion`, :class:`str` or :class:`ìnt`. 

1250 """ 

1251 return super().__lt__(other) 

1252 

1253 def __le__(self, other: Union["SemanticVersion", str, int, None]) -> bool: 

1254 """ 

1255 Compare two version numbers if the version is less than or equal the second operand. 

1256 

1257 The second operand should be an instance of :class:`SemanticVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1258 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1259 number is assumed (all other parts are zero). 

1260 

1261 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1262 number. 

1263 

1264 :param other: Operand to compare against. 

1265 :returns: ``True``, if version is less than or equal the second operand. 

1266 :raises ValueError: If parameter ``other`` is None. 

1267 :raises TypeError: If parameter ``other`` is not of type :class:`SemanticVersion`, :class:`str` or :class:`ìnt`. 

1268 """ 

1269 return super().__le__(other) 

1270 

1271 def __gt__(self, other: Union["SemanticVersion", str, int, None]) -> bool: 

1272 """ 

1273 Compare two version numbers if the version is greater than the second operand. 

1274 

1275 The second operand should be an instance of :class:`SemanticVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1276 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1277 number is assumed (all other parts are zero). 

1278 

1279 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1280 number. 

1281 

1282 :param other: Operand to compare against. 

1283 :returns: ``True``, if version is greater than the second operand. 

1284 :raises ValueError: If parameter ``other`` is None. 

1285 :raises TypeError: If parameter ``other`` is not of type :class:`SemanticVersion`, :class:`str` or :class:`ìnt`. 

1286 """ 

1287 return super().__gt__(other) 

1288 

1289 def __ge__(self, other: Union["SemanticVersion", str, int, None]) -> bool: 

1290 """ 

1291 Compare two version numbers if the version is greater than or equal the second operand. 

1292 

1293 The second operand should be an instance of :class:`SemanticVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1294 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1295 number is assumed (all other parts are zero). 

1296 

1297 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1298 number. 

1299 

1300 :param other: Operand to compare against. 

1301 :returns: ``True``, if version is greater than or equal the second operand. 

1302 :raises ValueError: If parameter ``other`` is None. 

1303 :raises TypeError: If parameter ``other`` is not of type :class:`SemanticVersion`, :class:`str` or :class:`ìnt`. 

1304 """ 

1305 return super().__ge__(other) 

1306 

1307 def __rshift__(self, other: Union["SemanticVersion", str, int, None]) -> bool: 

1308 return super().__rshift__(other) 

1309 

1310 def __hash__(self) -> int: 

1311 return super().__hash__() 

1312 

1313 def __format__(self, formatSpec: str) -> str: 

1314 result = self._format(formatSpec) 

1315 

1316 if (pos := result.find("%")) != -1 and result[pos + 1] != "%": # pragma: no cover 

1317 raise ValueError(f"Unknown format specifier '%{result[pos + 1]}' in '{formatSpec}'.") 

1318 

1319 return result.replace("%%", "%") 

1320 

1321 def __repr__(self) -> str: 

1322 """ 

1323 Return a string representation of this version number without prefix ``v``. 

1324 

1325 :returns: Raw version number representation without a prefix. 

1326 """ 

1327 return f"{self._prefix if Parts.Prefix in self._parts else ''}{self._major}.{self._minor}.{self._micro}" 

1328 

1329 def __str__(self) -> str: 

1330 """ 

1331 Return a string representation of this version number. 

1332 

1333 :returns: Version number representation. 

1334 """ 

1335 result = self._prefix if Parts.Prefix in self._parts else "" 

1336 result += f"{self._major}" # major is always present 

1337 result += f".{self._minor}" if Parts.Minor in self._parts else "" 

1338 result += f".{self._micro}" if Parts.Micro in self._parts else "" 

1339 result += f".{self._build}" if Parts.Build in self._parts else "" 

1340 if self._releaseLevel is ReleaseLevel.Development: 

1341 result += "-dev" 

1342 elif self._releaseLevel is ReleaseLevel.Alpha: 

1343 result += f".alpha{self._releaseNumber}" 

1344 elif self._releaseLevel is ReleaseLevel.Beta: 

1345 result += f".beta{self._releaseNumber}" 

1346 elif self._releaseLevel is ReleaseLevel.Gamma: 1346 ↛ 1347line 1346 didn't jump to line 1347 because the condition on line 1346 was never true

1347 result += f".gamma{self._releaseNumber}" 

1348 elif self._releaseLevel is ReleaseLevel.ReleaseCandidate: 

1349 result += f".rc{self._releaseNumber}" 

1350 result += f".post{self._post}" if Parts.Post in self._parts else "" 

1351 result += f".dev{self._dev}" if Parts.Dev in self._parts else "" 

1352 result += f"+{self._postfix}" if Parts.Postfix in self._parts else "" 

1353 

1354 return result 

1355 

1356 

1357@export 

1358class PythonVersion(SemanticVersion): 

1359 """ 

1360 Represents a Python version. 

1361 """ 

1362 

1363 @classmethod 

1364 def FromSysVersionInfo(cls) -> "PythonVersion": 

1365 """ 

1366 Create a Python version from :data:`sys.version_info`. 

1367 

1368 :returns: A PythonVersion instance of the current Python interpreter's version. 

1369 """ 

1370 from sys import version_info 

1371 

1372 if version_info.releaselevel == "final": 

1373 rl = ReleaseLevel.Final 

1374 number = None 

1375 else: # pragma: no cover 

1376 number = version_info.serial 

1377 

1378 if version_info.releaselevel == "alpha": 

1379 rl = ReleaseLevel.Alpha 

1380 elif version_info.releaselevel == "beta": 

1381 rl = ReleaseLevel.Beta 

1382 elif version_info.releaselevel == "candidate": 

1383 rl = ReleaseLevel.ReleaseCandidate 

1384 else: # pragma: no cover 

1385 raise ToolingException(f"Unsupported release level '{version_info.releaselevel}'.") 

1386 

1387 return cls(version_info.major, version_info.minor, version_info.micro, level=rl, number=number) 

1388 

1389 def __hash__(self) -> int: 

1390 return super().__hash__() 

1391 

1392 def __str__(self) -> str: 

1393 """ 

1394 Return a string representation of this version number. 

1395 

1396 :returns: Version number representation. 

1397 """ 

1398 result = self._prefix if Parts.Prefix in self._parts else "" 

1399 result += f"{self._major}" # major is always present 

1400 result += f".{self._minor}" if Parts.Minor in self._parts else "" 

1401 result += f".{self._micro}" if Parts.Micro in self._parts else "" 

1402 if self._releaseLevel is ReleaseLevel.Alpha: 

1403 result += f"a{self._releaseNumber}" 

1404 elif self._releaseLevel is ReleaseLevel.Beta: 

1405 result += f"b{self._releaseNumber}" 

1406 elif self._releaseLevel is ReleaseLevel.Gamma: 

1407 result += f"c{self._releaseNumber}" 

1408 elif self._releaseLevel is ReleaseLevel.ReleaseCandidate: 

1409 result += f"rc{self._releaseNumber}" 

1410 result += f".post{self._post}" if Parts.Post in self._parts else "" 

1411 result += f".dev{self._dev}" if Parts.Dev in self._parts else "" 

1412 result += f"+{self._postfix}" if Parts.Postfix in self._parts else "" 

1413 

1414 return result 

1415 

1416 

1417@export 

1418class CalendarVersion(Version): 

1419 """Representation of a calendar version number like ``2021.10``.""" 

1420 

1421 def __init__( 

1422 self, 

1423 major: int, 

1424 minor: Nullable[int] = None, 

1425 micro: Nullable[int] = None, 

1426 build: Nullable[int] = None, 

1427 flags: Flags = Flags.Clean, 

1428 prefix: Nullable[str] = None, 

1429 postfix: Nullable[str] = None 

1430 ) -> None: 

1431 """ 

1432 Initializes a calendar version number representation. 

1433 

1434 :param major: Major number part of the version number. 

1435 :param minor: Minor number part of the version number. 

1436 :param micro: Micro (patch) number part of the version number. 

1437 :param build: Build number part of the version number. 

1438 :param flags: The version number's flags. 

1439 :param prefix: The version number's prefix. 

1440 :param postfix: The version number's postfix. 

1441 :raises TypeError: If parameter 'major' is not of type int. 

1442 :raises ValueError: If parameter 'major' is a negative number. 

1443 :raises TypeError: If parameter 'minor' is not of type int. 

1444 :raises ValueError: If parameter 'minor' is a negative number. 

1445 :raises TypeError: If parameter 'micro' is not of type int. 

1446 :raises ValueError: If parameter 'micro' is a negative number. 

1447 :raises TypeError: If parameter 'build' is not of type int. 

1448 :raises ValueError: If parameter 'build' is a negative number. 

1449 :raises TypeError: If parameter 'prefix' is not of type str. 

1450 :raises TypeError: If parameter 'postfix' is not of type str. 

1451 """ 

1452 super().__init__(major, minor, micro, build=build, postfix=postfix, prefix=prefix, flags=flags) 

1453 

1454 @classmethod 

1455 def Parse(cls, versionString: Nullable[str], validator: Nullable[Callable[["CalendarVersion"], bool]] = None) -> "CalendarVersion": 

1456 """ 

1457 Parse a version string and return a :class:`CalendarVersion` instance. 

1458 

1459 :param versionString: The version string to parse. 

1460 :returns: An object representing a calendar version. 

1461 :raises TypeError: If parameter ``other`` is not a string. 

1462 :raises ValueError: If parameter ``other`` is None. 

1463 :raises ValueError: If parameter ``other`` is empty. 

1464 """ 

1465 parts = Parts.Unknown 

1466 

1467 if versionString is None: 

1468 raise ValueError("Parameter 'versionString' is None.") 

1469 elif not isinstance(versionString, str): 

1470 ex = TypeError(f"Parameter 'versionString' is not of type 'str'.") 

1471 ex.add_note(f"Got type '{getFullyQualifiedName(versionString)}'.") 

1472 raise ex 

1473 elif versionString == "": 

1474 raise ValueError("Parameter 'versionString' is empty.") 

1475 

1476 split = versionString.split(".") 

1477 length = len(split) 

1478 major = int(split[0]) 

1479 minor = 0 

1480 parts |= Parts.Major 

1481 

1482 if length >= 2: 

1483 minor = int(split[1]) 

1484 parts |= Parts.Minor 

1485 

1486 flags = Flags.Clean 

1487 

1488 version = cls(major, minor, flags=flags) 

1489 if validator is not None and not validator(version): 

1490 raise ValueError(f"Failed to validate version string '{versionString}'.") # pragma: no cover 

1491 

1492 return version 

1493 

1494 @property 

1495 def Year(self) -> int: 

1496 """ 

1497 Read-only property to access the year part. 

1498 

1499 :return: The year part. 

1500 """ 

1501 return self._major 

1502 

1503 def _equal(self, left: "CalendarVersion", right: "CalendarVersion") -> Nullable[bool]: 

1504 """ 

1505 Private helper method to compute the equality of two :class:`CalendarVersion` instances. 

1506 

1507 :param left: Left parameter. 

1508 :param right: Right parameter. 

1509 :returns: ``True``, if ``left`` is equal to ``right``, otherwise it's ``False``. 

1510 """ 

1511 return (left._major == right._major) and (left._minor == right._minor) and (left._micro == right._micro) 

1512 

1513 def _compare(self, left: "CalendarVersion", right: "CalendarVersion") -> Nullable[bool]: 

1514 """ 

1515 Private helper method to compute the comparison of two :class:`CalendarVersion` instances. 

1516 

1517 :param left: Left parameter. 

1518 :param right: Right parameter. 

1519 :returns: ``True``, if ``left`` is smaller than ``right``. |br| 

1520 False if ``left`` is greater than ``right``. |br| 

1521 Otherwise it's None (both parameters are equal). 

1522 """ 

1523 if left._major < right._major: 

1524 return True 

1525 elif left._major > right._major: 

1526 return False 

1527 

1528 if left._minor < right._minor: 

1529 return True 

1530 elif left._minor > right._minor: 

1531 return False 

1532 

1533 if left._micro < right._micro: 1533 ↛ 1534line 1533 didn't jump to line 1534 because the condition on line 1533 was never true

1534 return True 

1535 elif left._micro > right._micro: 1535 ↛ 1536line 1535 didn't jump to line 1536 because the condition on line 1535 was never true

1536 return False 

1537 

1538 return None 

1539 

1540 def __eq__(self, other: Union["CalendarVersion", str, int, None]) -> bool: 

1541 """ 

1542 Compare two version numbers for equality. 

1543 

1544 The second operand should be an instance of :class:`CalendarVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1545 In case of ``str``, it's tried to parse the string as a calendar version number. In case of ``int``, a single major 

1546 number is assumed (all other parts are zero). 

1547 

1548 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1549 number. 

1550 

1551 :param other: Parameter to compare against. 

1552 :returns: ``True``, if both version numbers are equal. 

1553 :raises ValueError: If parameter ``other`` is None. 

1554 :raises TypeError: If parameter ``other`` is not of type :class:`CalendarVersion`, :class:`str` or :class:`ìnt`. 

1555 """ 

1556 return super().__eq__(other) 

1557 

1558 def __ne__(self, other: Union["CalendarVersion", str, int, None]) -> bool: 

1559 """ 

1560 Compare two version numbers for inequality. 

1561 

1562 The second operand should be an instance of :class:`CalendarVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1563 In case of ``str``, it's tried to parse the string as a calendar version number. In case of ``int``, a single major 

1564 number is assumed (all other parts are zero). 

1565 

1566 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1567 number. 

1568 

1569 :param other: Parameter to compare against. 

1570 :returns: ``True``, if both version numbers are not equal. 

1571 :raises ValueError: If parameter ``other`` is None. 

1572 :raises TypeError: If parameter ``other`` is not of type :class:`CalendarVersion`, :class:`str` or :class:`ìnt`. 

1573 """ 

1574 return super().__ne__(other) 

1575 

1576 def __lt__(self, other: Union["CalendarVersion", str, int, None]) -> bool: 

1577 """ 

1578 Compare two version numbers if the version is less than the second operand. 

1579 

1580 The second operand should be an instance of :class:`CalendarVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1581 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1582 number is assumed (all other parts are zero). 

1583 

1584 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1585 number. 

1586 

1587 :param other: Parameter to compare against. 

1588 :returns: ``True``, if version is less than the second operand. 

1589 :raises ValueError: If parameter ``other`` is None. 

1590 :raises TypeError: If parameter ``other`` is not of type :class:`CalendarVersion`, :class:`str` or :class:`ìnt`. 

1591 """ 

1592 return super().__lt__(other) 

1593 

1594 def __le__(self, other: Union["CalendarVersion", str, int, None]) -> bool: 

1595 """ 

1596 Compare two version numbers if the version is less than or equal the second operand. 

1597 

1598 The second operand should be an instance of :class:`CalendarVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1599 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1600 number is assumed (all other parts are zero). 

1601 

1602 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1603 number. 

1604 

1605 :param other: Parameter to compare against. 

1606 :returns: ``True``, if version is less than or equal the second operand. 

1607 :raises ValueError: If parameter ``other`` is None. 

1608 :raises TypeError: If parameter ``other`` is not of type :class:`CalendarVersion`, :class:`str` or :class:`ìnt`. 

1609 """ 

1610 return super().__le__(other) 

1611 

1612 def __gt__(self, other: Union["CalendarVersion", str, int, None]) -> bool: 

1613 """ 

1614 Compare two version numbers if the version is greater than the second operand. 

1615 

1616 The second operand should be an instance of :class:`CalendarVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1617 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1618 number is assumed (all other parts are zero). 

1619 

1620 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1621 number. 

1622 

1623 :param other: Parameter to compare against. 

1624 :returns: ``True``, if version is greater than the second operand. 

1625 :raises ValueError: If parameter ``other`` is None. 

1626 :raises TypeError: If parameter ``other`` is not of type :class:`CalendarVersion`, :class:`str` or :class:`ìnt`. 

1627 """ 

1628 return super().__gt__(other) 

1629 

1630 def __ge__(self, other: Union["CalendarVersion", str, int, None]) -> bool: 

1631 """ 

1632 Compare two version numbers if the version is greater than or equal the second operand. 

1633 

1634 The second operand should be an instance of :class:`CalendarVersion`, but ``str`` and ``int`` are accepted, too. |br| 

1635 In case of ``str``, it's tried to parse the string as a semantic version number. In case of ``int``, a single major 

1636 number is assumed (all other parts are zero). 

1637 

1638 ``float`` is not supported, due to rounding issues when converting the fractional part of the float to a minor 

1639 number. 

1640 

1641 :param other: Parameter to compare against. 

1642 :returns: ``True``, if version is greater than or equal the second operand. 

1643 :raises ValueError: If parameter ``other`` is None. 

1644 :raises TypeError: If parameter ``other`` is not of type :class:`CalendarVersion`, :class:`str` or :class:`ìnt`. 

1645 """ 

1646 return super().__ge__(other) 

1647 

1648 def __hash__(self) -> int: 

1649 return super().__hash__() 

1650 

1651 def __format__(self, formatSpec: str) -> str: 

1652 """ 

1653 Return a string representation of this version number according to the format specification. 

1654 

1655 .. topic:: Format Specifiers 

1656 

1657 * ``%M`` - major number (year) 

1658 * ``%m`` - minor number (month/week) 

1659 

1660 :param formatSpec: The format specification. 

1661 :return: Formatted version number. 

1662 """ 

1663 if formatSpec == "": 

1664 return self.__str__() 

1665 

1666 result = formatSpec 

1667 # result = result.replace("%P", str(self._prefix)) 

1668 result = result.replace("%M", str(self._major)) 

1669 result = result.replace("%m", str(self._minor)) 

1670 # result = result.replace("%p", str(self._pre)) 

1671 

1672 return result.replace("%%", "%") 

1673 

1674 def __repr__(self) -> str: 

1675 """ 

1676 Return a string representation of this version number without prefix ``v``. 

1677 

1678 :returns: Raw version number representation without a prefix. 

1679 """ 

1680 return f"{self._major}.{self._minor}" 

1681 

1682 def __str__(self) -> str: 

1683 """ 

1684 Return a string representation of this version number with prefix ``v``. 

1685 

1686 :returns: Version number representation including a prefix. 

1687 """ 

1688 result = f"{self._major}" 

1689 result += f".{self._minor}" if Parts.Minor in self._parts else "" 

1690 

1691 return result 

1692 

1693 

1694@export 

1695class YearMonthVersion(CalendarVersion): 

1696 """Representation of a calendar version number made of year and month like ``2021.10``.""" 

1697 

1698 def __init__( 

1699 self, 

1700 year: int, 

1701 month: Nullable[int] = None, 

1702 build: Nullable[int] = None, 

1703 flags: Flags = Flags.Clean, 

1704 prefix: Nullable[str] = None, 

1705 postfix: Nullable[str] = None 

1706 ) -> None: 

1707 """ 

1708 Initializes a year-month version number representation. 

1709 

1710 :param year: Year part of the version number. 

1711 :param month: Month part of the version number. 

1712 :param build: Build number part of the version number. 

1713 :param flags: The version number's flags. 

1714 :param prefix: The version number's prefix. 

1715 :param postfix: The version number's postfix. 

1716 :raises TypeError: If parameter 'major' is not of type int. 

1717 :raises ValueError: If parameter 'major' is a negative number. 

1718 :raises TypeError: If parameter 'minor' is not of type int. 

1719 :raises ValueError: If parameter 'minor' is a negative number. 

1720 :raises TypeError: If parameter 'micro' is not of type int. 

1721 :raises ValueError: If parameter 'micro' is a negative number. 

1722 :raises TypeError: If parameter 'build' is not of type int. 

1723 :raises ValueError: If parameter 'build' is a negative number. 

1724 :raises TypeError: If parameter 'prefix' is not of type str. 

1725 :raises TypeError: If parameter 'postfix' is not of type str. 

1726 """ 

1727 super().__init__(year, month, 0, build, flags, prefix, postfix) 

1728 

1729 @property 

1730 def Month(self) -> int: 

1731 """ 

1732 Read-only property to access the month part. 

1733 

1734 :return: The month part. 

1735 """ 

1736 return self._minor 

1737 

1738 def __hash__(self) -> int: 

1739 return super().__hash__() 

1740 

1741 

1742@export 

1743class YearWeekVersion(CalendarVersion): 

1744 """Representation of a calendar version number made of year and week like ``2021.47``.""" 

1745 

1746 def __init__( 

1747 self, 

1748 year: int, 

1749 week: Nullable[int] = None, 

1750 build: Nullable[int] = None, 

1751 flags: Flags = Flags.Clean, 

1752 prefix: Nullable[str] = None, 

1753 postfix: Nullable[str] = None 

1754 ) -> None: 

1755 """ 

1756 Initializes a year-week version number representation. 

1757 

1758 :param year: Year part of the version number. 

1759 :param week: Week part of the version number. 

1760 :param build: Build number part of the version number. 

1761 :param flags: The version number's flags. 

1762 :param prefix: The version number's prefix. 

1763 :param postfix: The version number's postfix. 

1764 :raises TypeError: If parameter 'major' is not of type int. 

1765 :raises ValueError: If parameter 'major' is a negative number. 

1766 :raises TypeError: If parameter 'minor' is not of type int. 

1767 :raises ValueError: If parameter 'minor' is a negative number. 

1768 :raises TypeError: If parameter 'micro' is not of type int. 

1769 :raises ValueError: If parameter 'micro' is a negative number. 

1770 :raises TypeError: If parameter 'build' is not of type int. 

1771 :raises ValueError: If parameter 'build' is a negative number. 

1772 :raises TypeError: If parameter 'prefix' is not of type str. 

1773 :raises TypeError: If parameter 'postfix' is not of type str. 

1774 """ 

1775 super().__init__(year, week, 0, build, flags, prefix, postfix) 

1776 

1777 @property 

1778 def Week(self) -> int: 

1779 """ 

1780 Read-only property to access the week part. 

1781 

1782 :return: The week part. 

1783 """ 

1784 return self._minor 

1785 

1786 def __hash__(self) -> int: 

1787 return super().__hash__() 

1788 

1789 

1790@export 

1791class YearReleaseVersion(CalendarVersion): 

1792 """Representation of a calendar version number made of year and release per year like ``2021.2``.""" 

1793 

1794 def __init__( 

1795 self, 

1796 year: int, 

1797 release: Nullable[int] = None, 

1798 build: Nullable[int] = None, 

1799 flags: Flags = Flags.Clean, 

1800 prefix: Nullable[str] = None, 

1801 postfix: Nullable[str] = None 

1802 ) -> None: 

1803 """ 

1804 Initializes a year-release version number representation. 

1805 

1806 :param year: Year part of the version number. 

1807 :param release: Release number of the version number. 

1808 :param build: Build number part of the version number. 

1809 :param flags: The version number's flags. 

1810 :param prefix: The version number's prefix. 

1811 :param postfix: The version number's postfix. 

1812 :raises TypeError: If parameter 'major' is not of type int. 

1813 :raises ValueError: If parameter 'major' is a negative number. 

1814 :raises TypeError: If parameter 'minor' is not of type int. 

1815 :raises ValueError: If parameter 'minor' is a negative number. 

1816 :raises TypeError: If parameter 'micro' is not of type int. 

1817 :raises ValueError: If parameter 'micro' is a negative number. 

1818 :raises TypeError: If parameter 'build' is not of type int. 

1819 :raises ValueError: If parameter 'build' is a negative number. 

1820 :raises TypeError: If parameter 'prefix' is not of type str. 

1821 :raises TypeError: If parameter 'postfix' is not of type str. 

1822 """ 

1823 super().__init__(year, release, 0, build, flags, prefix, postfix) 

1824 

1825 @property 

1826 def Release(self) -> int: 

1827 """ 

1828 Read-only property to access the release number. 

1829 

1830 :return: The release number. 

1831 """ 

1832 return self._minor 

1833 

1834 def __hash__(self) -> int: 

1835 return super().__hash__() 

1836 

1837 

1838@export 

1839class YearMonthDayVersion(CalendarVersion): 

1840 """Representation of a calendar version number made of year, month and day like ``2021.10.15``.""" 

1841 

1842 def __init__( 

1843 self, 

1844 year: int, 

1845 month: Nullable[int] = None, 

1846 day: Nullable[int] = None, 

1847 build: Nullable[int] = None, 

1848 flags: Flags = Flags.Clean, 

1849 prefix: Nullable[str] = None, 

1850 postfix: Nullable[str] = None 

1851 ) -> None: 

1852 """ 

1853 Initializes a year-month-day version number representation. 

1854 

1855 :param year: Year part of the version number. 

1856 :param month: Month part of the version number. 

1857 :param day: Day part of the version number. 

1858 :param build: Build number part of the version number. 

1859 :param flags: The version number's flags. 

1860 :param prefix: The version number's prefix. 

1861 :param postfix: The version number's postfix. 

1862 :raises TypeError: If parameter 'major' is not of type int. 

1863 :raises ValueError: If parameter 'major' is a negative number. 

1864 :raises TypeError: If parameter 'minor' is not of type int. 

1865 :raises ValueError: If parameter 'minor' is a negative number. 

1866 :raises TypeError: If parameter 'micro' is not of type int. 

1867 :raises ValueError: If parameter 'micro' is a negative number. 

1868 :raises TypeError: If parameter 'build' is not of type int. 

1869 :raises ValueError: If parameter 'build' is a negative number. 

1870 :raises TypeError: If parameter 'prefix' is not of type str. 

1871 :raises TypeError: If parameter 'postfix' is not of type str. 

1872 """ 

1873 super().__init__(year, month, day, build, flags, prefix, postfix) 

1874 

1875 @property 

1876 def Month(self) -> int: 

1877 """ 

1878 Read-only property to access the month part. 

1879 

1880 :return: The month part. 

1881 """ 

1882 return self._minor 

1883 

1884 @property 

1885 def Day(self) -> int: 

1886 """ 

1887 Read-only property to access the day part. 

1888 

1889 :return: The day part. 

1890 """ 

1891 return self._micro 

1892 

1893 def __hash__(self) -> int: 

1894 return super().__hash__() 

1895 

1896 

1897V = TypeVar("V", bound=Version) 

1898 

1899@export 

1900class RangeBoundHandling(Flag): 

1901 """ 

1902 A flag defining how to handle bounds in a range. 

1903 

1904 If a bound is inclusive, the bound's value is within the range. If a bound is exclusive, the bound's value is the 

1905 first value outside the range. Inclusive and exclusive behavior can be mixed for lower and upper bounds. 

1906 """ 

1907 BothBoundsInclusive = 0 #: Lower and upper bound are inclusive. 

1908 LowerBoundInclusive = 0 #: Lower bound is inclusive. 

1909 UpperBoundInclusive = 0 #: Upper bound is inclusive. 

1910 LowerBoundExclusive = 1 #: Lower bound is exclusive. 

1911 UpperBoundExclusive = 2 #: Upper bound is exclusive. 

1912 BothBoundsExclusive = 3 #: Lower and upper bound are exclusive. 

1913 

1914 

1915@export 

1916class VersionRange(Generic[V], metaclass=ExtendedType, slots=True): 

1917 """ 

1918 Representation of a version range described by a lower bound and upper bound version. 

1919 

1920 This version range works with :class:`SemanticVersion` and :class:`CalendarVersion` and its derived classes. 

1921 """ 

1922 _lowerBound: V 

1923 _upperBound: V 

1924 _boundHandling: RangeBoundHandling 

1925 

1926 def __init__(self, lowerBound: V, upperBound: V, boundHandling: RangeBoundHandling = RangeBoundHandling.BothBoundsInclusive) -> None: 

1927 """ 

1928 Initializes a version range described by a lower and upper bound. 

1929 

1930 :param lowerBound: lowest version (inclusive). 

1931 :param upperBound: hightest version (inclusive). 

1932 :raises TypeError: If parameter ``lowerBound`` is not of type :class:`Version`. 

1933 :raises TypeError: If parameter ``upperBound`` is not of type :class:`Version`. 

1934 :raises TypeError: If parameter ``lowerBound`` and ``upperBound`` are unrelated types. 

1935 :raises ValueError: If parameter ``lowerBound`` isn't less than or equal to ``upperBound``. 

1936 """ 

1937 if not isinstance(lowerBound, Version): 

1938 ex = TypeError(f"Parameter 'lowerBound' is not of type 'Version'.") 

1939 ex.add_note(f"Got type '{getFullyQualifiedName(lowerBound)}'.") 

1940 raise ex 

1941 

1942 if not isinstance(upperBound, Version): 

1943 ex = TypeError(f"Parameter 'upperBound' is not of type 'Version'.") 

1944 ex.add_note(f"Got type '{getFullyQualifiedName(upperBound)}'.") 

1945 raise ex 

1946 

1947 if not ((lBC := lowerBound.__class__) is (uBC := upperBound.__class__) or issubclass(lBC, uBC) or issubclass(uBC, lBC)): 

1948 ex = TypeError(f"Parameters 'lowerBound' and 'upperBound' are not compatible with each other.") 

1949 ex.add_note(f"Got type '{getFullyQualifiedName(lowerBound)}' for lowerBound and type '{getFullyQualifiedName(upperBound)}' for upperBound.") 

1950 raise ex 

1951 

1952 if not (lowerBound <= upperBound): 

1953 ex = ValueError(f"Parameter 'lowerBound' isn't less than parameter 'upperBound'.") 

1954 ex.add_note(f"Got '{lowerBound}' for lowerBound and '{upperBound}' for upperBound.") 

1955 raise ex 

1956 

1957 self._lowerBound = lowerBound 

1958 self._upperBound = upperBound 

1959 self._boundHandling = boundHandling 

1960 

1961 @property 

1962 def LowerBound(self) -> V: 

1963 """ 

1964 Property to access the range's lower bound. 

1965 

1966 :return: Lower bound of the version range. 

1967 """ 

1968 return self._lowerBound 

1969 

1970 @LowerBound.setter 

1971 def LowerBound(self, value: V) -> None: 

1972 if not isinstance(value, Version): 

1973 ex = TypeError(f"Parameter 'value' is not of type 'Version'.") 

1974 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

1975 raise ex 

1976 

1977 self._lowerBound = value 

1978 

1979 @readonly 

1980 def UpperBound(self) -> V: 

1981 """ 

1982 Property to access the range's upper bound. 

1983 

1984 :return: Upper bound of the version range. 

1985 """ 

1986 return self._upperBound 

1987 

1988 @UpperBound.setter 

1989 def UpperBound(self, value: V) -> None: 

1990 if not isinstance(value, Version): 

1991 ex = TypeError(f"Parameter 'value' is not of type 'Version'.") 

1992 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

1993 raise ex 

1994 

1995 self._upperBound = value 

1996 

1997 @readonly 

1998 def BoundHandling(self) -> RangeBoundHandling: 

1999 """ 

2000 Property to access the range's bound handling strategy. 

2001 

2002 :return: The range's bound handling strategy. 

2003 """ 

2004 return self._boundHandling 

2005 

2006 @BoundHandling.setter 

2007 def BoundHandling(self, value: RangeBoundHandling) -> None: 

2008 if not isinstance(value, RangeBoundHandling): 

2009 ex = TypeError(f"Parameter 'value' is not of type 'RangeBoundHandling'.") 

2010 ex.add_note(f"Got type '{getFullyQualifiedName(value)}'.") 

2011 raise ex 

2012 

2013 self._boundHandling = value 

2014 

2015 def __and__(self, other: Any) -> "VersionRange[T]": 

2016 """ 

2017 Compute the intersection of two version ranges. 

2018 

2019 :param other: Second version range to intersect with. 

2020 :returns: Intersected version range. 

2021 :raises TypeError: If parameter 'other' is not of type :class:`VersionRange`. 

2022 :raises ValueError: If intersection is empty. 

2023 """ 

2024 if not isinstance(other, VersionRange): 2024 ↛ 2025line 2024 didn't jump to line 2025 because the condition on line 2024 was never true

2025 ex = TypeError(f"Parameter 'other' is not of type 'VersionRange'.") 

2026 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2027 raise ex 

2028 

2029 if not (isinstance(other._lowerBound, self._lowerBound.__class__) and isinstance(self._lowerBound, other._lowerBound.__class__)): 2029 ↛ 2030line 2029 didn't jump to line 2030 because the condition on line 2029 was never true

2030 ex = TypeError(f"Parameter 'other's LowerBound and this range's 'LowerBound' are not compatible with each other.") 

2031 ex.add_note( 

2032 f"Got type '{getFullyQualifiedName(other._lowerBound)}' for other.LowerBound and type '{getFullyQualifiedName(self._lowerBound)}' for self.LowerBound.") 

2033 raise ex 

2034 

2035 if other._lowerBound < self._lowerBound: 

2036 lBound = self._lowerBound 

2037 elif other._lowerBound in self: 2037 ↛ 2040line 2037 didn't jump to line 2040 because the condition on line 2037 was always true

2038 lBound = other._lowerBound 

2039 else: 

2040 raise ValueError() 

2041 

2042 if other._upperBound > self._upperBound: 

2043 uBound = self._upperBound 

2044 elif other._upperBound in self: 2044 ↛ 2047line 2044 didn't jump to line 2047 because the condition on line 2044 was always true

2045 uBound = other._upperBound 

2046 else: 

2047 raise ValueError() 

2048 

2049 return self.__class__(lBound, uBound) 

2050 

2051 def __lt__(self, other: Any) -> bool: 

2052 """ 

2053 Compare a version range and a version numbers if the version range is less than the second operand (version). 

2054 

2055 :param other: Operand to compare against. 

2056 :returns: ``True``, if version range is less than the second operand (version). 

2057 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2058 """ 

2059 # TODO: support VersionRange < VersionRange too 

2060 # TODO: support str, int, ... like Version ? 

2061 if not isinstance(other, Version): 

2062 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2063 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2064 raise ex 

2065 

2066 if not (isinstance(other, self._lowerBound.__class__) and isinstance(self._lowerBound, other.__class__)): 2066 ↛ 2067line 2066 didn't jump to line 2067 because the condition on line 2066 was never true

2067 ex = TypeError(f"Parameter 'other' is not compatible with version range.") 

2068 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2069 raise ex 

2070 

2071 return self._upperBound < other 

2072 

2073 def __le__(self, other: Any) -> bool: 

2074 """ 

2075 Compare a version range and a version numbers if the version range is less than or equal the second operand (version). 

2076 

2077 :param other: Operand to compare against. 

2078 :returns: ``True``, if version range is less than or equal the second operand (version). 

2079 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2080 """ 

2081 # TODO: support VersionRange < VersionRange too 

2082 # TODO: support str, int, ... like Version ? 

2083 if not isinstance(other, Version): 2083 ↛ 2084line 2083 didn't jump to line 2084 because the condition on line 2083 was never true

2084 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2085 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2086 raise ex 

2087 

2088 if not (isinstance(other, self._lowerBound.__class__) and isinstance(self._lowerBound, other.__class__)): 2088 ↛ 2089line 2088 didn't jump to line 2089 because the condition on line 2088 was never true

2089 ex = TypeError(f"Parameter 'other' is not compatible with version range.") 

2090 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2091 raise ex 

2092 

2093 if RangeBoundHandling.UpperBoundExclusive in self._boundHandling: 

2094 return self._upperBound < other 

2095 else: 

2096 return self._upperBound <= other 

2097 

2098 def __gt__(self, other: Any) -> bool: 

2099 """ 

2100 Compare a version range and a version numbers if the version range is greater than the second operand (version). 

2101 

2102 :param other: Operand to compare against. 

2103 :returns: ``True``, if version range is greater than the second operand (version). 

2104 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2105 """ 

2106 # TODO: support VersionRange < VersionRange too 

2107 # TODO: support str, int, ... like Version ? 

2108 if not isinstance(other, Version): 2108 ↛ 2109line 2108 didn't jump to line 2109 because the condition on line 2108 was never true

2109 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2110 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2111 raise ex 

2112 

2113 if not (isinstance(other, self._upperBound.__class__) and isinstance(self._upperBound, other.__class__)): 2113 ↛ 2114line 2113 didn't jump to line 2114 because the condition on line 2113 was never true

2114 ex = TypeError(f"Parameter 'other' is not compatible with version range.") 

2115 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2116 raise ex 

2117 

2118 return self._lowerBound > other 

2119 

2120 def __ge__(self, other: Any) -> bool: 

2121 """ 

2122 Compare a version range and a version numbers if the version range is greater than or equal the second operand (version). 

2123 

2124 :param other: Operand to compare against. 

2125 :returns: ``True``, if version range is greater than or equal the second operand (version). 

2126 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2127 """ 

2128 # TODO: support VersionRange < VersionRange too 

2129 # TODO: support str, int, ... like Version ? 

2130 if not isinstance(other, Version): 2130 ↛ 2131line 2130 didn't jump to line 2131 because the condition on line 2130 was never true

2131 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2132 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2133 raise ex 

2134 

2135 if not (isinstance(other, self._upperBound.__class__) and isinstance(self._upperBound, other.__class__)): 2135 ↛ 2136line 2135 didn't jump to line 2136 because the condition on line 2135 was never true

2136 ex = TypeError(f"Parameter 'other' is not compatible with version range.") 

2137 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2138 raise ex 

2139 

2140 if RangeBoundHandling.LowerBoundExclusive in self._boundHandling: 2140 ↛ 2141line 2140 didn't jump to line 2141 because the condition on line 2140 was never true

2141 return self._lowerBound > other 

2142 else: 

2143 return self._lowerBound >= other 

2144 

2145 def __contains__(self, version: Version) -> bool: 

2146 """ 

2147 Check if the version is in the version range. 

2148 

2149 :param version: Version to check. 

2150 :returns: ``True``, if version is in range. 

2151 :raises TypeError: If parameter ``version`` is not of type :class:`Version`. 

2152 """ 

2153 if not isinstance(version, Version): 2153 ↛ 2154line 2153 didn't jump to line 2154 because the condition on line 2153 was never true

2154 ex = TypeError(f"Parameter 'item' is not of type 'Version'.") 

2155 ex.add_note(f"Got type '{getFullyQualifiedName(version)}'.") 

2156 raise ex 

2157 

2158 if self._boundHandling is RangeBoundHandling.BothBoundsInclusive: 2158 ↛ 2160line 2158 didn't jump to line 2160 because the condition on line 2158 was always true

2159 return self._lowerBound <= version <= self._upperBound 

2160 elif self._boundHandling is (RangeBoundHandling.LowerBoundInclusive | RangeBoundHandling.UpperBoundExclusive): 

2161 return self._lowerBound <= version < self._upperBound 

2162 elif self._boundHandling is (RangeBoundHandling.LowerBoundExclusive | RangeBoundHandling.UpperBoundInclusive): 

2163 return self._lowerBound < version <= self._upperBound 

2164 else: 

2165 return self._lowerBound < version < self._upperBound 

2166 

2167 

2168@export 

2169class VersionSet(Generic[V], metaclass=ExtendedType, slots=True): 

2170 """ 

2171 Representation of an ordered set of versions. 

2172 

2173 This version set works with :class:`SemanticVersion` and :class:`CalendarVersion` and its derived classes. 

2174 """ 

2175 _items: List[V] #: An ordered list of set members. 

2176 

2177 def __init__(self, versions: Union[Version, Iterable[V]]): 

2178 """ 

2179 Initializes a version set either by a single version or an iterable of versions. 

2180 

2181 :param versions: A single version or an iterable of versions. 

2182 :raises ValueError: If parameter ``versions`` is None`. 

2183 :raises TypeError: In case of a single version, if parameter ``version`` is not of type :class:`Version`. 

2184 :raises TypeError: In case of an iterable, if parameter ``versions`` containes elements, which are not of type :class:`Version`. 

2185 :raises TypeError: If parameter ``versions`` is neither a single version nor an iterable thereof. 

2186 """ 

2187 if versions is None: 

2188 raise ValueError(f"Parameter 'versions' is None.") 

2189 

2190 if isinstance(versions, Version): 

2191 self._items = [versions] 

2192 elif isinstance(versions, abc_Iterable): 2192 ↛ 2210line 2192 didn't jump to line 2210 because the condition on line 2192 was always true

2193 iterator = iter(versions) 

2194 try: 

2195 firstVersion = next(iterator) 

2196 except StopIteration: 

2197 self._items = [] 

2198 return 

2199 

2200 if not isinstance(firstVersion, Version): 2200 ↛ 2201line 2200 didn't jump to line 2201 because the condition on line 2200 was never true

2201 raise TypeError(f"First element in parameter 'versions' is not of type Version.") 

2202 

2203 baseType = firstVersion.__class__ 

2204 for version in iterator: 

2205 if not isinstance(version, baseType): 

2206 raise TypeError(f"Element from parameter 'versions' is not of type {baseType.__name__}") 

2207 

2208 self._items = list(sorted(versions)) 

2209 else: 

2210 raise TypeError(f"Parameter 'versions' is not an Iterable.") 

2211 

2212 def __and__(self, other: "VersionSet[V]") -> "VersionSet[T]": 

2213 """ 

2214 Compute intersection of two version sets. 

2215 

2216 :param other: Second set of versions. 

2217 :returns: Intersection of two version sets. 

2218 """ 

2219 selfIterator = self.__iter__() 

2220 otherIterator = other.__iter__() 

2221 

2222 result = [] 

2223 try: 

2224 selfValue = next(selfIterator) 

2225 otherValue = next(otherIterator) 

2226 

2227 while True: 

2228 if selfValue < otherValue: 

2229 selfValue = next(selfIterator) 

2230 elif otherValue < selfValue: 

2231 otherValue = next(otherIterator) 

2232 else: 

2233 result.append(selfValue) 

2234 selfValue = next(selfIterator) 

2235 otherValue = next(otherIterator) 

2236 

2237 except StopIteration: 

2238 pass 

2239 

2240 return VersionSet(result) 

2241 

2242 def __or__(self, other: "VersionSet[V]") -> "VersionSet[T]": 

2243 """ 

2244 Compute union of two version sets. 

2245 

2246 :param other: Second set of versions. 

2247 :returns: Union of two version sets. 

2248 """ 

2249 selfIterator = self.__iter__() 

2250 otherIterator = other.__iter__() 

2251 

2252 result = [] 

2253 try: 

2254 selfValue = next(selfIterator) 

2255 except StopIteration: 

2256 for otherValue in otherIterator: 

2257 result.append(otherValue) 

2258 

2259 try: 

2260 otherValue = next(otherIterator) 

2261 except StopIteration: 

2262 for selfValue in selfIterator: 

2263 result.append(selfValue) 

2264 

2265 while True: 

2266 if selfValue < otherValue: 

2267 result.append(selfValue) 

2268 try: 

2269 selfValue = next(selfIterator) 

2270 except StopIteration: 

2271 result.append(otherValue) 

2272 for otherValue in otherIterator: 2272 ↛ 2273line 2272 didn't jump to line 2273 because the loop on line 2272 never started

2273 result.append(otherValue) 

2274 

2275 break 

2276 elif otherValue < selfValue: 

2277 result.append(otherValue) 

2278 try: 

2279 otherValue = next(otherIterator) 

2280 except StopIteration: 

2281 result.append(selfValue) 

2282 for selfValue in selfIterator: 

2283 result.append(selfValue) 

2284 

2285 break 

2286 else: 

2287 result.append(selfValue) 

2288 try: 

2289 selfValue = next(selfIterator) 

2290 except StopIteration: 

2291 for otherValue in otherIterator: 2291 ↛ 2292line 2291 didn't jump to line 2292 because the loop on line 2291 never started

2292 result.append(otherValue) 

2293 

2294 break 

2295 

2296 try: 

2297 otherValue = next(otherIterator) 

2298 except StopIteration: 

2299 for selfValue in selfIterator: 

2300 result.append(selfValue) 

2301 

2302 break 

2303 

2304 return VersionSet(result) 

2305 

2306 def __lt__(self, other: Any) -> bool: 

2307 """ 

2308 Compare a version set and a version numbers if the version set is less than the second operand (version). 

2309 

2310 :param other: Operand to compare against. 

2311 :returns: ``True``, if version set is less than the second operand (version). 

2312 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2313 """ 

2314 # TODO: support VersionRange < VersionRange too 

2315 # TODO: support str, int, ... like Version ? 

2316 if not isinstance(other, Version): 

2317 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2318 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2319 raise ex 

2320 

2321 return self._items[-1] < other 

2322 

2323 def __le__(self, other: Any) -> bool: 

2324 """ 

2325 Compare a version set and a version numbers if the version set is less than or equal the second operand (version). 

2326 

2327 :param other: Operand to compare against. 

2328 :returns: ``True``, if version set is less than or equal the second operand (version). 

2329 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2330 """ 

2331 # TODO: support VersionRange < VersionRange too 

2332 # TODO: support str, int, ... like Version ? 

2333 if not isinstance(other, Version): 2333 ↛ 2334line 2333 didn't jump to line 2334 because the condition on line 2333 was never true

2334 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2335 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2336 raise ex 

2337 

2338 return self._items[-1] <= other 

2339 

2340 def __gt__(self, other: Any) -> bool: 

2341 """ 

2342 Compare a version set and a version numbers if the version set is greater than the second operand (version). 

2343 

2344 :param other: Operand to compare against. 

2345 :returns: ``True``, if version set is greater than the second operand (version). 

2346 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2347 """ 

2348 # TODO: support VersionRange < VersionRange too 

2349 # TODO: support str, int, ... like Version ? 

2350 if not isinstance(other, Version): 2350 ↛ 2351line 2350 didn't jump to line 2351 because the condition on line 2350 was never true

2351 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2352 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2353 raise ex 

2354 

2355 return self._items[0] > other 

2356 

2357 def __ge__(self, other: Any) -> bool: 

2358 """ 

2359 Compare a version set and a version numbers if the version set is greater than or equal the second operand (version). 

2360 

2361 :param other: Operand to compare against. 

2362 :returns: ``True``, if version set is greater than or equal the second operand (version). 

2363 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2364 """ 

2365 # TODO: support VersionRange < VersionRange too 

2366 # TODO: support str, int, ... like Version ? 

2367 if not isinstance(other, Version): 2367 ↛ 2368line 2367 didn't jump to line 2368 because the condition on line 2367 was never true

2368 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2369 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2370 raise ex 

2371 

2372 return self._items[0] >= other 

2373 

2374 def __contains__(self, version: V) -> bool: 

2375 """ 

2376 Checks if the version a member of the set. 

2377 

2378 :param version: The version to check. 

2379 :returns: ``True``, if the version is a member of the set. 

2380 """ 

2381 return version in self._items 

2382 

2383 def __len__(self) -> int: 

2384 """ 

2385 Returns the number of members in the set. 

2386 

2387 :returns: Number of set members. 

2388 """ 

2389 return len(self._items) 

2390 

2391 def __iter__(self) -> Iterator[V]: 

2392 """ 

2393 Returns an iterator to iterate all versions of this set from lowest to highest. 

2394 

2395 :returns: Iterator to iterate versions. 

2396 """ 

2397 return self._items.__iter__() 

2398 

2399 def __getitem__(self, index: int) -> V: 

2400 """ 

2401 Access to a version of a set by index. 

2402 

2403 :param index: The index of the version to access. 

2404 :returns: The indexed version. 

2405 

2406 .. hint:: Versions are ordered from lowest to highest version number. 

2407 """ 

2408 return self._items[index]