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

974 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-21 22:22 +0000

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

2# _____ _ _ __ __ _ _ # 

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

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

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

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

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

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

9# Authors: # 

10# Patrick Lehmann # 

11# # 

12# License: # 

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

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

15# # 

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

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

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

19# # 

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

21# # 

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

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

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

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

26# limitations under the License. # 

27# # 

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

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

30# 

31""" 

32Implementation of semantic and date versioning version-numbers. 

33 

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

35""" 

36from collections.abc import Iterable as abc_Iterable 

37from enum import Flag, Enum 

38from re import compile as re_compile 

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

40 

41try: 

42 from pyTooling.Decorators import export, readonly 

43 from pyTooling.MetaClasses import ExtendedType, abstractmethod, mustoverride 

44 from pyTooling.Exceptions import ToolingException 

45 from pyTooling.Common import getFullyQualifiedName 

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

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

48 

49 try: 

50 from Decorators import export, readonly 

51 from MetaClasses import ExtendedType, abstractmethod, mustoverride 

52 from Exceptions import ToolingException 

53 from Common import getFullyQualifiedName 

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

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

56 raise ex 

57 

58 

59@export 

60class Parts(Flag): 

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

62 Unknown = 0 #: Undocumented 

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

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

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

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

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

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

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

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

71 Level = 8 #: Release level is present. 

72 Dev = 16 #: Development part is present. 

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

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

75 Prefix = 128 #: Prefix is present. 

76 Postfix = 256 #: Postfix is present. 

77 Hash = 512 #: Hash is present. 

78# AHead = 256 

79 

80 

81@export 

82class ReleaseLevel(Enum): 

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

84 Final = 0 #: 

85 ReleaseCandidate = -10 #: 

86 Development = -20 #: 

87 Gamma = -30 #: 

88 Beta = -40 #: 

89 Alpha = -50 #: 

90 

91 def __eq__(self, other: Any): 

92 """ 

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

94 

95 :param other: Operand to compare against. 

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

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

98 """ 

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

100 other = ReleaseLevel(other) 

101 

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

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

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

105 raise ex 

106 

107 return self is other 

108 

109 def __ne__(self, other: Any): 

110 """ 

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

112 

113 :param other: Operand to compare against. 

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

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

116 """ 

117 if isinstance(other, str): 

118 other = ReleaseLevel(other) 

119 

120 if not isinstance(other, ReleaseLevel): 

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

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

123 raise ex 

124 

125 return self is not other 

126 

127 def __lt__(self, other: Any): 

128 """ 

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

130 

131 :param other: Operand to compare against. 

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

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

134 """ 

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

136 other = ReleaseLevel(other) 

137 

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

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

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

141 raise ex 

142 

143 return self.value < other.value 

144 

145 def __le__(self, other: Any): 

146 """ 

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

148 

149 :param other: Operand to compare against. 

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

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

152 """ 

153 if isinstance(other, str): 

154 other = ReleaseLevel(other) 

155 

156 if not isinstance(other, ReleaseLevel): 

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

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

159 raise ex 

160 

161 return self.value <= other.value 

162 

163 def __gt__(self, other: Any): 

164 """ 

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

166 

167 :param other: Operand to compare against. 

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

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

170 """ 

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

172 other = ReleaseLevel(other) 

173 

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

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

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

177 raise ex 

178 

179 return self.value > other.value 

180 

181 def __ge__(self, other: Any): 

182 """ 

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

184 

185 :param other: Operand to compare against. 

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

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

188 """ 

189 if isinstance(other, str): 

190 other = ReleaseLevel(other) 

191 

192 if not isinstance(other, ReleaseLevel): 

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

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

195 raise ex 

196 

197 return self.value >= other.value 

198 

199 def __hash__(self) -> int: 

200 return hash(self.value) 

201 

202 def __str__(self) -> str: 

203 """ 

204 Returns the release level's string equivalent. 

205 

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

207 """ 

208 if self is ReleaseLevel.Final: 

209 return "final" 

210 elif self is ReleaseLevel.ReleaseCandidate: 

211 return "rc" 

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

213 return "dev" 

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

215 return "beta" 

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

217 return "alpha" 

218 

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

220 

221 

222@export 

223class Flags(Flag): 

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

225 NoVCS = 0 #: No Version Control System VCS 

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

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

228 

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

230 SVN = 32 #: Subversion (SVN) 

231 Git = 64 #: Git 

232 Hg = 128 #: Mercurial (Hg) 

233 

234 

235@export 

236def WordSizeValidator( 

237 bits: Nullable[int] = None, 

238 majorBits: Nullable[int] = None, 

239 minorBits: Nullable[int] = None, 

240 microBits: Nullable[int] = None, 

241 buildBits: Nullable[int] = None 

242): 

243 """ 

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

245 

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

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

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

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

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

251 :return: A validation function for Version instances. 

252 """ 

253 majorMax = minorMax = microMax = buildMax = -1 

254 if bits is not None: 

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

256 

257 if majorBits is not None: 

258 majorMax = 2**majorBits - 1 

259 if minorBits is not None: 

260 minorMax = 2**minorBits - 1 

261 if microBits is not None: 

262 microMax = 2 ** microBits - 1 

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

264 buildMax = 2**buildBits - 1 

265 

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

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

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

269 

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

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

272 

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

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

275 

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

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

278 

279 return True 

280 

281 return validator 

282 

283 

284@export 

285def MaxValueValidator( 

286 max: Nullable[int] = None, 

287 majorMax: Nullable[int] = None, 

288 minorMax: Nullable[int] = None, 

289 microMax: Nullable[int] = None, 

290 buildMax: Nullable[int] = None 

291): 

