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

972 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-07 17:18 +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 

43from pyTooling.Decorators import export, readonly 

44from pyTooling.MetaClasses import ExtendedType, abstractmethod, mustoverride 

45from pyTooling.Exceptions import ToolingException 

46from pyTooling.Common import getFullyQualifiedName 

47 

48 

49@export 

50class Parts(Flag): 

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

52 Unknown = 0 #: Undocumented 

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

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

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

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

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

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

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

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

61 Level = 8 #: Release level is present. 

62 Dev = 16 #: Development part is present. 

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

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

65 Prefix = 128 #: Prefix is present. 

66 Postfix = 256 #: Postfix is present. 

67 Hash = 512 #: Hash is present. 

68# AHead = 256 

69 

70 

71@export 

72class ReleaseLevel(Enum): 

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

74 Final = 0 #: 

75 ReleaseCandidate = -10 #: 

76 Development = -20 #: 

77 Gamma = -30 #: 

78 Beta = -40 #: 

79 Alpha = -50 #: 

80 

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

82 """ 

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

84 

85 :param other: Operand to compare against. 

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

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

88 """ 

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

90 other = ReleaseLevel(other) 

91 

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

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

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

95 raise ex 

96 

97 return self is other 

98 

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

100 """ 

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

102 

103 :param other: Operand to compare against. 

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

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

106 """ 

107 if isinstance(other, str): 

108 other = ReleaseLevel(other) 

109 

110 if not isinstance(other, ReleaseLevel): 

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

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

113 raise ex 

114 

115 return self is not other 

116 

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

118 """ 

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

120 

121 :param other: Operand to compare against. 

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

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

124 """ 

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

126 other = ReleaseLevel(other) 

127 

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

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

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

131 raise ex 

132 

133 return self.value < other.value 

134 

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

136 """ 

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

138 

139 :param other: Operand to compare against. 

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

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

142 """ 

143 if isinstance(other, str): 

144 other = ReleaseLevel(other) 

145 

146 if not isinstance(other, ReleaseLevel): 

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

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

149 raise ex 

150 

151 return self.value <= other.value 

152 

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

154 """ 

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

156 

157 :param other: Operand to compare against. 

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

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

160 """ 

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

162 other = ReleaseLevel(other) 

163 

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

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

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

167 raise ex 

168 

169 return self.value > other.value 

170 

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

172 """ 

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

174 

175 :param other: Operand to compare against. 

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

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

178 """ 

179 if isinstance(other, str): 

180 other = ReleaseLevel(other) 

181 

182 if not isinstance(other, ReleaseLevel): 

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

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

185 raise ex 

186 

187 return self.value >= other.value 

188 

189 def __hash__(self) -> int: 

190 return hash(self.value) 

191 

192 def __str__(self) -> str: 

193 """ 

194 Returns the release level's string equivalent. 

195 

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

197 """ 

198 if self is ReleaseLevel.Final: 

199 return "final" 

200 elif self is ReleaseLevel.ReleaseCandidate: 

201 return "rc" 

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

203 return "dev" 

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

205 return "beta" 

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

207 return "alpha" 

208 

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

210 

211 

212@export 

213class Flags(Flag): 

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

215 NoVCS = 0 #: No Version Control System VCS 

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

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

218 

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

220 SVN = 32 #: Subversion (SVN) 

221 Git = 64 #: Git 

222 Hg = 128 #: Mercurial (Hg) 

223 

224 

225@export 

226def WordSizeValidator( 

227 bits: Nullable[int] = None, 

228 majorBits: Nullable[int] = None, 

229 minorBits: Nullable[int] = None, 

230 microBits: Nullable[int] = None, 

231 buildBits: Nullable[int] = None 

232): 

233 """ 

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

235 

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

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

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

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

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

241 :return: A validation function for Version instances. 

242 """ 

243 majorMax = minorMax = microMax = buildMax = -1 

244 if bits is not None: 

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

246 

247 if majorBits is not None: 

248 majorMax = 2**majorBits - 1 

249 if minorBits is not None: 

250 minorMax = 2**minorBits - 1 

251 if microBits is not None: 

252 microMax = 2 ** microBits - 1 

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

254 buildMax = 2**buildBits - 1 

255 

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

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

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

259 

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

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

262 

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

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

265 

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

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

268 

269 return True 

270 

271 return validator 

272 

273 

274@export 

275def MaxValueValidator( 

276 max: Nullable[int] = None, 

277 majorMax: Nullable[int] = None, 

278 minorMax: Nullable[int] = None, 

279 microMax: Nullable[int] = None, 

280 buildMax: Nullable[int] = None 

281): 

282 """ 

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

284 

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

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

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

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

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

290 :return: A validation function for Version instances. 

291 """ 

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

293 majorMax = minorMax = microMax = buildMax = max 

294 

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

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

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

298 

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

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

301 

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

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

304 

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

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

307 

308 return True 

309 

310 return validator 

311 

312 

313@export 

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

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

316 

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

318 

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

320 _prefix: str #: Prefix string 

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

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

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

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

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

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

327 _dev: int #: Development number 

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

329 _postfix: str #: Postfix string 

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

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

332 

