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

973 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-08 23:46 +0000

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

2# _____ _ _ __ __ _ _ # 

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

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

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

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

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

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

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

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

14# Copyright 2020-2026 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:: 

35 

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

37""" 

38from collections.abc import Iterable as abc_Iterable 

39from enum import Flag, Enum 

40from re import compile as re_compile 

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

42 

43try: 

44 from pyTooling.Decorators import export, readonly 

45 from pyTooling.MetaClasses import ExtendedType, abstractmethod, mustoverride 

46 from pyTooling.Exceptions import ToolingException 

47 from pyTooling.Common import getFullyQualifiedName 

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

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

50 

51 try: 

52 from Decorators import export, readonly 

53 from MetaClasses import ExtendedType, abstractmethod, mustoverride 

54 from Exceptions import ToolingException 

55 from Common import getFullyQualifiedName 

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

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

58 raise ex 

59 

60 

61@export 

62class Parts(Flag): 

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

64 Unknown = 0 #: Undocumented 

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

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

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

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

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

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

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

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

73 Level = 8 #: Release level is present. 

74 Dev = 16 #: Development part is present. 

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

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

77 Prefix = 128 #: Prefix is present. 

78 Postfix = 256 #: Postfix is present. 

79 Hash = 512 #: Hash is present. 

80# AHead = 256 

81 

82 

83@export 

84class ReleaseLevel(Enum): 

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

86 Final = 0 #: 

87 ReleaseCandidate = -10 #: 

88 Development = -20 #: 

89 Gamma = -30 #: 

90 Beta = -40 #: 

91 Alpha = -50 #: 

92 

93 def __eq__(self, other: Any) -> bool: 

94 """ 

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

96 

97 :param other: Operand to compare against. 

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

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

100 """ 

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

102 other = ReleaseLevel(other) 

103 

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

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

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

107 raise ex 

108 

109 return self is other 

110 

111 def __ne__(self, other: Any) -> bool: 

112 """ 

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

114 

115 :param other: Operand to compare against. 

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

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

118 """ 

119 if isinstance(other, str): 

120 other = ReleaseLevel(other) 

121 

122 if not isinstance(other, ReleaseLevel): 

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

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

125 raise ex 

126 

127 return self is not other 

128 

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

130 """ 

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

132 

133 :param other: Operand to compare against. 

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

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

136 """ 

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

138 other = ReleaseLevel(other) 

139 

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

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

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

143 raise ex 

144 

145 return self.value < other.value 

146 

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

148 """ 

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

150 

151 :param other: Operand to compare against. 

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

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

154 """ 

155 if isinstance(other, str): 

156 other = ReleaseLevel(other) 

157 

158 if not isinstance(other, ReleaseLevel): 

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

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

161 raise ex 

162 

163 return self.value <= other.value 

164 

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

166 """ 

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

168 

169 :param other: Operand to compare against. 

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

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

172 """ 

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

174 other = ReleaseLevel(other) 

175 

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

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

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

179 raise ex 

180 

181 return self.value > other.value 

182 

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

184 """ 

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

186 

187 :param other: Operand to compare against. 

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

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

190 """ 

191 if isinstance(other, str): 

192 other = ReleaseLevel(other) 

193 

194 if not isinstance(other, ReleaseLevel): 

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

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

197 raise ex 

198 

199 return self.value >= other.value 

200 

201 def __hash__(self) -> int: 

202 return hash(self.value) 

203 

204 def __str__(self) -> str: 

205 """ 

206 Returns the release level's string equivalent. 

207 

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

209 """ 

210 if self is ReleaseLevel.Final: 

211 return "final" 

212 elif self is ReleaseLevel.ReleaseCandidate: 

213 return "rc" 

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

215 return "dev" 

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

217 return "beta" 

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

219 return "alpha" 

220 

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

222 

223 

224@export 

225class Flags(Flag): 

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

227 NoVCS = 0 #: No Version Control System VCS 

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

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

230 

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

232 SVN = 32 #: Subversion (SVN) 

233 Git = 64 #: Git 

234 Hg = 128 #: Mercurial (Hg) 

235 

236 

237@export 

238def WordSizeValidator( 

239 bits: Nullable[int] = None, 

240 majorBits: Nullable[int] = None, 

241 minorBits: Nullable[int] = None, 

242 microBits: Nullable[int] = None, 

243 buildBits: Nullable[int] = None 

244): 

245 """ 

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

247 

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

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

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

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

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

253 :return: A validation function for Version instances. 

254 """ 

255 majorMax = minorMax = microMax = buildMax = -1 

256 if bits is not None: 

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

258 

259 if majorBits is not None: 

260 majorMax = 2**majorBits - 1 

261 if minorBits is not None: 

262 minorMax = 2**minorBits - 1 

263 if microBits is not None: 

264 microMax = 2 ** microBits - 1 

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

266 buildMax = 2**buildBits - 1 

267 

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

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

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

271 

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

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

274 

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

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

277 

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

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

280 

281 return True 

282 

283 return validator 

284 

285 

286@export 

287def MaxValueValidator( 

288 max: Nullable[int] = None, 

289 majorMax: Nullable[int] = None, 

290 minorMax: Nullable[int] = None, 

291 microMax: Nullable[int] = None, 

292 buildMax: Nullable[int] = None 

293): 

294 """ 

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

296 

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

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

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

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

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

302 :return: A validation function for Version instances. 