292 """ 

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

294 

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

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

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

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

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

300 :return: A validation function for Version instances. 

301 """ 

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

303 majorMax = minorMax = microMax = buildMax = max 

304 

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

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

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

308 

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

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

311 

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

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

314 

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

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

317 

318 return True 

319 

320 return validator 

321 

322 

323@export 

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

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

326 

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

328 

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

330 _prefix: str #: Prefix string 

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

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

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

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

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

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

337 _dev: int #: Development number 

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

339 _postfix: str #: Postfix string 

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

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

342 

343 def __init__( 

344 self, 

345 major: int, 

346 minor: Nullable[int] = None, 

347 micro: Nullable[int] = None, 

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

349 number: Nullable[int] = None, 

350 post: Nullable[int] = None, 

351 dev: Nullable[int] = None, 

352 *, 

353 build: Nullable[int] = None, 

354 postfix: Nullable[str] = None, 

355 prefix: Nullable[str] = None, 

356 hash: Nullable[str] = None, 

357 flags: Flags = Flags.NoVCS 

358 ) -> None: 

359 """ 

360 Initializes a version number representation. 

361 

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

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

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

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

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

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

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

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

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

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

372 :param hash: Postfix string. 

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

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

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

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

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

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

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

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

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

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

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

384 """ 

385 self.__hash = None 

386 

387 if not isinstance(major, int): 

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

389 elif major < 0: 

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

391 

392 self._parts = Parts.Major 

393 self._major = major 

394 

395 if minor is not None: 

396 if not isinstance(minor, int): 

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

398 elif minor < 0: 

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

400 

401 self._parts |= Parts.Minor 

402 self._minor = minor 

403 else: 

404 self._minor = 0 

405 

406 if micro is not None: 

407 if not isinstance(micro, int): 

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

409 elif micro < 0: 

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

411 

412 self._parts |= Parts.Micro 

413 self._micro = micro 

414 else: 

415 self._micro = 0 

416 

417 if level is None: 

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

419 elif not isinstance(level, ReleaseLevel): 

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

421 elif level is ReleaseLevel.Final: 

422 if number is not None: 

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

424 

425 self._parts |= Parts.Level 

426 self._releaseLevel = level 

427 self._releaseNumber = 0 

428 else: 

429 self._parts |= Parts.Level 

430 self._releaseLevel = level 

431 

432 if number is not None: 

433 if not isinstance(number, int): 

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

435 elif number < 0: 

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

437 

438 self._releaseNumber = number 

439 else: 

440 self._releaseNumber = 0 

441 

442 if dev is not None: 

443 if not isinstance(dev, int): 

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

445 elif dev < 0: 

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

447 

448 self._parts |= Parts.Dev 

449 self._dev = dev 

450 else: 

451 self._dev = 0 

452 

453 if post is not None: 

454 if not isinstance(post, int): 

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

456 elif post < 0: 

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

458 

459 self._parts |= Parts.Post 

460 self._post = post 

461 else: 

462 self._post = 0 

463 

464 if build is not None: 

465 if not isinstance(build, int): 

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

467 elif build < 0: 

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

469 

470 self._build = build 

471 self._parts |= Parts.Build 

472 else: 

473 self._build = 0 

474 

475 if postfix is not None: 

476 if not isinstance(postfix, str): 

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

478 

479 self._parts |= Parts.Postfix 

480 self._postfix = postfix 

481 else: 

482 self._postfix = "" 

483 

484 if prefix is not None: 

485 if not isinstance(prefix, str): 

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

487 

488 self._parts |= Parts.Prefix 

489 self._prefix = prefix 

490 else: 

491 self._prefix = "" 

492 

493 if hash is not None: 

494 if not isinstance(hash, str): 

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

496 

497 self._parts |= Parts.Hash 

498 self._hash = hash 

499 else: 

500 self._hash = "" 

501 

502 if flags is None: 

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

504 elif not isinstance(flags, Flags): 

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

506 

507 self._flags = flags 

508 

509 @classmethod 

510 @abstractmethod 

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

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

513 

514 @readonly 

515 def Parts(self) -> Parts: 

516 """ 

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

518 

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

520 """ 

521 return self._parts 

522 

523 @readonly 

524 def Prefix(self) -> str: 

525 """ 

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

527 

528 :return: The prefix of the version number. 

529 """ 

530 return self._prefix 

531 

532 @readonly 

533 def Major(self) -> int: 

534 """ 

535 Read-only property to access the major number. 

536 

537 :return: The major number. 

538 """ 

539 return self._major 

540 

541 @readonly 

542 def Minor(self) -> int: 

543 """ 

544 Read-only property to access the minor number. 

545 

546 :return: The minor number. 

547 """ 

548 return self._minor 

549 

550 @readonly 

551 def Micro(self) -> int: 

552 """ 

553 Read-only property to access the micro number. 

554 

555 :return: The micro number. 

556 """ 

557 return self._micro 

558 

559 @readonly 

560 def ReleaseLevel(self) -> ReleaseLevel: 

561 """ 

562 Read-only property to access the release level. 

563 

564 :return: The release level. 

565 """ 

566 return self._releaseLevel 

567 

568 @readonly 

569 def ReleaseNumber(self) -> int: 

570 """ 

571 Read-only property to access the release number. 

572 

573 :return: The release number. 

574 """ 

575 return self._releaseNumber 

576 

577 @readonly 

578 def Post(self) -> int: 

579 """ 

580 Read-only property to access the post number. 

581 

582 :return: The post number. 

583 """ 

584 return self._post 

585 

586 @readonly 

587 def Dev(self) -> int: 

588 """ 

589 Read-only property to access the development number. 

590 

591 :return: The development number. 

592 """ 