333 def __init__( 

334 self, 

335 major: int, 

336 minor: Nullable[int] = None, 

337 micro: Nullable[int] = None, 

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

339 number: Nullable[int] = None, 

340 post: Nullable[int] = None, 

341 dev: Nullable[int] = None, 

342 *, 

343 build: Nullable[int] = None, 

344 postfix: Nullable[str] = None, 

345 prefix: Nullable[str] = None, 

346 hash: Nullable[str] = None, 

347 flags: Flags = Flags.NoVCS 

348 ) -> None: 

349 """ 

350 Initializes a version number representation. 

351 

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

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

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

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

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

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

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

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

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

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

362 :param hash: Postfix string. 

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

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

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

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

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

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

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

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

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

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

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

374 """ 

375 self.__hash = None 

376 

377 if not isinstance(major, int): 

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

379 elif major < 0: 

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

381 

382 self._parts = Parts.Major 

383 self._major = major 

384 

385 if minor is not None: 

386 if not isinstance(minor, int): 

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

388 elif minor < 0: 

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

390 

391 self._parts |= Parts.Minor 

392 self._minor = minor 

393 else: 

394 self._minor = 0 

395 

396 if micro is not None: 

397 if not isinstance(micro, int): 

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

399 elif micro < 0: 

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

401 

402 self._parts |= Parts.Micro 

403 self._micro = micro 

404 else: 

405 self._micro = 0 

406 

407 if level is None: 

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

409 elif not isinstance(level, ReleaseLevel): 

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

411 elif level is ReleaseLevel.Final: 

412 if number is not None: 

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

414 

415 self._parts |= Parts.Level 

416 self._releaseLevel = level 

417 self._releaseNumber = 0 

418 else: 

419 self._parts |= Parts.Level 

420 self._releaseLevel = level 

421 

422 if number is not None: 

423 if not isinstance(number, int): 

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

425 elif number < 0: 

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

427 

428 self._releaseNumber = number 

429 else: 

430 self._releaseNumber = 0 

431 

432 if dev is not None: 

433 if not isinstance(dev, int): 

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

435 elif dev < 0: 

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

437 

438 self._parts |= Parts.Dev 

439 self._dev = dev 

440 else: 

441 self._dev = 0 

442 

443 if post is not None: 

444 if not isinstance(post, int): 

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

446 elif post < 0: 

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

448 

449 self._parts |= Parts.Post 

450 self._post = post 

451 else: 

452 self._post = 0 

453 

454 if build is not None: 

455 if not isinstance(build, int): 

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

457 elif build < 0: 

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

459 

460 self._build = build 

461 self._parts |= Parts.Build 

462 else: 

463 self._build = 0 

464 

465 if postfix is not None: 

466 if not isinstance(postfix, str): 

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

468 

469 self._parts |= Parts.Postfix 

470 self._postfix = postfix 

471 else: 

472 self._postfix = "" 

473 

474 if prefix is not None: 

475 if not isinstance(prefix, str): 

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

477 

478 self._parts |= Parts.Prefix 

479 self._prefix = prefix 

480 else: 

481 self._prefix = "" 

482 

483 if hash is not None: 

484 if not isinstance(hash, str): 

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

486 

487 self._parts |= Parts.Hash 

488 self._hash = hash 

489 else: 

490 self._hash = "" 

491 

492 if flags is None: 

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

494 elif not isinstance(flags, Flags): 

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

496 

497 self._flags = flags 

498 

499 @classmethod 

500 @abstractmethod 

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

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

503 

504 @readonly 

505 def Parts(self) -> Parts: 

506 """ 

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

508 

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

510 """ 

511 return self._parts 

512 

513 @readonly 

514 def Prefix(self) -> str: 

515 """ 

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

517 

518 :return: The prefix of the version number. 

519 """ 

520 return self._prefix 

521 

522 @readonly 

523 def Major(self) -> int: 

524 """ 

525 Read-only property to access the major number. 

526 

527 :return: The major number. 

528 """ 

529 return self._major 

530 

531 @readonly 

532 def Minor(self) -> int: 

533 """ 

534 Read-only property to access the minor number. 

535 

536 :return: The minor number. 

537 """ 

538 return self._minor 

539 

540 @readonly 

541 def Micro(self) -> int: 

542 """ 

543 Read-only property to access the micro number. 

544 

545 :return: The micro number. 

546 """ 

547 return self._micro 

548 

549 @readonly 

550 def ReleaseLevel(self) -> ReleaseLevel: 

551 """ 

552 Read-only property to access the release level. 

553 

554 :return: The release level. 

555 """ 

556 return self._releaseLevel 

557 

558 @readonly 

559 def ReleaseNumber(self) -> int: 

560 """ 

561 Read-only property to access the release number. 

562 

563 :return: The release number. 

564 """ 

565 return self._releaseNumber 

566 

567 @readonly 

568 def Post(self) -> int: 

569 """ 

570 Read-only property to access the post number. 

571 

572 :return: The post number. 

573 """ 

574 return self._post 

575 

576 @readonly 

577 def Dev(self) -> int: 

578 """ 

579 Read-only property to access the development number. 

580 

581 :return: The development number. 

582 """ 

583 return self._dev 

584 

585 @readonly 

586 def Build(self) -> int: 

587 """ 

588 Read-only property to access the build number. 

589 

590 :return: The build number. 

591 """ 

592 return self._build 

593 

594 @readonly 

595 def Postfix(self) -> str: 

596 """ 

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

598 

599 :return: The postfix of the version number. 

600 """ 