303 """ 

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

305 majorMax = minorMax = microMax = buildMax = max 

306 

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

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

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

310 

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

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

313 

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

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

316 

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

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

319 

320 return True 

321 

322 return validator 

323 

324 

325@export 

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

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

328 

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

330 

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

332 _prefix: str #: Prefix string 

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

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

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

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

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

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

339 _dev: int #: Development number 

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

341 _postfix: str #: Postfix string 

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

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

344 

345 def __init__( 

346 self, 

347 major: int, 

348 minor: Nullable[int] = None, 

349 micro: Nullable[int] = None, 

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

351 number: Nullable[int] = None, 

352 post: Nullable[int] = None, 

353 dev: Nullable[int] = None, 

354 *, 

355 build: Nullable[int] = None, 

356 postfix: Nullable[str] = None, 

357 prefix: Nullable[str] = None, 

358 hash: Nullable[str] = None, 

359 flags: Flags = Flags.NoVCS 

360 ) -> None: 

361 """ 

362 Initializes a version number representation. 

363 

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

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

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

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

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

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

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

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

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

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

374 :param hash: Postfix string. 

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

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

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

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

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

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

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

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

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

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

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

386 """ 

387 self.__hash = None 

388 

389 if not isinstance(major, int): 

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

391 elif major < 0: 

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

393 

394 self._parts = Parts.Major 

395 self._major = major 

396 

397 if minor is not None: 

398 if not isinstance(minor, int): 

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

400 elif minor < 0: 

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

402 

403 self._parts |= Parts.Minor 

404 self._minor = minor 

405 else: 

406 self._minor = 0 

407 

408 if micro is not None: 

409 if not isinstance(micro, int): 

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

411 elif micro < 0: 

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

413 

414 self._parts |= Parts.Micro 

415 self._micro = micro 

416 else: 

417 self._micro = 0 

418 

419 if level is None: 

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

421 elif not isinstance(level, ReleaseLevel): 

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

423 elif level is ReleaseLevel.Final: 

424 if number is not None: 

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

426 

427 self._parts |= Parts.Level 

428 self._releaseLevel = level 

429 self._releaseNumber = 0 

430 else: 

431 self._parts |= Parts.Level 

432 self._releaseLevel = level 

433 

434 if number is not None: 

435 if not isinstance(number, int): 

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

437 elif number < 0: 

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

439 

440 self._releaseNumber = number 

441 else: 

442 self._releaseNumber = 0 

443 

444 if dev is not None: 

445 if not isinstance(dev, int): 

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

447 elif dev < 0: 

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

449 

450 self._parts |= Parts.Dev 

451 self._dev = dev 

452 else: 

453 self._dev = 0 

454 

455 if post is not None: 

456 if not isinstance(post, int): 

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

458 elif post < 0: 

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

460 

461 self._parts |= Parts.Post 

462 self._post = post 

463 else: 

464 self._post = 0 

465 

466 if build is not None: 

467 if not isinstance(build, int): 

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

469 elif build < 0: 

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

471 

472 self._build = build 

473 self._parts |= Parts.Build 

474 else: 

475 self._build = 0 

476 

477 if postfix is not None: 

478 if not isinstance(postfix, str): 

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

480 

481 self._parts |= Parts.Postfix 

482 self._postfix = postfix 

483 else: 

484 self._postfix = "" 

485 

486 if prefix is not None: 

487 if not isinstance(prefix, str): 

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

489 

490 self._parts |= Parts.Prefix 

491 self._prefix = prefix 

492 else: 

493 self._prefix = "" 

494 

495 if hash is not None: 

496 if not isinstance(hash, str): 

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

498 

499 self._parts |= Parts.Hash 

500 self._hash = hash 

501 else: 

502 self._hash = "" 

503 

504 if flags is None: 

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

506 elif not isinstance(flags, Flags): 

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

508 

509 self._flags = flags 

510 

511 @classmethod 

512 @abstractmethod 

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

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

515 

516 @readonly 

517 def Parts(self) -> Parts: 

518 """ 

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

520 

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

522 """ 

523 return self._parts 

524 

525 @readonly 

526 def Prefix(self) -> str: 

527 """ 

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

529 

530 :return: The prefix of the version number. 

531 """ 

532 return self._prefix 

533 

534 @readonly 

535 def Major(self) -> int: 

536 """ 

537 Read-only property to access the major number. 

538 

539 :return: The major number. 

540 """ 

541 return self._major 

542 

543 @readonly 

544 def Minor(self) -> int: 

545 """ 

546 Read-only property to access the minor number. 

547 

548 :return: The minor number. 

549 """ 

550 return self._minor 

551 

552 @readonly 

553 def Micro(self) -> int: 

554 """ 

555 Read-only property to access the micro number. 

556 

557 :return: The micro number. 

558 """ 

559 return self._micro 

560 

561 @readonly 

562 def ReleaseLevel(self) -> ReleaseLevel: 

563 """ 

564 Read-only property to access the release level. 

565 

566 :return: The release level. 

567 """ 

568 return self._releaseLevel 

569 

570 @readonly 

571 def ReleaseNumber(self) -> int: 

572 """ 

573 Read-only property to access the release number. 

574 

575 :return: The release number. 

576 """ 

577 return self._releaseNumber 

578 

579 @readonly 

580 def Post(self) -> int: 

581 """ 

582 Read-only property to access the post number. 

583 

584 :return: The post number. 