593 return self._dev 

594 

595 @readonly 

596 def Build(self) -> int: 

597 """ 

598 Read-only property to access the build number. 

599 

600 :return: The build number. 

601 """ 

602 return self._build 

603 

604 @readonly 

605 def Postfix(self) -> str: 

606 """ 

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

608 

609 :return: The postfix of the version number. 

610 """ 

611 return self._postfix 

612 

613 @readonly 

614 def Hash(self) -> str: 

615 """ 

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

617 

618 :return: The hash. 

619 """ 

620 return self._hash 

621 

622 @readonly 

623 def Flags(self) -> Flags: 

624 """ 

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

626 

627 :return: The flags of the version number. 

628 """ 

629 return self._flags 

630 

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

632 """ 

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

634 

635 :param left: Left operand. 

636 :param right: Right operand. 

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

638 """ 

639 return ( 

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

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

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

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

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

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

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

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

648 (left._postfix == right._postfix) 

649 ) 

650 

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

652 """ 

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

654 

655 :param left: Left operand. 

656 :param right: Right operand. 

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

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

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

660 """ 

661 if left._major < right._major: 

662 return True 

663 elif left._major > right._major: 

664 return False 

665 

666 if left._minor < right._minor: 

667 return True 

668 elif left._minor > right._minor: 

669 return False 

670 

671 if left._micro < right._micro: 

672 return True 

673 elif left._micro > right._micro: 

674 return False 

675 

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

677 return True 

678 elif left._releaseLevel > right._releaseLevel: 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._releaseNumber < right._releaseNumber: 681 ↛ 682line 681 didn't jump to line 682 because the condition on line 681 was never true

682 return True 

683 elif left._releaseNumber > right._releaseNumber: 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._post < right._post: 686 ↛ 687line 686 didn't jump to line 687 because the condition on line 686 was never true

687 return True 

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

689 return False 

690 

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

692 return True 

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

694 return False 

695 

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

697 return True 

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

699 return False 

700 

701 return None 

702 

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

704 exactMajor = Parts.Minor in expected._parts 

705 exactMinor = Parts.Micro in expected._parts 

706 

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

708 return False 

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

710 return False 

711 

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

713 return False 

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

715 return False 

716 

717 if Parts.Micro in expected._parts: 

718 return actual._micro >= expected._micro 

719 

720 return True 

721 

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

723 """ 

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

725 

726 .. topic:: Format Specifiers 

727 

728 * ``%p`` - prefix 

729 * ``%M`` - major number 

730 * ``%m`` - minor number 

731 * ``%u`` - micro number 

732 * ``%b`` - build number 

733 

734 :param formatSpec: The format specification. 

735 :return: Formatted version number. 

736 """ 

737 if formatSpec == "": 

738 return self.__str__() 

739 

740 result = formatSpec 

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

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

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

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

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

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

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

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

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

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

751 

752 return result 

753 

754 @mustoverride 

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

756 """ 

757 Compare two version numbers for equality. 

758 

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

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

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

762 

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

764 number. 

765 

766 :param other: Operand to compare against. 

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

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

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

770 """ 

771 if other is None: 

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

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

774 pass 

775 elif isinstance(other, str): 

776 other = self.__class__.Parse(other) 

777 elif isinstance(other, int): 

778 other = self.__class__(major=other) 

779 else: 

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

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

782 raise ex 

783 

784 return self._equal(self, other) 

785 

786 @mustoverride 

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

788 """ 

789 Compare two version numbers for inequality. 

790 

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

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

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

794 

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

796 number. 

797 

798 :param other: Operand to compare against. 

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

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

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

802 """ 

803 if other is None: 

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

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

806 pass 

807 elif isinstance(other, str): 

808 other = self.__class__.Parse(other) 

809 elif isinstance(other, int): 

810 other = self.__class__(major=other) 

811 else: 

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

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

814 raise ex 

815 

816 return not self._equal(self, other) 

817 

818 @mustoverride 

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

820 """ 

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

822 

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

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

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

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

827 

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

829 number. 

830 

831 :param other: Operand to compare against. 

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

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

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

835 """ 

836 if other is None: 

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

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

839 pass 

840 elif isinstance(other, VersionRange): 

841 other = other._lowerBound 

842 elif isinstance(other, VersionSet): 

843 other = other._items[0] 

844 elif isinstance(other, str): 

845 other = self.__class__.Parse(other) 

846 elif isinstance(other, int): 

847 other = self.__class__(major=other) 

848 else: 

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

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

851 raise ex 

852 

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

854 

855 @mustoverride 

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

857 """ 

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

859 

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

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

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

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

864 

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

866 number. 

867 

868 :param other: Operand to compare against. 

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

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

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

872 """ 

873 equalValue = True 

874 if other is None: 

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

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

877 pass 

878 elif isinstance(other, VersionRange): 

879 equalValue = RangeBoundHandling.LowerBoundExclusive not in other._boundHandling 

880 other = other._lowerBound 

881 elif isinstance(other, VersionSet): 

882 other = other._items[0] 

883 elif isinstance(other, str): 

884 other = self.__class__.Parse(other) 

885 elif isinstance(other, int): 

886 other = self.__class__(major=other) 

887 else: 

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

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

890 raise ex 

891 

892 result = self._compare(self, other) 

893 return result if result is not None else equalValue 

894 

895 @mustoverride 

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

897 """ 

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

899 

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

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

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

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

904 

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

906 number. 

907 

908 :param other: Operand to compare against. 

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

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

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

912 """ 

913 if other is None: 

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

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

916 pass 

917 elif isinstance(other, VersionRange): 

918 other = other._upperBound 