601 return self._postfix 

602 

603 @readonly 

604 def Hash(self) -> str: 

605 """ 

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

607 

608 :return: The hash. 

609 """ 

610 return self._hash 

611 

612 @readonly 

613 def Flags(self) -> Flags: 

614 """ 

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

616 

617 :return: The flags of the version number. 

618 """ 

619 return self._flags 

620 

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

622 """ 

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

624 

625 :param left: Left operand. 

626 :param right: Right operand. 

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

628 """ 

629 return ( 

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

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

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

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

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

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

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

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

638 (left._postfix == right._postfix) 

639 ) 

640 

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

642 """ 

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

644 

645 :param left: Left operand. 

646 :param right: Right operand. 

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

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

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

650 """ 

651 if left._major < right._major: 

652 return True 

653 elif left._major > right._major: 

654 return False 

655 

656 if left._minor < right._minor: 

657 return True 

658 elif left._minor > right._minor: 

659 return False 

660 

661 if left._micro < right._micro: 

662 return True 

663 elif left._micro > right._micro: 

664 return False 

665 

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

667 return True 

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

669 return False 

670 

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

672 return True 

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

674 return False 

675 

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

677 return True 

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

679 return False 

680 

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

682 return True 

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

684 return False 

685 

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

687 return True 

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

689 return False 

690 

691 return None 

692 

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

694 exactMajor = Parts.Minor in expected._parts 

695 exactMinor = Parts.Micro in expected._parts 

696 

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

698 return False 

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

700 return False 

701 

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

703 return False 

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

705 return False 

706 

707 if Parts.Micro in expected._parts: 

708 return actual._micro >= expected._micro 

709 

710 return True 

711 

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

713 """ 

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

715 

716 .. topic:: Format Specifiers 

717 

718 * ``%p`` - prefix 

719 * ``%M`` - major number 

720 * ``%m`` - minor number 

721 * ``%u`` - micro number 

722 * ``%b`` - build number 

723 

724 :param formatSpec: The format specification. 

725 :return: Formatted version number. 

726 """ 

727 if formatSpec == "": 

728 return self.__str__() 

729 

730 result = formatSpec 

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

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

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

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

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

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

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

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

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

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

741 

742 return result 

743 

744 @mustoverride 

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

746 """ 

747 Compare two version numbers for equality. 

748 

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

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

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

752 

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

754 number. 

755 

756 :param other: Operand to compare against. 

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

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

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

760 """ 

761 if other is None: 

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

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

764 pass 

765 elif isinstance(other, str): 

766 other = self.__class__.Parse(other) 

767 elif isinstance(other, int): 

768 other = self.__class__(major=other) 

769 else: 

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

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

772 raise ex 

773 

774 return self._equal(self, other) 

775 

776 @mustoverride 

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

778 """ 

779 Compare two version numbers for inequality. 

780 

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

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

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

784 

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

786 number. 

787 

788 :param other: Operand to compare against. 

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

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

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

792 """ 

793 if other is None: 

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

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

796 pass 

797 elif isinstance(other, str): 

798 other = self.__class__.Parse(other) 

799 elif isinstance(other, int): 

800 other = self.__class__(major=other) 

801 else: 

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

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

804 raise ex 

805 

806 return not self._equal(self, other) 

807 

808 @mustoverride 

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

810 """ 

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

812 

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

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

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

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

817 

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

819 number. 

820 

821 :param other: Operand to compare against. 

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

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

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

825 """ 

826 if other is None: 

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

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

829 pass 

830 elif isinstance(other, VersionRange): 

831 other = other._lowerBound 

832 elif isinstance(other, VersionSet): 

833 other = other._items[0] 

834 elif isinstance(other, str): 

835 other = self.__class__.Parse(other) 

836 elif isinstance(other, int): 

837 other = self.__class__(major=other) 

838 else: 

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

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

841 raise ex 

842 

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

844 

845 @mustoverride 

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

847 """ 

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

849 

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

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

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

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

854 

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

856 number. 

857 

858 :param other: Operand to compare against. 

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

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

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

862 """ 

863 equalValue = True 

864 if other is None: 

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

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

867 pass 

868 elif isinstance(other, VersionRange): 

869 equalValue = RangeBoundHandling.LowerBoundExclusive not in other._boundHandling 

870 other = other._lowerBound 

871 elif isinstance(other, VersionSet): 

872 other = other._items[0] 

873 elif isinstance(other, str): 

874 other = self.__class__.Parse(other) 

875 elif isinstance(other, int): 

876 other = self.__class__(major=other) 

877 else: 

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

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

880 raise ex 

881 

882 result = self._compare(self, other) 

883 return result if result is not None else equalValue 

884 

885 @mustoverride 

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

887 """ 

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

889 

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

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

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

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

894 

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

896 number. 

897 

898 :param other: Operand to compare against. 

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

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

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

902 """ 

903 if other is None: 

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

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

906 pass 

907 elif isinstance(other, VersionRange): 

908 other = other._upperBound 

909 elif isinstance(other, VersionSet): 

910 other = other._items[-1] 

911 elif isinstance(other, str): 

912 other = self.__class__.Parse(other) 

913 elif isinstance(other, int): 

914 other = self.__class__(major=other) 

915 else: 

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

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

918 raise ex 

919 

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

921 

922 @mustoverride 

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