585 """ 

586 return self._post 

587 

588 @readonly 

589 def Dev(self) -> int: 

590 """ 

591 Read-only property to access the development number. 

592 

593 :return: The development number. 

594 """ 

595 return self._dev 

596 

597 @readonly 

598 def Build(self) -> int: 

599 """ 

600 Read-only property to access the build number. 

601 

602 :return: The build number. 

603 """ 

604 return self._build 

605 

606 @readonly 

607 def Postfix(self) -> str: 

608 """ 

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

610 

611 :return: The postfix of the version number. 

612 """ 

613 return self._postfix 

614 

615 @readonly 

616 def Hash(self) -> str: 

617 """ 

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

619 

620 :return: The hash. 

621 """ 

622 return self._hash 

623 

624 @readonly 

625 def Flags(self) -> Flags: 

626 """ 

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

628 

629 :return: The flags of the version number. 

630 """ 

631 return self._flags 

632 

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

634 """ 

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

636 

637 :param left: Left operand. 

638 :param right: Right operand. 

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

640 """ 

641 return ( 

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

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

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

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

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

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

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

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

650 (left._postfix == right._postfix) 

651 ) 

652 

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

654 """ 

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

656 

657 :param left: Left operand. 

658 :param right: Right operand. 

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

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

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

662 """ 

663 if left._major < right._major: 

664 return True 

665 elif left._major > right._major: 

666 return False 

667 

668 if left._minor < right._minor: 

669 return True 

670 elif left._minor > right._minor: 

671 return False 

672 

673 if left._micro < right._micro: 

674 return True 

675 elif left._micro > right._micro: 

676 return False 

677 

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

679 return True 

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

681 return False 

682 

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

684 return True 

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

686 return False 

687 

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

689 return True 

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

691 return False 

692 

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

694 return True 

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

696 return False 

697 

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

699 return True 

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

701 return False 

702 

703 return None 

704 

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

706 exactMajor = Parts.Minor in expected._parts 

707 exactMinor = Parts.Micro in expected._parts 

708 

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

710 return False 

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

712 return False 

713 

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

715 return False 

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

717 return False 

718 

719 if Parts.Micro in expected._parts: 

720 return actual._micro >= expected._micro 

721 

722 return True 

723 

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

725 """ 

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

727 

728 .. topic:: Format Specifiers 

729 

730 * ``%p`` - prefix 

731 * ``%M`` - major number 

732 * ``%m`` - minor number 

733 * ``%u`` - micro number 

734 * ``%b`` - build number 

735 

736 :param formatSpec: The format specification. 

737 :return: Formatted version number. 

738 """ 

739 if formatSpec == "": 

740 return self.__str__() 

741 

742 result = formatSpec 

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

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

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

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

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

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

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

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

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

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

753 

754 return result 

755 

756 @mustoverride 

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

758 """ 

759 Compare two version numbers for equality. 

760 

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

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

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

764 

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

766 number. 

767 

768 :param other: Operand to compare against. 

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

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

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

772 """ 

773 if other is None: 

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

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

776 pass 

777 elif isinstance(other, str): 

778 other = self.__class__.Parse(other) 

779 elif isinstance(other, int): 

780 other = self.__class__(major=other) 

781 else: 

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

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

784 raise ex 

785 

786 return self._equal(self, other) 

787 

788 @mustoverride 

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

790 """ 

791 Compare two version numbers for inequality. 

792 

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

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

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

796 

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

798 number. 

799 

800 :param other: Operand to compare against. 

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

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

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

804 """ 

805 if other is None: 

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

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

808 pass 

809 elif isinstance(other, str): 

810 other = self.__class__.Parse(other) 

811 elif isinstance(other, int): 

812 other = self.__class__(major=other) 

813 else: 

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

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

816 raise ex 

817 

818 return not self._equal(self, other) 

819 

820 @mustoverride 

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

822 """ 

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

824 

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

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

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

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

829 

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

831 number. 

832 

833 :param other: Operand to compare against. 

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

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

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

837 """ 

838 if other is None: 

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

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

841 pass 

842 elif isinstance(other, VersionRange): 

843 other = other._lowerBound 

844 elif isinstance(other, VersionSet): 

845 other = other._items[0] 

846 elif isinstance(other, str): 

847 other = self.__class__.Parse(other) 

848 elif isinstance(other, int): 

849 other = self.__class__(major=other) 

850 else: 

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

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

853 raise ex 

854 

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

856 

857 @mustoverride 

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

859 """ 

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

861 

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

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

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

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

866 

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

868 number. 

869 

870 :param other: Operand to compare against. 

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

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

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

874 """ 

875 equalValue = True 

876 if other is None: 

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

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

879 pass 

880 elif isinstance(other, VersionRange): 

881 equalValue = RangeBoundHandling.LowerBoundExclusive not in other._boundHandling 

882 other = other._lowerBound 

883 elif isinstance(other, VersionSet): 

884 other = other._items[0] 

885 elif isinstance(other, str): 

886 other = self.__class__.Parse(other) 

887 elif isinstance(other, int): 

888 other = self.__class__(major=other) 

889 else: 

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

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

892 raise ex 

893 

894 result = self._compare(self, other) 

895 return result if result is not None else equalValue 

896 

897 @mustoverride 

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

899 """ 

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

901 

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

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

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

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

906 

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

908 number. 

909 

910 :param other: Operand to compare against. 

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

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

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

