|
1 | 1 | from dataclasses import dataclass |
| 2 | +from typing import Optional |
2 | 3 |
|
3 | 4 |
|
4 | 5 | @dataclass |
5 | 6 | class Version: |
6 | 7 | major: int |
7 | 8 | minor: int |
8 | 9 | patch: int |
| 10 | + prerelease: Optional[int] |
9 | 11 |
|
10 | 12 | @staticmethod |
11 | 13 | def parse_version_string(version: str) -> "Version": |
12 | 14 | """Parse a version string with 'v' prefix (e.g., 'v1.2.3').""" |
13 | 15 | only_version = version[1:] |
| 16 | + if "beta" in only_version: |
| 17 | + [major, minor, patch, prerelease] = [part[0] for part in only_version.split(".")] |
| 18 | + return Version(int(major), int(minor), int(patch), int(prerelease)) |
14 | 19 | [major, minor, patch] = only_version.split(".") |
15 | | - return Version(int(major), int(minor), int(patch)) |
| 20 | + return Version(int(major), int(minor), int(patch), None) |
16 | 21 |
|
17 | 22 | @staticmethod |
18 | 23 | def parse(version: str) -> "Version": |
19 | 24 | """Parse a plain version string (e.g., '1.2.3').""" |
20 | 25 | parts = version.split(".") |
21 | | - if len(parts) != 3: |
| 26 | + if ("beta" in version and len(parts) != 4) or len(parts) != 3: |
22 | 27 | raise ValueError( |
23 | | - f"Invalid version string (expected 'MAJOR.MINOR.PATCH'): {version!r}" |
| 28 | + f"Invalid version string (expected 'MAJOR.MINOR.PATCH[-beta.PRERELEASE]'): {version!r}" |
24 | 29 | ) |
25 | 30 | try: |
26 | | - major, minor, patch = (int(part) for part in parts) |
| 31 | + if len(parts) == 4: |
| 32 | + major, minor, patch, prerelease = (int(part[0]) for part in parts) |
| 33 | + else: |
| 34 | + major, minor, patch = (int(part) for part in parts) |
| 35 | + prerelease = None |
27 | 36 | except ValueError as exc: |
28 | 37 | raise ValueError( |
29 | 38 | f"Invalid numeric components in version string: {version!r}" |
30 | 39 | ) from exc |
31 | | - return Version(major, minor, patch) |
| 40 | + return Version(major, minor, patch, prerelease) |
32 | 41 |
|
33 | 42 | def is_behind(self, other: "Version") -> bool: |
34 | 43 | """Returns if the current version is behind the other version based on major and minor versions.""" |
| 44 | + if self.prerelease and other.prerelease: |
| 45 | + return (other.major, other.minor, other.prerelease) > (self.major, self.minor, self.prerelease) |
35 | 46 | return (other.major, other.minor) > (self.major, self.minor) |
36 | 47 |
|
37 | 48 | def __repr__(self) -> str: |
| 49 | + if self.prerelease: |
| 50 | + return f"v{self.major}.{self.minor}.{self.patch}-beta.{self.prerelease}" |
38 | 51 | return f"v{self.major}.{self.minor}.{self.patch}" |
0 commit comments