919 elif isinstance(other, VersionSet): 

920 other = other._items[-1] 

921 elif isinstance(other, str): 

922 other = self.__class__.Parse(other) 

923 elif isinstance(other, int): 

924 other = self.__class__(major=other) 

925 else: 

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

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

928 raise ex 

929 

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

931 

932 @mustoverride 

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

934 """ 

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

936 

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

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

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

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

941 

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

943 number. 

944 

945 :param other: Operand to compare against. 

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

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

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

949 """ 

950 equalValue = True 

951 if other is None: 

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

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

954 pass 

955 elif isinstance(other, VersionRange): 

956 equalValue = RangeBoundHandling.UpperBoundExclusive not in other._boundHandling 

957 other = other._upperBound 

958 elif isinstance(other, VersionSet): 

959 other = other._items[-1] 

960 elif isinstance(other, str): 

961 other = self.__class__.Parse(other) 

962 elif isinstance(other, int): 

963 other = self.__class__(major=other) 

964 else: 

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

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

967 raise ex 

968 

969 result = self._compare(self, other) 

970 return not result if result is not None else equalValue 

971 

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

973 if other is None: 

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

975 elif isinstance(other, self.__class__): 

976 pass 

977 elif isinstance(other, str): 

978 other = self.__class__.Parse(other) 

979 elif isinstance(other, int): 

980 other = self.__class__(major=other) 

981 else: 

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

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

984 raise ex 

985 

986 return self._minimum(self, other) 

987 

988 def __hash__(self) -> int: 

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

990 self.__hash = hash(( 

991 self._prefix, 

992 self._major, 

993 self._minor, 

994 self._micro, 

995 self._releaseLevel, 

996 self._releaseNumber, 

997 self._post, 

998 self._dev, 

999 self._build, 

1000 self._postfix, 

1001 self._hash, 

1002 self._flags 

1003 )) 

1004 return self.__hash 

1005 

1006 

1007@export 

1008class SemanticVersion(Version): 

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

1010 

1011 _PATTERN = re_compile( 

1012 r"^" 

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

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

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

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

1017 r"(?:" 

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

1019 r"|" 

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

1021 r"|" 

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

1023 r")?" 

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

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

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

1027 r"$" 

1028 ) 

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

1030# ahead: int = 0 

1031 

1032 def __init__( 

1033 self, 

1034 major: int, 

1035 minor: Nullable[int] = None, 

1036 micro: Nullable[int] = None, 

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

1038 number: Nullable[int] = None, 

1039 post: Nullable[int] = None, 

1040 dev: Nullable[int] = None, 

1041 *, 

1042 build: Nullable[int] = None, 

1043 postfix: Nullable[str] = None, 

1044 prefix: Nullable[str] = None, 

1045 hash: Nullable[str] = None, 

1046 flags: Flags = Flags.NoVCS 

1047 ) -> None: 

1048 """ 

1049 Initializes a semantic version number representation. 

1050 

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

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

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

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

1055 :param level: tbd 

1056 :param number: tbd 

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

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

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

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

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

1062 :param hash: tbd 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1077 """ 

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

1079 

1080 @classmethod 

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

1082 """ 

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

1084 

1085 Allowed prefix characters: 

1086 

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

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

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

1090 * ``rev|REV`` - revision 

1091 

1092 :param versionString: The version string to parse. 

1093 :returns: An object representing a semantic version. 

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

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

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

1097 """ 

1098 if versionString is None: 

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

1100 elif not isinstance(versionString, str): 

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

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

1103 raise ex 

1104 

1105 versionString = versionString.strip() 

1106 if versionString == "": 

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

1108 

1109 match = cls._PATTERN.match(versionString) 

1110 if match is None: 

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

1112 

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

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

1115 return None 

1116 try: 

1117 return int(value) 

1118 except ValueError as ex: # pragma: no cover 

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

1120 

1121 release = match["release"] 

1122 if release is not None: 

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

1124 releaseLevel = ReleaseLevel.Development 

1125 elif release == "final": 

1126 releaseLevel = ReleaseLevel.Final 

1127 else: # pragma: no cover 

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

1129 else: 

1130 level = match["level"] 

1131 if level is not None: 

1132 level = level.lower() 

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

1134 releaseLevel = ReleaseLevel.Alpha 

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

1136 releaseLevel = ReleaseLevel.Beta 

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

1138 releaseLevel = ReleaseLevel.Gamma 

1139 elif level == "rc": 

1140 releaseLevel = ReleaseLevel.ReleaseCandidate 

1141 else: # pragma: no cover 

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

1143 else: 

1144 releaseLevel = ReleaseLevel.Final 

1145 

1146 version = cls( 

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

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

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

1150 level=releaseLevel, 

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

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

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

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

1155 postfix=match["postfix"], 

1156 prefix=match["prefix"], 

1157 # hash=match["hash"], 

1158 flags=Flags.Clean 

1159 ) 

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

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

1162 

1163 return version 

1164 

1165 @readonly 

1166 def Patch(self) -> int: 

1167 """ 

1168 Read-only property to access the patch number. 

1169 

1170 The patch number is identical to the micro number. 

1171 

1172 :return: The patch number. 

1173 """ 

1174 return self._micro 

1175 

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

1177 """ 

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

1179 

1180 :param left: Left operand. 

1181 :param right: Right operand. 

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

1183 """ 

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

1185 

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

1187 """ 

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

1189 

1190 :param left: Left operand. 

1191 :param right: Right operand. 

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

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

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

1195 """ 

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

1197 

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

1199 """ 

1200 Compare two version numbers for equality. 

1201 

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

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

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

1205 

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

1207 number. 

1208 

1209 :param other: Operand to compare against. 

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

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

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

1213 """ 