914 """ 

915 if other is None: 

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

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

918 pass 

919 elif isinstance(other, VersionRange): 

920 other = other._upperBound 

921 elif isinstance(other, VersionSet): 

922 other = other._items[-1] 

923 elif isinstance(other, str): 

924 other = self.__class__.Parse(other) 

925 elif isinstance(other, int): 

926 other = self.__class__(major=other) 

927 else: 

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

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

930 raise ex 

931 

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

933 

934 @mustoverride 

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

936 """ 

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

938 

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

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

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

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

943 

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

945 number. 

946 

947 :param other: Operand to compare against. 

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

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

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

951 """ 

952 equalValue = True 

953 if other is None: 

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

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

956 pass 

957 elif isinstance(other, VersionRange): 

958 equalValue = RangeBoundHandling.UpperBoundExclusive not in other._boundHandling 

959 other = other._upperBound 

960 elif isinstance(other, VersionSet): 

961 other = other._items[-1] 

962 elif isinstance(other, str): 

963 other = self.__class__.Parse(other) 

964 elif isinstance(other, int): 

965 other = self.__class__(major=other) 

966 else: 

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

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

969 raise ex 

970 

971 result = self._compare(self, other) 

972 return not result if result is not None else equalValue 

973 

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

975 if other is None: 

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

977 elif isinstance(other, self.__class__): 

978 pass 

979 elif isinstance(other, str): 

980 other = self.__class__.Parse(other) 

981 elif isinstance(other, int): 

982 other = self.__class__(major=other) 

983 else: 

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

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

986 raise ex 

987 

988 return self._minimum(self, other) 

989 

990 def __hash__(self) -> int: 

991 if self.__hash is None: 

992 self.__hash = hash(( 

993 self._prefix, 

994 self._major, 

995 self._minor, 

996 self._micro, 

997 self._releaseLevel, 

998 self._releaseNumber, 

999 self._post, 

1000 self._dev, 

1001 self._build, 

1002 self._postfix, 

1003 self._hash, 

1004 self._flags 

1005 )) 

1006 return self.__hash 

1007 

1008 

1009@export 

1010class SemanticVersion(Version): 

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

1012 

1013 _PATTERN = re_compile( 

1014 r"^" 

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

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

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

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

1019 r"(?:" 

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

1021 r"|" 

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

1023 r"|" 

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

1025 r")?" 

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

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

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

1029 r"$" 

1030 ) 

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

1032# ahead: int = 0 

1033 

1034 def __init__( 

1035 self, 

1036 major: int, 

1037 minor: Nullable[int] = None, 

1038 micro: Nullable[int] = None, 

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

1040 number: Nullable[int] = None, 

1041 post: Nullable[int] = None, 

1042 dev: Nullable[int] = None, 

1043 *, 

1044 build: Nullable[int] = None, 

1045 postfix: Nullable[str] = None, 

1046 prefix: Nullable[str] = None, 

1047 hash: Nullable[str] = None, 

1048 flags: Flags = Flags.NoVCS 

1049 ) -> None: 

1050 """ 

1051 Initializes a semantic version number representation. 

1052 

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

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

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

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

1057 :param level: tbd 

1058 :param number: tbd 

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

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

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

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

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

1064 :param hash: tbd 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1079 """ 

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

1081 

1082 @classmethod 

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

1084 """ 

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

1086 

1087 Allowed prefix characters: 

1088 

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

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

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

1092 * ``rev|REV`` - revision 

1093 

1094 :param versionString: The version string to parse. 

1095 :param validator: Optional, a validation function. 

1096 :returns: An object representing a semantic version. 

1097 :raises TypeError: When parameter ``versionString`` is not a string. 

1098 :raises ValueError: When parameter ``versionString`` is None. 

1099 :raises ValueError: When parameter ``versionString`` is empty. 

1100 """ 

1101 if versionString is None: 

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

1103 elif not isinstance(versionString, str): 

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

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

1106 raise ex 

1107 elif (versionString := versionString.strip()) == "": 

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

1109 

1110 if (match := cls._PATTERN.match(versionString)) is None: 

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

1112 

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

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

1115 return None 

1116 

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 

1162 if validator is not None and not validator(version): 1162 ↛ 1164line 1162 didn't jump to line 1164 because the condition on line 1162 was never true

1163 # TODO: VersionValidatorException 

1164 raise ValueError(f"Failed to validate version string '{versionString}'.") 

1165 

1166 return version 

1167 

1168 @readonly 

1169 def Patch(self) -> int: 

1170 """ 

1171 Read-only property to access the patch number. 

1172 

1173 The patch number is identical to the micro number. 

1174 

1175 :return: The patch number. 

1176 """ 

1177 return self._micro 

1178 

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

1180 """ 

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

1182 

1183 :param left: Left operand. 

1184 :param right: Right operand. 

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

1186 """ 

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

1188 

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

1190 """ 

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

1192 

1193 :param left: Left operand. 

1194 :param right: Right operand. 

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

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

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

1198 """ 

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

1200 

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

1202 """ 

1203 Compare two version numbers for equality. 

1204 

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

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

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

1208 

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

1210 number. 

1211 

1212 :param other: Operand to compare against. 

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

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

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

1216 """ 

1217 return super().__eq__(other) 

1218 

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

1220 """ 

1221 Compare two version numbers for inequality. 

1222 

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

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

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

1226 

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

1228 number. 

1229 

1230 :param other: Operand to compare against. 

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

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

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