924 """ 

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

926 

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

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

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

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

931 

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

933 number. 

934 

935 :param other: Operand to compare against. 

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

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

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

939 """ 

940 equalValue = True 

941 if other is None: 

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

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

944 pass 

945 elif isinstance(other, VersionRange): 

946 equalValue = RangeBoundHandling.UpperBoundExclusive not in other._boundHandling 

947 other = other._upperBound 

948 elif isinstance(other, VersionSet): 

949 other = other._items[-1] 

950 elif isinstance(other, str): 

951 other = self.__class__.Parse(other) 

952 elif isinstance(other, int): 

953 other = self.__class__(major=other) 

954 else: 

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

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

957 raise ex 

958 

959 result = self._compare(self, other) 

960 return not result if result is not None else equalValue 

961 

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

963 if other is None: 

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

965 elif isinstance(other, self.__class__): 

966 pass 

967 elif isinstance(other, str): 

968 other = self.__class__.Parse(other) 

969 elif isinstance(other, int): 

970 other = self.__class__(major=other) 

971 else: 

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

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

974 raise ex 

975 

976 return self._minimum(self, other) 

977 

978 def __hash__(self) -> int: 

979 if self.__hash is None: 

980 self.__hash = hash(( 

981 self._prefix, 

982 self._major, 

983 self._minor, 

984 self._micro, 

985 self._releaseLevel, 

986 self._releaseNumber, 

987 self._post, 

988 self._dev, 

989 self._build, 

990 self._postfix, 

991 self._hash, 

992 self._flags 

993 )) 

994 return self.__hash 

995 

996 

997@export 

998class SemanticVersion(Version): 

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

1000 

1001 _PATTERN = re_compile( 

1002 r"^" 

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

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

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

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

1007 r"(?:" 

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

1009 r"|" 

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

1011 r"|" 

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

1013 r")?" 

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

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

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

1017 r"$" 

1018 ) 

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

1020# ahead: int = 0 

1021 

1022 def __init__( 

1023 self, 

1024 major: int, 

1025 minor: Nullable[int] = None, 

1026 micro: Nullable[int] = None, 

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

1028 number: Nullable[int] = None, 

1029 post: Nullable[int] = None, 

1030 dev: Nullable[int] = None, 

1031 *, 

1032 build: Nullable[int] = None, 

1033 postfix: Nullable[str] = None, 

1034 prefix: Nullable[str] = None, 

1035 hash: Nullable[str] = None, 

1036 flags: Flags = Flags.NoVCS 

1037 ) -> None: 

1038 """ 

1039 Initializes a semantic version number representation. 

1040 

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

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

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

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

1045 :param level: tbd 

1046 :param number: tbd 

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

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

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

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

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

1052 :param hash: tbd 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1067 """ 

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

1069 

1070 @classmethod 

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

1072 """ 

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

1074 

1075 Allowed prefix characters: 

1076 

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

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

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

1080 * ``rev|REV`` - revision 

1081 

1082 :param versionString: The version string to parse. 

1083 :param validator: Optional, a validation function. 

1084 :returns: An object representing a semantic version. 

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

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

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

1088 """ 

1089 if versionString is None: 

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

1091 elif not isinstance(versionString, str): 

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

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

1094 raise ex 

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

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

1097 

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

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

1100 

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

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

1103 return None 

1104 

1105 try: 

1106 return int(value) 

1107 except ValueError as ex: # pragma: no cover 

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

1109 

1110 release = match["release"] 

1111 if release is not None: 

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

1113 releaseLevel = ReleaseLevel.Development 

1114 elif release == "final": 

1115 releaseLevel = ReleaseLevel.Final 

1116 else: # pragma: no cover 

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

1118 else: 

1119 level = match["level"] 

1120 if level is not None: 

1121 level = level.lower() 

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

1123 releaseLevel = ReleaseLevel.Alpha 

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

1125 releaseLevel = ReleaseLevel.Beta 

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

1127 releaseLevel = ReleaseLevel.Gamma 

1128 elif level == "rc": 

1129 releaseLevel = ReleaseLevel.ReleaseCandidate 

1130 else: # pragma: no cover 

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

1132 else: 

1133 releaseLevel = ReleaseLevel.Final 

1134 

1135 version = cls( 

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

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

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

1139 level=releaseLevel, 

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

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

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

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

1144 postfix=match["postfix"], 

1145 prefix=match["prefix"], 

1146 # hash=match["hash"], 

1147 flags=Flags.Clean 

1148 ) 

1149 

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

1151 # TODO: VersionValidatorException 

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

1153 

1154 return version 

1155 

1156 @readonly 

1157 def Patch(self) -> int: 

1158 """ 

1159 Read-only property to access the patch number. 

1160 

1161 The patch number is identical to the micro number. 

1162 

1163 :return: The patch number. 

1164 """ 

1165 return self._micro 

1166 

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

1168 """ 

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

1170 

1171 :param left: Left operand. 

1172 :param right: Right operand. 

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

1174 """ 

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

1176 

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

1178 """ 

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

1180 

1181 :param left: Left operand. 

1182 :param right: Right operand. 

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

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

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

1186 """ 

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

1188 

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

1190 """ 

1191 Compare two version numbers for equality. 

1192 

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

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

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

1196 

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

1198 number. 

1199 

1200 :param other: Operand to compare against. 

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

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

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

1204 """ 