1214 return super().__eq__(other) 

1215 

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

1217 """ 

1218 Compare two version numbers for inequality. 

1219 

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

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

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

1223 

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

1225 number. 

1226 

1227 :param other: Operand to compare against. 

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

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

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

1231 """ 

1232 return super().__ne__(other) 

1233 

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

1235 """ 

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

1237 

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

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

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

1241 

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

1243 number. 

1244 

1245 :param other: Operand to compare against. 

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

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

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

1249 """ 

1250 return super().__lt__(other) 

1251 

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

1253 """ 

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

1255 

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

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

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

1259 

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

1261 number. 

1262 

1263 :param other: Operand to compare against. 

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

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

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

1267 """ 

1268 return super().__le__(other) 

1269 

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

1271 """ 

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

1273 

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

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

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

1277 

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

1279 number. 

1280 

1281 :param other: Operand to compare against. 

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

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

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

1285 """ 

1286 return super().__gt__(other) 

1287 

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

1289 """ 

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

1291 

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

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

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

1295 

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

1297 number. 

1298 

1299 :param other: Operand to compare against. 

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

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

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

1303 """ 

1304 return super().__ge__(other) 

1305 

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

1307 return super().__rshift__(other) 

1308 

1309 def __hash__(self) -> int: 

1310 return super().__hash__() 

1311 

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

1313 result = self._format(formatSpec) 

1314 

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

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

1317 

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

1319 

1320 def __repr__(self) -> str: 

1321 """ 

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

1323 

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

1325 """ 

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

1327 

1328 def __str__(self) -> str: 

1329 """ 

1330 Return a string representation of this version number. 

1331 

1332 :returns: Version number representation. 

1333 """ 

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

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

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

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

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

1339 if self._releaseLevel is ReleaseLevel.Development: 

1340 result += "-dev" 

1341 elif self._releaseLevel is ReleaseLevel.Alpha: 

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

1343 elif self._releaseLevel is ReleaseLevel.Beta: 

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

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

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

1347 elif self._releaseLevel is ReleaseLevel.ReleaseCandidate: 

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

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

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

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

1352 

1353 return result 

1354 

1355 

1356@export 

1357class PythonVersion(SemanticVersion): 

1358 """ 

1359 Represents a Python version. 

1360 """ 

1361 

1362 @classmethod 

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

1364 """ 

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

1366 

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

1368 """ 

1369 from sys import version_info 

1370 

1371 if version_info.releaselevel == "final": 

1372 rl = ReleaseLevel.Final 

1373 number = None 

1374 else: # pragma: no cover 

1375 number = version_info.serial 

1376 

1377 if version_info.releaselevel == "alpha": 

1378 rl = ReleaseLevel.Alpha 

1379 elif version_info.releaselevel == "beta": 

1380 rl = ReleaseLevel.Beta 

1381 elif version_info.releaselevel == "candidate": 

1382 rl = ReleaseLevel.ReleaseCandidate 

1383 else: # pragma: no cover 

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

1385 

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

1387 

1388 def __hash__(self) -> int: 

1389 return super().__hash__() 

1390 

1391 def __str__(self) -> str: 

1392 """ 

1393 Return a string representation of this version number. 

1394 

1395 :returns: Version number representation. 

1396 """ 

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

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

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

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

1401 if self._releaseLevel is ReleaseLevel.Alpha: 

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

1403 elif self._releaseLevel is ReleaseLevel.Beta: 

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

1405 elif self._releaseLevel is ReleaseLevel.Gamma: 

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

1407 elif self._releaseLevel is ReleaseLevel.ReleaseCandidate: 

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

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

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

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

1412 

1413 return result 

1414 

1415 

1416@export 

1417class CalendarVersion(Version): 

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

1419 

1420 def __init__( 

1421 self, 

1422 major: int, 

1423 minor: Nullable[int] = None, 

1424 micro: Nullable[int] = None, 

1425 build: Nullable[int] = None, 

1426 flags: Flags = Flags.Clean, 

1427 prefix: Nullable[str] = None, 

1428 postfix: Nullable[str] = None 

1429 ) -> None: 

1430 """ 

1431 Initializes a calendar version number representation. 

1432 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1450 """ 

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

1452 

1453 @classmethod 

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

1455 """ 

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

1457 

1458 :param versionString: The version string to parse. 

1459 :returns: An object representing a calendar version. 

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

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

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

1463 """ 

1464 parts = Parts.Unknown 

1465 

1466 if versionString is None: 

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

1468 elif not isinstance(versionString, str): 

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

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

1471 raise ex 

1472 elif versionString == "": 

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

1474 

1475 split = versionString.split(".") 

1476 length = len(split) 

1477 major = int(split[0]) 

1478 minor = 0 

1479 parts |= Parts.Major 

1480 

1481 if length >= 2: 

1482 minor = int(split[1]) 

1483 parts |= Parts.Minor 

1484 

1485 flags = Flags.Clean 

1486 

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

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

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

1490 

1491 return version 

1492 

1493 @property 

1494 def Year(self) -> int: 

1495 """ 

1496 Read-only property to access the year part. 

1497 

1498 :return: The year part. 

1499 """ 

1500 return self._major 

1501 

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

1503 """ 

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

1505 

1506 :param left: Left parameter. 

1507 :param right: Right parameter. 

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

1509 """ 

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

1511 

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

1513 """ 

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

1515 

1516 :param left: Left parameter. 

1517 :param right: Right parameter. 

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

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

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

1521 """ 

1522 if left._major < right._major: 

1523 return True 

1524 elif left._major > right._major: 

1525 return False 

1526 