1234 """ 

1235 return super().__ne__(other) 

1236 

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

1238 """ 

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

1240 

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

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

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

1244 

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

1246 number. 

1247 

1248 :param other: Operand to compare against. 

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

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

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

1252 """ 

1253 return super().__lt__(other) 

1254 

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

1256 """ 

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

1258 

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

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

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

1262 

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

1264 number. 

1265 

1266 :param other: Operand to compare against. 

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

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

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

1270 """ 

1271 return super().__le__(other) 

1272 

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

1274 """ 

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

1276 

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

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

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

1280 

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

1282 number. 

1283 

1284 :param other: Operand to compare against. 

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

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

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

1288 """ 

1289 return super().__gt__(other) 

1290 

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

1292 """ 

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

1294 

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

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

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

1298 

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

1300 number. 

1301 

1302 :param other: Operand to compare against. 

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

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

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

1306 """ 

1307 return super().__ge__(other) 

1308 

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

1310 return super().__rshift__(other) 

1311 

1312 def __hash__(self) -> int: 

1313 return super().__hash__() 

1314 

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

1316 result = self._format(formatSpec) 

1317 

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

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

1320 

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

1322 

1323 def __repr__(self) -> str: 

1324 """ 

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

1326 

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

1328 """ 

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

1330 

1331 def __str__(self) -> str: 

1332 """ 

1333 Return a string representation of this version number. 

1334 

1335 :returns: Version number representation. 

1336 """ 

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

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

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

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

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

1342 if self._releaseLevel is ReleaseLevel.Development: 

1343 result += "-dev" 

1344 elif self._releaseLevel is ReleaseLevel.Alpha: 

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

1346 elif self._releaseLevel is ReleaseLevel.Beta: 

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

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

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

1350 elif self._releaseLevel is ReleaseLevel.ReleaseCandidate: 

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

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

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

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

1355 

1356 return result 

1357 

1358 

1359@export 

1360class PythonVersion(SemanticVersion): 

1361 """ 

1362 Represents a Python version. 

1363 """ 

1364 

1365 @classmethod 

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

1367 """ 

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

1369 

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

1371 """ 

1372 from sys import version_info 

1373 

1374 if version_info.releaselevel == "final": 

1375 rl = ReleaseLevel.Final 

1376 number = None 

1377 else: # pragma: no cover 

1378 number = version_info.serial 

1379 

1380 if version_info.releaselevel == "alpha": 

1381 rl = ReleaseLevel.Alpha 

1382 elif version_info.releaselevel == "beta": 

1383 rl = ReleaseLevel.Beta 

1384 elif version_info.releaselevel == "candidate": 

1385 rl = ReleaseLevel.ReleaseCandidate 

1386 else: # pragma: no cover 

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

1388 

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

1390 

1391 def __hash__(self) -> int: 

1392 return super().__hash__() 

1393 

1394 def __str__(self) -> str: 

1395 """ 

1396 Return a string representation of this version number. 

1397 

1398 :returns: Version number representation. 

1399 """ 

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

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

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

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

1404 if self._releaseLevel is ReleaseLevel.Alpha: 1404 ↛ 1405line 1404 didn't jump to line 1405 because the condition on line 1404 was never true

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

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

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

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

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

1410 elif self._releaseLevel is ReleaseLevel.ReleaseCandidate: 1410 ↛ 1411line 1410 didn't jump to line 1411 because the condition on line 1410 was never true

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

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

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

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

1415 

1416 return result 

1417 

1418 

1419@export 

1420class CalendarVersion(Version): 

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

1422 

1423 def __init__( 

1424 self, 

1425 major: int, 

1426 minor: Nullable[int] = None, 

1427 micro: Nullable[int] = None, 

1428 build: Nullable[int] = None, 

1429 flags: Flags = Flags.Clean, 

1430 prefix: Nullable[str] = None, 

1431 postfix: Nullable[str] = None 

1432 ) -> None: 

1433 """ 

1434 Initializes a calendar version number representation. 

1435 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1453 """ 

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

1455 

1456 @classmethod 

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

1458 """ 

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

1460 

1461 :param versionString: The version string to parse. 

1462 :returns: An object representing a calendar version. 

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

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

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

1466 """ 

1467 parts = Parts.Unknown 

1468 

1469 if versionString is None: 

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

1471 elif not isinstance(versionString, str): 

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

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

1474 raise ex 

1475 elif versionString == "": 

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

1477 

1478 split = versionString.split(".") 

1479 length = len(split) 

1480 major = int(split[0]) 

1481 minor = 0 

1482 parts |= Parts.Major 

1483 

1484 if length >= 2: 

1485 minor = int(split[1]) 

1486 parts |= Parts.Minor 

1487 

1488 flags = Flags.Clean 

1489 

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

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

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

1493 

1494 return version 

1495 

1496 @property 

1497 def Year(self) -> int: 

1498 """ 

1499 Read-only property to access the year part. 

1500 

1501 :return: The year part. 

1502 """ 

1503 return self._major 

1504 

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

1506 """ 

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

1508 

1509 :param left: Left parameter. 

1510 :param right: Right parameter. 

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

1512 """ 

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

1514 

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

1516 """ 

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

1518 

1519 :param left: Left parameter. 

1520 :param right: Right parameter. 

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

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

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