1205 return super().__eq__(other) 

1206 

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

1208 """ 

1209 Compare two version numbers for inequality. 

1210 

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

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

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

1214 

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

1216 number. 

1217 

1218 :param other: Operand to compare against. 

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

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

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

1222 """ 

1223 return super().__ne__(other) 

1224 

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

1226 """ 

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

1228 

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

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

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

1232 

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

1234 number. 

1235 

1236 :param other: Operand to compare against. 

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

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

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

1240 """ 

1241 return super().__lt__(other) 

1242 

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

1244 """ 

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

1246 

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

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

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

1250 

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

1252 number. 

1253 

1254 :param other: Operand to compare against. 

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

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

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

1258 """ 

1259 return super().__le__(other) 

1260 

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

1262 """ 

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

1264 

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

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

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

1268 

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

1270 number. 

1271 

1272 :param other: Operand to compare against. 

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

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

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

1276 """ 

1277 return super().__gt__(other) 

1278 

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

1280 """ 

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

1282 

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

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

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

1286 

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

1288 number. 

1289 

1290 :param other: Operand to compare against. 

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

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

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

1294 """ 

1295 return super().__ge__(other) 

1296 

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

1298 return super().__rshift__(other) 

1299 

1300 def __hash__(self) -> int: 

1301 return super().__hash__() 

1302 

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

1304 result = self._format(formatSpec) 

1305 

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

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

1308 

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

1310 

1311 def __repr__(self) -> str: 

1312 """ 

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

1314 

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

1316 """ 

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

1318 

1319 def __str__(self) -> str: 

1320 """ 

1321 Return a string representation of this version number. 

1322 

1323 :returns: Version number representation. 

1324 """ 

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

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

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

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

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

1330 if self._releaseLevel is ReleaseLevel.Development: 

1331 result += "-dev" 

1332 elif self._releaseLevel is ReleaseLevel.Alpha: 

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

1334 elif self._releaseLevel is ReleaseLevel.Beta: 

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

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

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

1338 elif self._releaseLevel is ReleaseLevel.ReleaseCandidate: 

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

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

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

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

1343 

1344 return result 

1345 

1346 

1347@export 

1348class PythonVersion(SemanticVersion): 

1349 """ 

1350 Represents a Python version. 

1351 """ 

1352 

1353 @classmethod 

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

1355 """ 

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

1357 

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

1359 """ 

1360 from sys import version_info 

1361 

1362 if version_info.releaselevel == "final": 

1363 rl = ReleaseLevel.Final 

1364 number = None 

1365 else: # pragma: no cover 

1366 number = version_info.serial 

1367 

1368 if version_info.releaselevel == "alpha": 

1369 rl = ReleaseLevel.Alpha 

1370 elif version_info.releaselevel == "beta": 

1371 rl = ReleaseLevel.Beta 

1372 elif version_info.releaselevel == "candidate": 

1373 rl = ReleaseLevel.ReleaseCandidate 

1374 else: # pragma: no cover 

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

1376 

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

1378 

1379 def __hash__(self) -> int: 

1380 return super().__hash__() 

1381 

1382 def __str__(self) -> str: 

1383 """ 

1384 Return a string representation of this version number. 

1385 

1386 :returns: Version number representation. 

1387 """ 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1403 

1404 return result 

1405 

1406 

1407@export 

1408class CalendarVersion(Version): 

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

1410 

1411 def __init__( 

1412 self, 

1413 major: int, 

1414 minor: Nullable[int] = None, 

1415 micro: Nullable[int] = None, 

1416 build: Nullable[int] = None, 

1417 flags: Flags = Flags.Clean, 

1418 prefix: Nullable[str] = None, 

1419 postfix: Nullable[str] = None 

1420 ) -> None: 

1421 """ 

1422 Initializes a calendar version number representation. 

1423 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1441 """ 

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

1443 

1444 @classmethod 

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

1446 """ 

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

1448 

1449 :param versionString: The version string to parse. 

1450 :returns: An object representing a calendar version. 

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

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

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

1454 """ 

1455 parts = Parts.Unknown 

1456 

1457 if versionString is None: 

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

1459 elif not isinstance(versionString, str): 

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

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

1462 raise ex 

1463 elif versionString == "": 

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

1465 

1466 split = versionString.split(".") 

1467 length = len(split) 

1468 major = int(split[0]) 

1469 minor = 0 

1470 parts |= Parts.Major 

1471 

1472 if length >= 2: 

1473 minor = int(split[1]) 

1474 parts |= Parts.Minor 

1475 

1476 flags = Flags.Clean 

1477 

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

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

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

1481 

1482 return version 

1483 

1484 @property 

1485 def Year(self) -> int: 

1486 """ 

1487 Read-only property to access the year part. 

1488 

1489 :return: The year part. 

1490 """ 

1491 return self._major 

1492 

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

1494 """ 

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

1496 

1497 :param left: Left parameter. 

1498 :param right: Right parameter. 

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

1500 """ 

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

1502 

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

1504 """ 

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

1506 

1507 :param left: Left parameter. 

1508 :param right: Right parameter. 

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

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

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

1512 """ 

1513 if left._major < right._major: 

1514 return True 

1515 elif left._major > right._major: 

1516 return False 

1517 

1518 if left._minor < right._minor: 

1519 return True 

1520 elif left._minor > right._minor: 