1527 if left._minor < right._minor: 

1528 return True 

1529 elif left._minor > right._minor: 

1530 return False 

1531 

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

1533 return True 

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

1535 return False 

1536 

1537 return None 

1538 

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

1540 """ 

1541 Compare two version numbers for equality. 

1542 

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

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

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

1546 

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

1548 number. 

1549 

1550 :param other: Parameter to compare against. 

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

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

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

1554 """ 

1555 return super().__eq__(other) 

1556 

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

1558 """ 

1559 Compare two version numbers for inequality. 

1560 

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

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

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

1564 

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

1566 number. 

1567 

1568 :param other: Parameter to compare against. 

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

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

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

1572 """ 

1573 return super().__ne__(other) 

1574 

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

1576 """ 

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

1578 

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

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

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

1582 

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

1584 number. 

1585 

1586 :param other: Parameter to compare against. 

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

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

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

1590 """ 

1591 return super().__lt__(other) 

1592 

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

1594 """ 

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

1596 

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

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

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

1600 

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

1602 number. 

1603 

1604 :param other: Parameter to compare against. 

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

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

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

1608 """ 

1609 return super().__le__(other) 

1610 

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

1612 """ 

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

1614 

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

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

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

1618 

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

1620 number. 

1621 

1622 :param other: Parameter to compare against. 

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

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

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

1626 """ 

1627 return super().__gt__(other) 

1628 

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

1630 """ 

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

1632 

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

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

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

1636 

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

1638 number. 

1639 

1640 :param other: Parameter to compare against. 

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

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

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

1644 """ 

1645 return super().__ge__(other) 

1646 

1647 def __hash__(self) -> int: 

1648 return super().__hash__() 

1649 

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

1651 """ 

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

1653 

1654 .. topic:: Format Specifiers 

1655 

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

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

1658 

1659 :param formatSpec: The format specification. 

1660 :return: Formatted version number. 

1661 """ 

1662 if formatSpec == "": 

1663 return self.__str__() 

1664 

1665 result = formatSpec 

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

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

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

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

1670 

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

1672 

1673 def __repr__(self) -> str: 

1674 """ 

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

1676 

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

1678 """ 

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

1680 

1681 def __str__(self) -> str: 

1682 """ 

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

1684 

1685 :returns: Version number representation including a prefix. 

1686 """ 

1687 result = f"{self._major}" 

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

1689 

1690 return result 

1691 

1692 

1693@export 

1694class YearMonthVersion(CalendarVersion): 

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

1696 

1697 def __init__( 

1698 self, 

1699 year: int, 

1700 month: Nullable[int] = None, 

1701 build: Nullable[int] = None, 

1702 flags: Flags = Flags.Clean, 

1703 prefix: Nullable[str] = None, 

1704 postfix: Nullable[str] = None 

1705 ) -> None: 

1706 """ 

1707 Initializes a year-month version number representation. 

1708 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1725 """ 

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

1727 

1728 @property 

1729 def Month(self) -> int: 

1730 """ 

1731 Read-only property to access the month part. 

1732 

1733 :return: The month part. 

1734 """ 

1735 return self._minor 

1736 

1737 def __hash__(self) -> int: 

1738 return super().__hash__() 

1739 

1740 

1741@export 

1742class YearWeekVersion(CalendarVersion): 

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

1744 

1745 def __init__( 

1746 self, 

1747 year: int, 

1748 week: Nullable[int] = None, 

1749 build: Nullable[int] = None, 

1750 flags: Flags = Flags.Clean, 

1751 prefix: Nullable[str] = None, 

1752 postfix: Nullable[str] = None 

1753 ) -> None: 

1754 """ 

1755 Initializes a year-week version number representation. 

1756 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1773 """ 

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

1775 

1776 @property 

1777 def Week(self) -> int: 

1778 """ 

1779 Read-only property to access the week part. 

1780 

1781 :return: The week part. 

1782 """ 

1783 return self._minor 

1784 

1785 def __hash__(self) -> int: 

1786 return super().__hash__() 

1787 

1788 

1789@export 

1790class YearReleaseVersion(CalendarVersion): 

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

1792 

1793 def __init__( 

1794 self, 

1795 year: int, 

1796 release: Nullable[int] = None, 

1797 build: Nullable[int] = None, 

1798 flags: Flags = Flags.Clean, 

1799 prefix: Nullable[str] = None, 

1800 postfix: Nullable[str] = None 

1801 ) -> None: 

1802 """ 

1803 Initializes a year-release version number representation. 

1804 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1821 """ 

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

1823 

1824 @property 

1825 def Release(self) -> int: 

1826 """ 

1827 Read-only property to access the release number. 

1828 

1829 :return: The release number. 

1830 """ 

1831 return self._minor 

1832 

1833 def __hash__(self) -> int: 

1834 return super().__hash__() 

1835 

1836 

1837@export 

1838class YearMonthDayVersion(CalendarVersion): 

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

1840 

1841 def __init__( 

1842 self, 

1843 year: int, 

1844 month: Nullable[int] = None, 

1845 day: Nullable[int] = None, 

1846 build: Nullable[int] = None, 

1847 flags: Flags = Flags.Clean, 

1848 prefix: Nullable[str] = None, 

1849 postfix: Nullable[str] = None 

1850 ) -> None: 

1851 """ 

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

1853 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1871 """ 

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

1873 

1874 @property 

1875 def Month(self) -> int: 

1876 """ 

1877 Read-only property to access the month part. 

1878 

1879 :return: The month part. 

1880 """ 

1881 return self._minor 

1882 

1883 @property 

1884 def Day(self) -> int: 