1524 """ 

1525 if left._major < right._major: 

1526 return True 

1527 elif left._major > right._major: 

1528 return False 

1529 

1530 if left._minor < right._minor: 

1531 return True 

1532 elif left._minor > right._minor: 

1533 return False 

1534 

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

1536 return True 

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

1538 return False 

1539 

1540 return None 

1541 

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

1543 """ 

1544 Compare two version numbers for equality. 

1545 

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

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

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

1549 

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

1551 number. 

1552 

1553 :param other: Parameter to compare against. 

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

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

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

1557 """ 

1558 return super().__eq__(other) 

1559 

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

1561 """ 

1562 Compare two version numbers for inequality. 

1563 

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

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

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

1567 

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

1569 number. 

1570 

1571 :param other: Parameter to compare against. 

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

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

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

1575 """ 

1576 return super().__ne__(other) 

1577 

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

1579 """ 

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

1581 

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

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

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

1585 

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

1587 number. 

1588 

1589 :param other: Parameter to compare against. 

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

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

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

1593 """ 

1594 return super().__lt__(other) 

1595 

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

1597 """ 

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

1599 

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

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

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

1603 

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

1605 number. 

1606 

1607 :param other: Parameter to compare against. 

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

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

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

1611 """ 

1612 return super().__le__(other) 

1613 

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

1615 """ 

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

1617 

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

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

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

1621 

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

1623 number. 

1624 

1625 :param other: Parameter to compare against. 

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

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

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

1629 """ 

1630 return super().__gt__(other) 

1631 

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

1633 """ 

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

1635 

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

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

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

1639 

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

1641 number. 

1642 

1643 :param other: Parameter to compare against. 

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

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

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

1647 """ 

1648 return super().__ge__(other) 

1649 

1650 def __hash__(self) -> int: 

1651 return super().__hash__() 

1652 

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

1654 """ 

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

1656 

1657 .. topic:: Format Specifiers 

1658 

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

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

1661 

1662 :param formatSpec: The format specification. 

1663 :return: Formatted version number. 

1664 """ 

1665 if formatSpec == "": 

1666 return self.__str__() 

1667 

1668 result = formatSpec 

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

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

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

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

1673 

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

1675 

1676 def __repr__(self) -> str: 

1677 """ 

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

1679 

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

1681 """ 

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

1683 

1684 def __str__(self) -> str: 

1685 """ 

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

1687 

1688 :returns: Version number representation including a prefix. 

1689 """ 

1690 result = f"{self._major}" 

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

1692 

1693 return result 

1694 

1695 

1696@export 

1697class YearMonthVersion(CalendarVersion): 

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

1699 

1700 def __init__( 

1701 self, 

1702 year: int, 

1703 month: Nullable[int] = None, 

1704 build: Nullable[int] = None, 

1705 flags: Flags = Flags.Clean, 

1706 prefix: Nullable[str] = None, 

1707 postfix: Nullable[str] = None 

1708 ) -> None: 

1709 """ 

1710 Initializes a year-month version number representation. 

1711 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1728 """ 

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

1730 

1731 @property 

1732 def Month(self) -> int: 

1733 """ 

1734 Read-only property to access the month part. 

1735 

1736 :return: The month part. 

1737 """ 

1738 return self._minor 

1739 

1740 def __hash__(self) -> int: 

1741 return super().__hash__() 

1742 

1743 

1744@export 

1745class YearWeekVersion(CalendarVersion): 

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

1747 

1748 def __init__( 

1749 self, 

1750 year: int, 

1751 week: Nullable[int] = None, 

1752 build: Nullable[int] = None, 

1753 flags: Flags = Flags.Clean, 

1754 prefix: Nullable[str] = None, 

1755 postfix: Nullable[str] = None 

1756 ) -> None: 

1757 """ 

1758 Initializes a year-week version number representation. 

1759 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1776 """ 

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

1778 

1779 @property 

1780 def Week(self) -> int: 

1781 """ 

1782 Read-only property to access the week part. 

1783 

1784 :return: The week part. 

1785 """ 

1786 return self._minor 

1787 

1788 def __hash__(self) -> int: 

1789 return super().__hash__() 

1790 

1791 

1792@export 

1793class YearReleaseVersion(CalendarVersion): 

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

1795 

1796 def __init__( 

1797 self, 

1798 year: int, 

1799 release: Nullable[int] = None, 

1800 build: Nullable[int] = None, 

1801 flags: Flags = Flags.Clean, 

1802 prefix: Nullable[str] = None, 

1803 postfix: Nullable[str] = None 

1804 ) -> None: 

1805 """ 

1806 Initializes a year-release version number representation. 

1807 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1824 """ 

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

1826 

1827 @property 

1828 def Release(self) -> int: 

1829 """ 

1830 Read-only property to access the release number. 

1831 

1832 :return: The release number. 

1833 """ 

1834 return self._minor 

1835 

1836 def __hash__(self) -> int: 

1837 return super().__hash__() 

1838 

1839 

1840@export 

1841class YearMonthDayVersion(CalendarVersion): 

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

1843 

1844 def __init__( 

1845 self, 

1846 year: int, 

1847 month: Nullable[int] = None, 

1848 day: Nullable[int] = None, 

1849 build: Nullable[int] = None, 

1850 flags: Flags = Flags.Clean, 

1851 prefix: Nullable[str] = None, 

1852 postfix: Nullable[str] = None 

1853 ) -> None: 

1854 """ 

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

1856 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1874 """ 

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

1876 

1877 @property 

1878 def Month(self) -> int: 

1879 """ 

