0
# Version Handling
1
2
Enhanced version class and utilities for parsing, normalizing, and comparing software versions. Extends PEP 440 with intelligent handling of real-world version inconsistencies found across different software projects and platforms.
3
4
## Capabilities
5
6
### Version Class
7
8
Enhanced version class that extends packaging.version.Version with additional normalization and transformation capabilities for handling inconsistent real-world version formats.
9
10
```python { .api }
11
class Version(PackagingVersion):
12
"""
13
Enhanced version class implementing PEP 440 with additional normalization.
14
15
This class extends packaging.version.Version with specialized transformations
16
to handle common version format inconsistencies found in real-world software
17
projects across different platforms and release practices.
18
"""
19
20
def __init__(self, version: str):
21
"""
22
Initialize Version with intelligent normalization.
23
24
Parameters:
25
- version: Version string to parse and normalize
26
27
Raises:
28
- InvalidVersion: If version cannot be normalized to PEP 440 format
29
"""
30
31
@staticmethod
32
def special_cases_transformation(version: str) -> str:
33
"""
34
Apply specialized transformations for common version format issues.
35
36
Handles cases like:
37
- Release candidate formats (rc1.2 → rc1)
38
- Post-release patterns (p1 → post1)
39
- Preview/early access versions
40
- Beta-RC combinations
41
- Dashed pre-release formats
42
43
Parameters:
44
- version: Raw version string
45
46
Returns:
47
- Transformed version string compatible with PEP 440
48
"""
49
```
50
51
### Version Transformation Patterns
52
53
The Version class applies various regex-based transformations to normalize version strings:
54
55
```python { .api }
56
# Dashed substitution patterns applied during normalization
57
regex_dashed_substitutions = [
58
(re.compile(r"-p(\d+)$"), "-post\\1"), # -p1 → -post1
59
(re.compile(r"-preview-(\d+)"), "-pre\\1"), # -preview-1 → -pre1
60
(re.compile(r"-early-access-(\d+)"), "-alpha\\1"), # -early-access-1 → -alpha1
61
(re.compile(r"-pre-(\d+)"), "-pre\\1"), # -pre-1 → -pre1
62
(re.compile(r"-beta[-.]rc(\d+)"), "-beta\\1"), # -beta-rc1 → -beta1
63
(re.compile(r"^pre-(.*)"), "\\1-pre0"), # pre-1.0 → 1.0-pre0
64
]
65
66
# Part normalization mapping for common pre-release identifiers
67
part_to_pypi_dict = {
68
"devel": "dev0",
69
"test": "dev0",
70
"dev": "dev0",
71
"alpha": "a0",
72
"beta": "b0",
73
"rc": "rc0",
74
"preview": "rc0",
75
"pre": "rc0",
76
}
77
```
78
79
### Version Parsing Utilities
80
81
Utility functions for extracting and parsing version information from various sources.
82
83
```python { .api }
84
def parse_version(tag: str) -> Version:
85
"""
86
Parse version from git tag or other version source.
87
88
Extracts version information from tag strings that may contain
89
prefixes, suffixes, or other metadata beyond the core version.
90
91
Parameters:
92
- tag: Tag string or version identifier
93
94
Returns:
95
- Version object with parsed and normalized version
96
97
Raises:
98
- InvalidVersion: If no valid version can be extracted
99
"""
100
```
101
102
## Usage Examples
103
104
### Basic Version Operations
105
106
```python
107
from lastversion.version import Version
108
109
# Create version objects
110
v1 = Version("1.2.3")
111
v2 = Version("1.2.4")
112
113
# Version comparison
114
print(v1 < v2) # True
115
print(v1 == v2) # False
116
print(max(v1, v2)) # Version('1.2.4')
117
118
# String representation
119
print(str(v1)) # "1.2.3"
120
print(repr(v1)) # "<Version('1.2.3')>"
121
```
122
123
### Handling Inconsistent Formats
124
125
```python
126
from lastversion.version import Version
127
128
# Normalize various real-world version formats
129
versions = [
130
"v1.2.3-rc1", # Git tag with prefix
131
"release-1.2.3", # Release prefix
132
"1.2.3-p1", # Post-release
133
"1.2.3-preview-1", # Preview release
134
"1.2.3-beta-rc2", # Beta release candidate
135
"pre-1.2.3", # Pre-release prefix
136
]
137
138
normalized = [Version(v) for v in versions]
139
for orig, norm in zip(versions, normalized):
140
print(f"{orig} → {norm}")
141
```
142
143
### Version Filtering and Selection
144
145
```python
146
from lastversion.version import Version
147
from packaging.version import InvalidVersion
148
149
def filter_stable_versions(version_strings):
150
"""Filter out pre-release and invalid versions."""
151
stable_versions = []
152
153
for version_str in version_strings:
154
try:
155
version = Version(version_str)
156
# Check if it's a stable release (no pre-release components)
157
if not version.is_prerelease:
158
stable_versions.append(version)
159
except InvalidVersion:
160
continue # Skip invalid versions
161
162
return sorted(stable_versions)
163
164
# Example usage
165
raw_versions = ["1.0.0", "1.1.0-beta", "1.1.0", "2.0.0-rc1", "2.0.0"]
166
stable = filter_stable_versions(raw_versions)
167
print(f"Latest stable: {max(stable)}") # Version('2.0.0')
168
```
169
170
### Integration with Core Functions
171
172
```python
173
from lastversion import latest
174
from lastversion.version import Version
175
176
# Get version object directly
177
version_obj = latest("mautic/mautic", output_format="version")
178
179
# Version objects support all comparison operations
180
if version_obj >= Version("4.0.0"):
181
print("Major version 4 or higher")
182
183
# Check version properties
184
print(f"Is prerelease: {version_obj.is_prerelease}")
185
print(f"Is devrelease: {version_obj.is_devrelease}")
186
print(f"Major version: {version_obj.major}")
187
print(f"Minor version: {version_obj.minor}")
188
print(f"Micro version: {version_obj.micro}")
189
```
190
191
### Custom Version Transformations
192
193
```python
194
from lastversion.version import Version
195
196
# Example of how special_cases_transformation works
197
test_versions = [
198
"1.0.0-p1", # Post release
199
"2.0.0-preview-1", # Preview
200
"3.0.0-beta-rc2", # Beta release candidate
201
"pre-4.0.0", # Pre-prefixed version
202
]
203
204
for version_str in test_versions:
205
transformed = Version.special_cases_transformation(version_str)
206
version_obj = Version(transformed)
207
print(f"{version_str} → {transformed} → {version_obj}")
208
```
209
210
## Error Handling
211
212
```python
213
from lastversion.version import Version
214
from packaging.version import InvalidVersion
215
216
def safe_version_parse(version_str):
217
"""Safely parse version with error handling."""
218
try:
219
return Version(version_str)
220
except InvalidVersion as e:
221
print(f"Invalid version '{version_str}': {e}")
222
return None
223
224
# Example usage
225
versions = ["1.2.3", "invalid", "1.2.3-rc1", "not.a.version"]
226
parsed = [safe_version_parse(v) for v in versions]
227
valid_versions = [v for v in parsed if v is not None]
228
```