1885 """ 

1886 Read-only property to access the day part. 

1887 

1888 :return: The day part. 

1889 """ 

1890 return self._micro 

1891 

1892 def __hash__(self) -> int: 

1893 return super().__hash__() 

1894 

1895 

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

1897 

1898@export 

1899class RangeBoundHandling(Flag): 

1900 """ 

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

1902 

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

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

1905 """ 

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

1907 LowerBoundInclusive = 0 #: Lower bound is inclusive. 

1908 UpperBoundInclusive = 0 #: Upper bound is inclusive. 

1909 LowerBoundExclusive = 1 #: Lower bound is exclusive. 

1910 UpperBoundExclusive = 2 #: Upper bound is exclusive. 

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

1912 

1913 

1914@export 

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

1916 """ 

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

1918 

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

1920 """ 

1921 _lowerBound: V 

1922 _upperBound: V 

1923 _boundHandling: RangeBoundHandling 

1924 

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

1926 """ 

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

1928 

1929 :param lowerBound: lowest version (inclusive). 

1930 :param upperBound: hightest version (inclusive). 

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

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

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

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

1935 """ 

1936 if not isinstance(lowerBound, Version): 

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

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

1939 raise ex 

1940 

1941 if not isinstance(upperBound, Version): 

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

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

1944 raise ex 

1945 

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

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

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

1949 raise ex 

1950 

1951 if not (lowerBound <= upperBound): 

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

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

1954 raise ex 

1955 

1956 self._lowerBound = lowerBound 

1957 self._upperBound = upperBound 

1958 self._boundHandling = boundHandling 

1959 

1960 @property 

1961 def LowerBound(self) -> V: 

1962 """ 

1963 Property to access the range's lower bound. 

1964 

1965 :return: Lower bound of the version range. 

1966 """ 

1967 return self._lowerBound 

1968 

1969 @LowerBound.setter 

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

1971 if not isinstance(value, Version): 

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

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

1974 raise ex 

1975 

1976 self._lowerBound = value 

1977 

1978 @readonly 

1979 def UpperBound(self) -> V: 

1980 """ 

1981 Property to access the range's upper bound. 

1982 

1983 :return: Upper bound of the version range. 

1984 """ 

1985 return self._upperBound 

1986 

1987 @UpperBound.setter 

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

1989 if not isinstance(value, Version): 

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

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

1992 raise ex 

1993 

1994 self._upperBound = value 

1995 

1996 @readonly 

1997 def BoundHandling(self) -> RangeBoundHandling: 

1998 """ 

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

2000 

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

2002 """ 

2003 return self._boundHandling 

2004 

2005 @BoundHandling.setter 

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

2007 if not isinstance(value, RangeBoundHandling): 

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

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

2010 raise ex 

2011 

2012 self._boundHandling = value 

2013 

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

2015 """ 

2016 Compute the intersection of two version ranges. 

2017 

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

2019 :returns: Intersected version range. 

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

2021 :raises ValueError: If intersection is empty. 

2022 """ 

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

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

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

2026 raise ex 

2027 

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

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