1880 Read-only property to access the month part. 

1881 

1882 :return: The month part. 

1883 """ 

1884 return self._minor 

1885 

1886 @property 

1887 def Day(self) -> int: 

1888 """ 

1889 Read-only property to access the day part. 

1890 

1891 :return: The day part. 

1892 """ 

1893 return self._micro 

1894 

1895 def __hash__(self) -> int: 

1896 return super().__hash__() 

1897 

1898 

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

1900 

1901@export 

1902class RangeBoundHandling(Flag): 

1903 """ 

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

1905 

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

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

1908 """ 

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

1910 LowerBoundInclusive = 0 #: Lower bound is inclusive. 

1911 UpperBoundInclusive = 0 #: Upper bound is inclusive. 

1912 LowerBoundExclusive = 1 #: Lower bound is exclusive. 

1913 UpperBoundExclusive = 2 #: Upper bound is exclusive. 

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

1915 

1916 

1917@export 

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

1919 """ 

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

1921 

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

1923 """ 

1924 _lowerBound: V 

1925 _upperBound: V 

1926 _boundHandling: RangeBoundHandling 

1927 

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

1929 """ 

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

1931 

1932 :param lowerBound: lowest version (inclusive). 

1933 :param upperBound: hightest version (inclusive). 

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

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

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

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

1938 """ 

1939 if not isinstance(lowerBound, Version): 

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

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

1942 raise ex 

1943 

1944 if not isinstance(upperBound, Version): 

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

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

1947 raise ex 

1948 

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

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

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

1952 raise ex 

1953 

1954 if not (lowerBound <= upperBound): 

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

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

1957 raise ex 

1958 

1959 self._lowerBound = lowerBound 

1960 self._upperBound = upperBound 

1961 self._boundHandling = boundHandling 

1962 

1963 @property 

1964 def LowerBound(self) -> V: 

1965 """ 

1966 Property to access the range's lower bound. 

1967 

1968 :return: Lower bound of the version range. 

1969 """ 

1970 return self._lowerBound 

1971 

1972 @LowerBound.setter 

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

1974 if not isinstance(value, Version): 

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

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

1977 raise ex 

1978 

1979 self._lowerBound = value 

1980 

1981 @readonly 

1982 def UpperBound(self) -> V: 

1983 """ 

1984 Property to access the range's upper bound. 

1985 

1986 :return: Upper bound of the version range. 

1987 """ 

1988 return self._upperBound 

1989 

1990 @UpperBound.setter 

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

1992 if not isinstance(value, Version): 

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

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

1995 raise ex 

1996 

1997 self._upperBound = value 

1998 

1999 @readonly 

2000 def BoundHandling(self) -> RangeBoundHandling: 

2001 """ 

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

2003 

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

2005 """ 

2006 return self._boundHandling 

2007 

2008 @BoundHandling.setter 

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

2010 if not isinstance(value, RangeBoundHandling): 

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

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

2013 raise ex 

2014 

2015 self._boundHandling = value 

2016 

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

2018 """ 

2019 Compute the intersection of two version ranges. 

2020 

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

2022 :returns: Intersected version range. 

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

2024 :raises ValueError: If intersection is empty. 

2025 """ 

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

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

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

2029 raise ex 

2030 

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

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

2033 ex.add_note( 

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

2035 raise ex 

2036 

2037 if other._lowerBound < self._lowerBound: 

2038 lBound = self._lowerBound 

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

2040 lBound = other._lowerBound 

2041 else: 

2042 raise ValueError() 

2043 

2044 if other._upperBound > self._upperBound: 

2045 uBound = self._upperBound 

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

2047 uBound = other._upperBound 

2048 else: 

2049 raise ValueError() 

2050 

2051 return self.__class__(lBound, uBound) 

2052 

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

2054 """ 

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

2056 

2057 :param other: Operand to compare against. 

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

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

2060 """ 

2061 # TODO: support VersionRange < VersionRange too 

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

2063 if not isinstance(other, Version): 

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

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

2066 raise ex 

2067 

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

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

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

2071 raise ex 

2072 

2073 return self._upperBound < other 

2074 

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

2076 """ 

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

2078 

2079 :param other: Operand to compare against. 

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

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

2082 """ 

2083 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2088 raise ex 

2089 

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

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

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

2093 raise ex 

2094 

2095 if RangeBoundHandling.UpperBoundExclusive in self._boundHandling: 

2096 return self._upperBound < other 

2097 else: 

2098 return self._upperBound <= other 

2099 

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

2101 """ 

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

2103 

2104 :param other: Operand to compare against. 

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

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

2107 """ 

2108 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2113 raise ex 

2114 

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

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

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

2118 raise ex 

2119 

2120 return self._lowerBound > other 

2121 

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

2123 """ 

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

2125 

2126 :param other: Operand to compare against. 

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

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

2129 """ 

2130 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2135 raise ex 

2136 

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

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

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

2140 raise ex 

2141 

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

2143 return self._lowerBound > other 

2144 else: 

2145 return self._lowerBound >= other 

2146 

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

2148 """ 

2149 Check if the version is in the version range. 

2150 

2151 :param version: Version to check. 

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

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

2154 """ 

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

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

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

2158 raise ex 

2159 

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

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

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

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

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

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

2166 else: 

2167 return self._lowerBound < version < self._upperBound 

2168 

2169 

2170@export 

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

2172 """ 

