Skip to content

Commit 09f50cf

Browse files
committed
Add to_python_version method to convert version to Python compatible string format
1 parent 70fbcda commit 09f50cf

File tree

2 files changed

+79
-19
lines changed

2 files changed

+79
-19
lines changed

version/tests/version_tests.py

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -390,19 +390,6 @@ def test_getters_and_setters():
390390
assert v.prerelease == "rc.1"
391391
assert v.metadata == "build.2"
392392

393-
def test_setters_invalid_values():
394-
v = Version(1, 2, 3)
395-
with pytest.raises(ValueError):
396-
v.major = "a"
397-
with pytest.raises(ValueError):
398-
v.minor = "b"
399-
with pytest.raises(ValueError):
400-
v.patch = "c"
401-
with pytest.raises(ValueError):
402-
v.prerelease = "invalid@prerelease"
403-
with pytest.raises(ValueError):
404-
v.metadata = "invalid@meta"
405-
406393

407394
@pytest.mark.parametrize(
408395
"input_str,expected_major,expected_minor,expected_patch,expected_prerelease, expected_metadata",
@@ -554,3 +541,55 @@ def test_is_valid_string(version_str, expected):
554541
print(repr(Version.from_string(version_str)))
555542
# Assert
556543
assert result == expected
544+
545+
546+
@pytest.mark.parametrize(
547+
"major, minor, patch, prerelease, metadata, expected_str",
548+
[
549+
(1, 0, 0, None, None, "1.0.0"),
550+
(1, 2, 3, None, None, "1.2.3"),
551+
(1, 0, 0, "alpha", None, "1.0.0.alpha"),
552+
(1, 0, 0, "beta", None, "1.0.0.beta"),
553+
(1, 0, 0, "rc.1", None, "1.0.0.rc_1"),
554+
(1, 2, 3, "alpha.1", None, "1.2.3.alpha_1"),
555+
(1, 2, 3, "beta.2.3", None, "1.2.3.beta_2_3"),
556+
(1, 0, 0, None, "build", "1.0.0.postbuild"),
557+
(1, 0, 0, None, "build.123", "1.0.0.postbuild_123"),
558+
(1, 2, 3, "alpha", "build", "1.2.3.alpha.postbuild"),
559+
(1, 2, 3, "alpha.1", "build.123", "1.2.3.alpha_1.postbuild_123"),
560+
(10, 20, 30, "beta.1.2.3", "build.456.789", "10.20.30.beta_1_2_3.postbuild_456_789"),
561+
],
562+
ids=[
563+
"no_prerelease_no_metadata",
564+
"simple_version",
565+
"alpha_prerelease",
566+
"beta_prerelease",
567+
"rc_prerelease_with_dots",
568+
"prerelease_with_single_dot",
569+
"prerelease_with_multiple_dots",
570+
"metadata_only",
571+
"metadata_with_dots",
572+
"prerelease_and_metadata",
573+
"prerelease_and_metadata_with_dots",
574+
"complex_prerelease_and_metadata",
575+
]
576+
)
577+
def test_to_python_version(major, minor, patch, prerelease, metadata, expected_str):
578+
version = Version(major, minor, patch, prerelease, metadata)
579+
assert version.to_python_version() == expected_str
580+
581+
def test_to_python_version_replaces_special_chars():
582+
# Test that dots and dashes are replaced with underscores
583+
version = Version(1, 2, 3, "alpha.beta", "metadata.123")
584+
result = version.to_python_version()
585+
assert result == "1.2.3.alpha_beta.postmetadata_123"
586+
assert '.' not in result.split('.')[-1] # No dots in the last segment after splitting on main dots
587+
588+
def test_to_python_version_vs_str():
589+
# Verify that to_python_version and __str__ produce different outputs for versions with prerelease/metadata
590+
version = Version(1, 2, 3, "alpha.1", "build.123")
591+
python_version = version.to_python_version()
592+
semver_version = str(version)
593+
assert python_version == "1.2.3.alpha_1.postbuild_123"
594+
assert semver_version == "1.2.3-alpha.1+build.123"
595+
assert python_version != semver_version

version/version/version.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,21 @@ def __str__(self) -> str:
166166
if self.__metadata:
167167
version_str += f"+{self.__metadata}"
168168
return version_str
169+
170+
def to_python_version(self) -> str:
171+
"""
172+
Return the version as a Python compatible version string.
173+
174+
:return: Python compatible version string
175+
"""
176+
version_str = f"{self.__major}.{self.__minor}.{self.__patch}"
177+
if self.__prerelease:
178+
prerelease = self.__prerelease.replace('.', '_').replace('-', '_')
179+
version_str += f".{prerelease}"
180+
if self.__metadata:
181+
metadata = self.__metadata.replace('.', '_').replace('-', '_')
182+
version_str += f".post{metadata}"
183+
return version_str
169184

170185
def __repr__(self) -> str:
171186
"""
@@ -220,12 +235,18 @@ def __lt__(self, other : object) -> bool: #pylint: disable=too-many-return-state
220235
if self.__patch != other.patch: # 1.1.1 < 1.1.2
221236
return self.__patch < other.patch
222237

223-
if self.__prerelease is None and other.prerelease is not None: # 1.0.0 < 1.0.0-alpha
224-
return False
225-
if self.__prerelease is not None and other.prerelease is None: # 1.0.0-alpha < 1.0.0
226-
return True
227-
if self.__prerelease is None and other.prerelease is None: # 1.0.0 < 1.0.0
228-
return False
238+
# if self.__prerelease is None and other.prerelease is not None: # 1.0.0 < 1.0.0-alpha
239+
# return False
240+
# if self.__prerelease is not None and other.prerelease is None: # 1.0.0-alpha < 1.0.0
241+
# return True
242+
# if self.__prerelease is None and other.prerelease is None: # 1.0.0 < 1.0.0
243+
# return False
244+
if self.__prerelease is None:
245+
return False # 1.0.0 > 1.0.0-alpha or 1.0.0 < 1.0.0
246+
if other.prerelease is None:
247+
return True # 1.0.0-alpha < 1.0.0
248+
249+
# case like 1.0.0-alpha < 1.0.0-beta
229250

230251
self_tokens = self.__prerelease.split('.')
231252
other_tokens = other.prerelease.split('.')

0 commit comments

Comments
 (0)