2030 ex.add_note( 

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

2032 raise ex 

2033 

2034 if other._lowerBound < self._lowerBound: 

2035 lBound = self._lowerBound 

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

2037 lBound = other._lowerBound 

2038 else: 

2039 raise ValueError() 

2040 

2041 if other._upperBound > self._upperBound: 

2042 uBound = self._upperBound 

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

2044 uBound = other._upperBound 

2045 else: 

2046 raise ValueError() 

2047 

2048 return self.__class__(lBound, uBound) 

2049 

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

2051 """ 

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

2053 

2054 :param other: Operand to compare against. 

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

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

2057 """ 

2058 # TODO: support VersionRange < VersionRange too 

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

2060 if not isinstance(other, Version): 

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

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

2063 raise ex 

2064 

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

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

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

2068 raise ex 

2069 

2070 return self._upperBound < other 

2071 

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

2073 """ 

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

2075 

2076 :param other: Operand to compare against. 

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

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

2079 """ 

2080 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2085 raise ex 

2086 

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

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

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

2090 raise ex 

2091 

2092 if RangeBoundHandling.UpperBoundExclusive in self._boundHandling: 

2093 return self._upperBound < other 

2094 else: 

2095 return self._upperBound <= other 

2096 

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

2098 """ 

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

2100 

2101 :param other: Operand to compare against. 

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

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

2104 """ 

2105 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2110 raise ex 

2111 

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

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

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

2115 raise ex 

2116 

2117 return self._lowerBound > other 

2118 

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

2120 """ 

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

2122 

2123 :param other: Operand to compare against. 

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

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

2126 """ 

2127 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2132 raise ex 

2133 

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

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

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

2137 raise ex 

2138 

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

2140 return self._lowerBound > other 

2141 else: 

2142 return self._lowerBound >= other 

2143 

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

2145 """ 

2146 Check if the version is in the version range. 

2147 

2148 :param version: Version to check. 

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

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

2151 """ 

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

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

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

2155 raise ex 

2156 

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

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

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

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

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

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

2163 else: 

2164 return self._lowerBound < version < self._upperBound 

2165 

2166 

2167@export 

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

2169 """ 

2170 Representation of an ordered set of versions. 

2171 

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

2173 """ 

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

2175 

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

2177 """ 

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

2179 

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

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

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

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

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

2185 """ 

2186 if versions is None: 

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

2188 

2189 if isinstance(versions, Version): 

2190 self._items = [versions] 

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

2192 iterator = iter(versions) 

2193 try: 

2194 firstVersion = next(iterator) 

2195 except StopIteration: 

2196 self._items = [] 

2197 return 

2198 

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

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

2201 

2202 baseType = firstVersion.__class__ 

2203 for version in iterator: 

2204 if not isinstance(version, baseType): 

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

2206 

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

2208 else: 

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

2210 

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

2212 """ 

2213 Compute intersection of two version sets. 

2214 

2215 :param other: Second set of versions. 

2216 :returns: Intersection of two version sets. 

2217 """ 

2218 selfIterator = self.__iter__() 

2219 otherIterator = other.__iter__() 

2220 

2221 result = [] 

2222 try: 

2223 selfValue = next(selfIterator) 

2224 otherValue = next(otherIterator) 

2225 

2226 while True: 

2227 if selfValue < otherValue: 

2228 selfValue = next(selfIterator) 

2229 elif otherValue < selfValue: 

2230 otherValue = next(otherIterator) 

2231 else: 

2232 result.append(selfValue) 

2233 selfValue = next(selfIterator) 

2234 otherValue = next(otherIterator) 

2235 

2236 except StopIteration: 

2237 pass 

2238 

2239 return VersionSet(result) 

2240 

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

2242 """ 

2243 Compute union of two version sets. 

2244 

2245 :param other: Second set of versions. 

2246 :returns: Union of two version sets. 

2247 """ 

2248 selfIterator = self.__iter__() 

2249 otherIterator = other.__iter__() 

2250 

2251 result = [] 

2252 try: 

2253 selfValue = next(selfIterator) 

2254 except StopIteration: 

2255 for otherValue in otherIterator: 

2256 result.append(otherValue) 

2257 

2258 try: 

2259 otherValue = next(otherIterator) 

2260 except StopIteration: 

2261 for selfValue in selfIterator: 

2262 result.append(selfValue) 

2263 

2264 while True: 

2265 if selfValue < otherValue: 

2266 result.append(selfValue) 

2267 try: 

2268 selfValue = next(selfIterator) 

2269 except StopIteration: 

2270 result.append(otherValue) 

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

2272 result.append(otherValue) 

2273 

2274 break 

2275 elif otherValue < selfValue: 

2276 result.append(otherValue) 

2277 try: 

2278 otherValue = next(otherIterator) 

2279 except StopIteration: 

2280 result.append(selfValue) 

2281 for selfValue in selfIterator: 

2282 result.append(selfValue) 

2283 

2284 break 

2285 else: 

2286 result.append(selfValue) 

2287 try: 

2288 selfValue = next(selfIterator) 

2289 except StopIteration: 

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

2291 result.append(otherValue) 

2292 

2293 break 

2294 

2295 try: 

2296 otherValue = next(otherIterator) 

2297 except StopIteration: 

2298 for selfValue in selfIterator: 

2299 result.append(selfValue) 

2300 

2301 break 

2302 

2303 return VersionSet(result) 

2304 

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

2306 """ 

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

2308 

2309 :param other: Operand to compare against. 

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

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

2312 """ 

2313 # TODO: support VersionRange < VersionRange too 

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

2315 if not isinstance(other, Version): 

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

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

2318 raise ex 

2319 

2320 return self._items[-1] < other 

2321 

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

2323 """ 

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

2325 

2326 :param other: Operand to compare against. 

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

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

2329 """ 

2330 # TODO: support VersionRange < VersionRange too 

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

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

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

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

2335 raise ex 

2336 

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

2338 

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

2340 """ 

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

2342 

2343 :param other: Operand to compare against. 

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

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

2346 """ 

2347 # TODO: support VersionRange < VersionRange too 

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

2349 if not isinstance(other, Version): 2349 ↛ 2350line 2349 didn't jump to line 2350 because the condition on line 2349 was never true

2350 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2351 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2352 raise ex 

2353 

2354 return self._items[0] > other 

2355 

2356 def __ge__(self, other: Any) -> bool: 

2357 """ 

2358 Compare a version set and a version numbers if the version set is greater than or equal the second operand (version). 

2359 

2360 :param other: Operand to compare against. 

2361 :returns: ``True``, if version set is greater than or equal the second operand (version). 

2362 :raises TypeError: If parameter ``other`` is not of type :class:`Version`. 

2363 """ 

2364 # TODO: support VersionRange < VersionRange too 

2365 # TODO: support str, int, ... like Version ? 

2366 if not isinstance(other, Version): 2366 ↛ 2367line 2366 didn't jump to line 2367 because the condition on line 2366 was never true

2367 ex = TypeError(f"Parameter 'other' is not of type 'Version'.") 

2368 ex.add_note(f"Got type '{getFullyQualifiedName(other)}'.") 

2369 raise ex 

2370 

2371 return self._items[0] >= other 

2372 

2373 def __contains__(self, version: V) -> bool: 

2374 """ 

2375 Checks if the version a member of the set. 

2376 

2377 :param version: The version to check. 

2378 :returns: ``True``, if the version is a member of the set. 

2379 """ 

2380 return version in self._items 

2381 

2382 def __len__(self) -> int: 

2383 """ 

2384 Returns the number of members in the set. 

2385 

2386 :returns: Number of set members. 

2387 """ 

2388 return len(self._items) 

2389 

2390 def __iter__(self) -> Iterator[V]: 

2391 """ 

2392 Returns an iterator to iterate all versions of this set from lowest to highest. 

2393 

2394 :returns: Iterator to iterate versions. 

2395 """ 

2396 return self._items.__iter__() 

2397 

2398 def __getitem__(self, index: int) -> V: 

2399 """ 

2400 Access to a version of a set by index. 

2401 

2402 :param index: The index of the version to access. 

2403 :returns: The indexed version. 

2404 

2405 .. hint:: Versions are ordered from lowest to highest version number. 

2406 """ 

2407 return self._items[index]