2173 Representation of an ordered set of versions. 

2174 

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

2176 """ 

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

2178 

2179 def __init__(self, versions: Union[Version, Iterable[V]]) -> None: 

2180 """ 

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

2182 

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

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

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

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

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

2188 """ 

2189 if versions is None: 

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

2191 

2192 if isinstance(versions, Version): 

2193 self._items = [versions] 

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

2195 iterator = iter(versions) 

2196 try: 

2197 firstVersion = next(iterator) 

2198 except StopIteration: 

2199 self._items = [] 

2200 return 

2201 

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

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

2204 

2205 baseType = firstVersion.__class__ 

2206 for version in iterator: 

2207 if not isinstance(version, baseType): 

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

2209 

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

2211 else: 

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

2213 

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

2215 """ 

2216 Compute intersection of two version sets. 

2217 

2218 :param other: Second set of versions. 

2219 :returns: Intersection of two version sets. 

2220 """ 

2221 selfIterator = self.__iter__() 

2222 otherIterator = other.__iter__() 

2223 

2224 result = [] 

2225 try: 

2226 selfValue = next(selfIterator) 

2227 otherValue = next(otherIterator) 

2228 

2229 while True: 

2230 if selfValue < otherValue: 

2231 selfValue = next(selfIterator) 

2232 elif otherValue < selfValue: 

2233 otherValue = next(otherIterator) 

2234 else: 

2235 result.append(selfValue) 

2236 selfValue = next(selfIterator) 

2237 otherValue = next(otherIterator) 

2238 

2239 except StopIteration: 

2240 pass 

2241 

2242 return VersionSet(result) 

2243 

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

2245 """ 

2246 Compute union of two version sets. 

2247 

2248 :param other: Second set of versions. 

2249 :returns: Union of two version sets. 

2250 """ 

2251 selfIterator = self.__iter__() 

2252 otherIterator = other.__iter__() 

2253 

2254 result = [] 

2255 try: 

2256 selfValue = next(selfIterator) 

2257 except StopIteration: 

2258 for otherValue in otherIterator: 

2259 result.append(otherValue) 

2260 

2261 try: 

2262 otherValue = next(otherIterator) 

2263 except StopIteration: 

2264 for selfValue in selfIterator: 

2265 result.append(selfValue) 

2266 

2267 while True: 

2268 if selfValue < otherValue: 

2269 result.append(selfValue) 

2270 try: 

2271 selfValue = next(selfIterator) 

2272 except StopIteration: 

2273 result.append(otherValue) 

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

2275 result.append(otherValue) 

2276 

2277 break 

2278 elif otherValue < selfValue: 

2279 result.append(otherValue) 

2280 try: 

2281 otherValue = next(otherIterator) 

2282 except StopIteration: 

2283 result.append(selfValue) 

2284 for selfValue in selfIterator: 

2285 result.append(selfValue) 

2286 

2287 break 

2288 else: 

2289 result.append(selfValue) 

2290 try: 

2291 selfValue = next(selfIterator) 

2292 except StopIteration: 

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

2294 result.append(otherValue) 

2295 

2296 break 

2297 

2298 try: 

2299 otherValue = next(otherIterator) 

2300 except StopIteration: 

2301 for selfValue in selfIterator: 

2302 result.append(selfValue) 

2303 

2304 break 

2305 

2306 return VersionSet(result) 

2307 

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

2309 """ 

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

2311 

2312 :param other: Operand to compare against. 

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

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

2315 """ 

2316 # TODO: support VersionRange < VersionRange too 

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

2318 if not isinstance(other, Version): 

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

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

2321 raise ex 

2322 

2323 return self._items[-1] < other 

2324 

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

2326 """ 

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

2328 

2329 :param other: Operand to compare against. 

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

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

2332 """ 

2333 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2338 raise ex 

2339 

2340 return self._items[-1] <= other 

2341 

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

2343 """ 

2344 Compare a version set and a version numbers if the version set is greater than the second operand (version). 

2345 

2346 :param other: Operand to compare against. 

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

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

2349 """ 

2350 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2355 raise ex 

2356 

2357 return self._items[0] > other 

2358 

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

2360 """ 

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

2362 

2363 :param other: Operand to compare against. 

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

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

2366 """ 

2367 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2372 raise ex 

2373 

2374 return self._items[0] >= other 

2375 

2376 def __contains__(self, version: V) -> bool: 

2377 """ 

2378 Checks if the version a member of the set. 

2379 

2380 :param version: The version to check. 

2381 :returns: ``True``, if the version is a member of the set. 

2382 """ 

2383 return version in self._items 

2384 

2385 def __len__(self) -> int: 

2386 """ 

2387 Returns the number of members in the set. 

2388 

2389 :returns: Number of set members. 

2390 """ 

2391 return len(self._items) 

2392 

2393 def __iter__(self) -> Iterator[V]: 

2394 """ 

2395 Returns an iterator to iterate all versions of this set from lowest to highest. 

2396 

2397 :returns: Iterator to iterate versions. 

2398 """ 

2399 return self._items.__iter__() 

2400 

2401 def __getitem__(self, index: int) -> V: 

2402 """ 

2403 Access to a version of a set by index. 

2404 

2405 :param index: The index of the version to access. 

2406 :returns: The indexed version. 

2407 

2408 .. hint:: 

2409 

2410 Versions are ordered from lowest to highest version number. 

2411 """ 

2412 return self._items[index]