1521 return False 

1522 

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

1524 return True 

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

1526 return False 

1527 

1528 return None 

1529 

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

1531 """ 

1532 Compare two version numbers for equality. 

1533 

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

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

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

1537 

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

1539 number. 

1540 

1541 :param other: Parameter to compare against. 

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

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

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

1545 """ 

1546 return super().__eq__(other) 

1547 

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

1549 """ 

1550 Compare two version numbers for inequality. 

1551 

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

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

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

1555 

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

1557 number. 

1558 

1559 :param other: Parameter to compare against. 

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

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

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

1563 """ 

1564 return super().__ne__(other) 

1565 

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

1567 """ 

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

1569 

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

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

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

1573 

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

1575 number. 

1576 

1577 :param other: Parameter to compare against. 

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

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

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

1581 """ 

1582 return super().__lt__(other) 

1583 

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

1585 """ 

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

1587 

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

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

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

1591 

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

1593 number. 

1594 

1595 :param other: Parameter to compare against. 

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

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

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

1599 """ 

1600 return super().__le__(other) 

1601 

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

1603 """ 

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

1605 

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

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

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

1609 

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

1611 number. 

1612 

1613 :param other: Parameter to compare against. 

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

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

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

1617 """ 

1618 return super().__gt__(other) 

1619 

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

1621 """ 

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

1623 

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

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

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

1627 

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

1629 number. 

1630 

1631 :param other: Parameter to compare against. 

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

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

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

1635 """ 

1636 return super().__ge__(other) 

1637 

1638 def __hash__(self) -> int: 

1639 return super().__hash__() 

1640 

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

1642 """ 

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

1644 

1645 .. topic:: Format Specifiers 

1646 

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

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

1649 

1650 :param formatSpec: The format specification. 

1651 :return: Formatted version number. 

1652 """ 

1653 if formatSpec == "": 

1654 return self.__str__() 

1655 

1656 result = formatSpec 

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

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

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

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

1661 

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

1663 

1664 def __repr__(self) -> str: 

1665 """ 

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

1667 

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

1669 """ 

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

1671 

1672 def __str__(self) -> str: 

1673 """ 

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

1675 

1676 :returns: Version number representation including a prefix. 

1677 """ 

1678 result = f"{self._major}" 

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

1680 

1681 return result 

1682 

1683 

1684@export 

1685class YearMonthVersion(CalendarVersion): 

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

1687 

1688 def __init__( 

1689 self, 

1690 year: int, 

1691 month: Nullable[int] = None, 

1692 build: Nullable[int] = None, 

1693 flags: Flags = Flags.Clean, 

1694 prefix: Nullable[str] = None, 

1695 postfix: Nullable[str] = None 

1696 ) -> None: 

1697 """ 

1698 Initializes a year-month version number representation. 

1699 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1716 """ 

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

1718 

1719 @property 

1720 def Month(self) -> int: 

1721 """ 

1722 Read-only property to access the month part. 

1723 

1724 :return: The month part. 

1725 """ 

1726 return self._minor 

1727 

1728 def __hash__(self) -> int: 

1729 return super().__hash__() 

1730 

1731 

1732@export 

1733class YearWeekVersion(CalendarVersion): 

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

1735 

1736 def __init__( 

1737 self, 

1738 year: int, 

1739 week: Nullable[int] = None, 

1740 build: Nullable[int] = None, 

1741 flags: Flags = Flags.Clean, 

1742 prefix: Nullable[str] = None, 

1743 postfix: Nullable[str] = None 

1744 ) -> None: 

1745 """ 

1746 Initializes a year-week version number representation. 

1747 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1764 """ 

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

1766 

1767 @property 

1768 def Week(self) -> int: 

1769 """ 

1770 Read-only property to access the week part. 

1771 

1772 :return: The week part. 

1773 """ 

1774 return self._minor 

1775 

1776 def __hash__(self) -> int: 

1777 return super().__hash__() 

1778 

1779 

1780@export 

1781class YearReleaseVersion(CalendarVersion): 

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

1783 

1784 def __init__( 

1785 self, 

1786 year: int, 

1787 release: Nullable[int] = None, 

1788 build: Nullable[int] = None, 

1789 flags: Flags = Flags.Clean, 

1790 prefix: Nullable[str] = None, 

1791 postfix: Nullable[str] = None 

1792 ) -> None: 

1793 """ 

1794 Initializes a year-release version number representation. 

1795 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1812 """ 

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

1814 

1815 @property 

1816 def Release(self) -> int: 

1817 """ 

1818 Read-only property to access the release number. 

1819 

1820 :return: The release number. 

1821 """ 

1822 return self._minor 

1823 

1824 def __hash__(self) -> int: 

1825 return super().__hash__() 

1826 

1827 

1828@export 

1829class YearMonthDayVersion(CalendarVersion): 

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

1831 

1832 def __init__( 

1833 self, 

1834 year: int, 

1835 month: Nullable[int] = None, 

1836 day: Nullable[int] = None, 

1837 build: Nullable[int] = None, 

1838 flags: Flags = Flags.Clean, 

1839 prefix: Nullable[str] = None, 

1840 postfix: Nullable[str] = None 

1841 ) -> None: 

1842 """ 

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

1844 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1862 """ 

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

1864 

1865 @property 

1866 def Month(self) -> int: 

1867 """ 

1868 Read-only property to access the month part. 

1869 

1870 :return: The month part. 

1871 """ 

1872 return self._minor 

1873 

1874 @property 

1875 def Day(self) -> int: 

1876 """ 

1877 Read-only property to access the day part. 

1878 

1879 :return: The day part. 

1880 """ 

1881 return self._micro 

1882 

1883 def __hash__(self) -> int: 

1884 return super().__hash__() 

1885 

1886 

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

1888 

1889@export 

1890class RangeBoundHandling(Flag): 

1891 """ 

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

1893 

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

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

1896 """ 

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

1898 LowerBoundInclusive = 0 #: Lower bound is inclusive. 

1899 UpperBoundInclusive = 0 #: Upper bound is inclusive. 

1900 LowerBoundExclusive = 1 #: Lower bound is exclusive. 

1901 UpperBoundExclusive = 2 #: Upper bound is exclusive. 

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

1903 

1904 

1905@export 

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

1907 """ 

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

1909 

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

1911 """ 

1912 _lowerBound: V 

1913 _upperBound: V 

1914 _boundHandling: RangeBoundHandling 

1915 

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

1917 """ 

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

1919 

1920 :param lowerBound: lowest version (inclusive). 

1921 :param upperBound: hightest version (inclusive). 

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

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

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

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

1926 """ 

1927 if not isinstance(lowerBound, Version): 

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

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

1930 raise ex 

1931 

1932 if not isinstance(upperBound, Version): 

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

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

1935 raise ex 

1936 

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

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

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

1940 raise ex 

1941 

1942 if not (lowerBound <= upperBound): 

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

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

1945 raise ex 

1946 

1947 self._lowerBound = lowerBound 

1948 self._upperBound = upperBound 

1949 self._boundHandling = boundHandling 

1950 

1951 @property 

1952 def LowerBound(self) -> V: 

1953 """ 

1954 Property to access the range's lower bound. 

1955 

1956 :return: Lower bound of the version range. 

1957 """ 

1958 return self._lowerBound 

1959 

1960 @LowerBound.setter 

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

1962 if not isinstance(value, Version): 

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

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

1965 raise ex 

1966 

1967 self._lowerBound = value 

1968 

1969 @readonly 

1970 def UpperBound(self) -> V: 

1971 """ 

1972 Property to access the range's upper bound. 

1973 

1974 :return: Upper bound of the version range. 

1975 """ 

1976 return self._upperBound 

1977 

1978 @UpperBound.setter 

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

1980 if not isinstance(value, Version): 

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

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

1983 raise ex 

1984 

1985 self._upperBound = value 

1986 

1987 @readonly 

1988 def BoundHandling(self) -> RangeBoundHandling: 

1989 """ 

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

1991 

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

1993 """ 

1994 return self._boundHandling 

1995 

1996 @BoundHandling.setter 

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

1998 if not isinstance(value, RangeBoundHandling): 

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

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

2001 raise ex 

2002 

2003 self._boundHandling = value 

2004 

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

2006 """ 

2007 Compute the intersection of two version ranges. 

2008 

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

2010 :returns: Intersected version range. 

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

2012 :raises ValueError: If intersection is empty. 

2013 """ 

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

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

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

2017 raise ex 

2018 

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

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

2021 ex.add_note( 

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

2023 raise ex 

2024 

2025 if other._lowerBound < self._lowerBound: 

2026 lBound = self._lowerBound 

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

2028 lBound = other._lowerBound 

2029 else: 

2030 raise ValueError() 

2031 

2032 if other._upperBound > self._upperBound: 

2033 uBound = self._upperBound 

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

2035 uBound = other._upperBound 

2036 else: 

2037 raise ValueError() 

2038 

2039 return self.__class__(lBound, uBound) 

2040 

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

2042 """ 

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

2044 

2045 :param other: Operand to compare against. 

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

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

2048 """ 

2049 # TODO: support VersionRange < VersionRange too 

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

2051 if not isinstance(other, Version): 

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

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

2054 raise ex 

2055 

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

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

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

2059 raise ex 

2060 

2061 return self._upperBound < other 

2062 

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

2064 """ 

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

2066 

2067 :param other: Operand to compare against. 

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

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

2070 """ 

2071 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2076 raise ex 

2077 

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

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

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

2081 raise ex 

2082 

2083 if RangeBoundHandling.UpperBoundExclusive in self._boundHandling: 

2084 return self._upperBound < other 

2085 else: 

2086 return self._upperBound <= other 

2087 

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

2089 """ 

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

2091 

2092 :param other: Operand to compare against. 

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

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

2095 """ 

2096 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2101 raise ex 

2102 

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

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

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

2106 raise ex 

2107 

2108 return self._lowerBound > other 

2109 

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

2111 """ 

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

2113 

2114 :param other: Operand to compare against. 

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

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

2117 """ 

2118 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2123 raise ex 

2124 

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

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

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

2128 raise ex 

2129 

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

2131 return self._lowerBound > other 

2132 else: 

2133 return self._lowerBound >= other 

2134 

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

2136 """ 

2137 Check if the version is in the version range. 

2138 

2139 :param version: Version to check. 

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

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

2142 """ 

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

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

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

2146 raise ex 

2147 

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

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

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

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

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

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

2154 else: 

2155 return self._lowerBound < version < self._upperBound 

2156 

2157 

2158@export 

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

2160 """ 

2161 Representation of an ordered set of versions. 

2162 

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

2164 """ 

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

2166 

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

2168 """ 

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

2170 

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

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

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

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

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

2176 """ 

2177 if versions is None: 

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

2179 

2180 if isinstance(versions, Version): 

2181 self._items = [versions] 

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

2183 iterator = iter(versions) 

2184 try: 

2185 firstVersion = next(iterator) 

2186 except StopIteration: 

2187 self._items = [] 

2188 return 

2189 

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

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

2192 

2193 baseType = firstVersion.__class__ 

2194 for version in iterator: 

2195 if not isinstance(version, baseType): 

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

2197 

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

2199 else: 

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

2201 

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

2203 """ 

2204 Compute intersection of two version sets. 

2205 

2206 :param other: Second set of versions. 

2207 :returns: Intersection of two version sets. 

2208 """ 

2209 selfIterator = self.__iter__() 

2210 otherIterator = other.__iter__() 

2211 

2212 result = [] 

2213 try: 

2214 selfValue = next(selfIterator) 

2215 otherValue = next(otherIterator) 

2216 

2217 while True: 

2218 if selfValue < otherValue: 

2219 selfValue = next(selfIterator) 

2220 elif otherValue < selfValue: 

2221 otherValue = next(otherIterator) 

2222 else: 

2223 result.append(selfValue) 

2224 selfValue = next(selfIterator) 

2225 otherValue = next(otherIterator) 

2226 

2227 except StopIteration: 

2228 pass 

2229 

2230 return VersionSet(result) 

2231 

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

2233 """ 

2234 Compute union of two version sets. 

2235 

2236 :param other: Second set of versions. 

2237 :returns: Union of two version sets. 

2238 """ 

2239 selfIterator = self.__iter__() 

2240 otherIterator = other.__iter__() 

2241 

2242 result = [] 

2243 try: 

2244 selfValue = next(selfIterator) 

2245 except StopIteration: 

2246 for otherValue in otherIterator: 

2247 result.append(otherValue) 

2248 

2249 try: 

2250 otherValue = next(otherIterator) 

2251 except StopIteration: 

2252 for selfValue in selfIterator: 

2253 result.append(selfValue) 

2254 

2255 while True: 

2256 if selfValue < otherValue: 

2257 result.append(selfValue) 

2258 try: 

2259 selfValue = next(selfIterator) 

2260 except StopIteration: 

2261 result.append(otherValue) 

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

2263 result.append(otherValue) 

2264 

2265 break 

2266 elif otherValue < selfValue: 

2267 result.append(otherValue) 

2268 try: 

2269 otherValue = next(otherIterator) 

2270 except StopIteration: 

2271 result.append(selfValue) 

2272 for selfValue in selfIterator: 

2273 result.append(selfValue) 

2274 

2275 break 

2276 else: 

2277 result.append(selfValue) 

2278 try: 

2279 selfValue = next(selfIterator) 

2280 except StopIteration: 

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

2282 result.append(otherValue) 

2283 

2284 break 

2285 

2286 try: 

2287 otherValue = next(otherIterator) 

2288 except StopIteration: 

2289 for selfValue in selfIterator: 

2290 result.append(selfValue) 

2291 

2292 break 

2293 

2294 return VersionSet(result) 

2295 

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

2297 """ 

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

2299 

2300 :param other: Operand to compare against. 

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

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

2303 """ 

2304 # TODO: support VersionRange < VersionRange too 

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

2306 if not isinstance(other, Version): 

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

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

2309 raise ex 

2310 

2311 return self._items[-1] < other 

2312 

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

2314 """ 

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

2316 

2317 :param other: Operand to compare against. 

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

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

2320 """ 

2321 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2326 raise ex 

2327 

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

2329 

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

2331 """ 

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

2333 

2334 :param other: Operand to compare against. 

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

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

2337 """ 

2338 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2343 raise ex 

2344 

2345 return self._items[0] > other 

2346 

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

2348 """ 

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

2350 

2351 :param other: Operand to compare against. 

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

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

2354 """ 

2355 # TODO: support VersionRange < VersionRange too 

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

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

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

2359 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2360 raise ex 

2361 

2362 return self._items[0] >= other 

2363 

2364 def __contains__(self, version: V) -> bool: 

2365 """ 

2366 Checks if the version a member of the set. 

2367 

2368 :param version: The version to check. 

2369 :returns: ``True``, if the version is a member of the set. 

2370 """ 

2371 return version in self._items 

2372 

2373 def __len__(self) -> int: 

2374 """ 

2375 Returns the number of members in the set. 

2376 

2377 :returns: Number of set members. 

2378 """ 

2379 return len(self._items) 

2380 

2381 def __iter__(self) -> Iterator[V]: 

2382 """ 

2383 Returns an iterator to iterate all versions of this set from lowest to highest. 

2384 

2385 :returns: Iterator to iterate versions. 

2386 """ 

2387 return self._items.__iter__() 

2388 

2389 def __getitem__(self, index: int) -> V: 

2390 """ 

2391 Access to a version of a set by index. 

2392 

2393 :param index: The index of the version to access. 

2394 :returns: The indexed version. 

2395 

2396 .. hint:: 

2397 

2398 Versions are ordered from lowest to highest version number. 

2399 """ 

2400 return self._items[index]