Skip to content

Commit 972a262

Browse files
committed
Initial cut at a new version impl that should be more compatible with Cassandra versions
1 parent 7d8015e commit 972a262

2 files changed

Lines changed: 64 additions & 67 deletions

File tree

cassandra/util.py

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,54 +1692,42 @@ def __repr__(self):
16921692
self.lower_bound, self.upper_bound, self.value
16931693
)
16941694

1695+
VERSION_REGEX = re.compile("(\\d+)\\.(\\d+)(\\.\\d+)?(\\.\\d+)?([~\\-]\\w[.\\w]*(?:-\\w[.\\w]*)*)?(\\+[.\\w]+)?")
16951696

16961697
@total_ordering
16971698
class Version(object):
1698-
"""
1699-
Internal minimalist class to compare versions.
1700-
A valid version is: <int>.<int>.<int>.<int or str>.
1701-
1702-
TODO: when python2 support is removed, use packaging.version.
1703-
"""
1704-
1705-
_version = None
1706-
major = None
1707-
minor = 0
1708-
patch = 0
1709-
build = 0
1710-
prerelease = 0
17111699

17121700
def __init__(self, version):
17131701
self._version = version
1714-
if '-' in version:
1715-
version_without_prerelease, self.prerelease = version.split('-', 1)
1716-
else:
1717-
version_without_prerelease = version
1718-
parts = list(reversed(version_without_prerelease.split('.')))
1719-
if len(parts) > 4:
1720-
prerelease_string = "-{}".format(self.prerelease) if self.prerelease else ""
1721-
log.warning("Unrecognized version: {}. Only 4 components plus prerelease are supported. "
1722-
"Assuming version as {}{}".format(version, '.'.join(parts[:-5:-1]), prerelease_string))
1702+
1703+
match = VERSION_REGEX.match(version)
1704+
if not match:
1705+
raise ValueError("Version string did not match expected format")
1706+
print(match.groups())
1707+
1708+
self.major = int(match[1])
1709+
self.minor = int(match[2])
17231710

17241711
try:
1725-
self.major = int(parts.pop())
1726-
except ValueError as e:
1727-
raise ValueError(
1728-
"Couldn't parse version {}. Version should start with a number".format(version))\
1729-
.with_traceback(e.__traceback__)
1712+
self.patch = self._cleanup(match[3])
1713+
except:
1714+
self.patch = 0
1715+
17301716
try:
1731-
self.minor = int(parts.pop()) if parts else 0
1732-
self.patch = int(parts.pop()) if parts else 0
1717+
self.build = self._cleanup(match[4])
1718+
except:
1719+
self.build = 0
17331720

1734-
if parts: # we have a build version
1735-
build = parts.pop()
1736-
try:
1737-
self.build = int(build)
1738-
except ValueError:
1739-
self.build = build
1740-
except ValueError:
1741-
assumed_version = "{}.{}.{}.{}-{}".format(self.major, self.minor, self.patch, self.build, self.prerelease)
1742-
log.warning("Unrecognized version {}. Assuming version as {}".format(version, assumed_version))
1721+
try:
1722+
self.prerelease = self._cleanup_prerelease(match[5])
1723+
except:
1724+
self.prerelease = 0
1725+
1726+
def _cleanup(self, s):
1727+
return int(s[1:]) if s else 0
1728+
1729+
def _cleanup_prerelease(self, str):
1730+
return str[1:] if str else 0
17431731

17441732
def __hash__(self):
17451733
return self._version

tests/unit/test_util_types.py

Lines changed: 38 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -209,21 +209,25 @@ class VersionTests(unittest.TestCase):
209209

210210
def test_version_parsing(self):
211211
versions = [
212-
('2.0.0', (2, 0, 0, 0, 0)),
213-
('3.1.0', (3, 1, 0, 0, 0)),
214-
('2.4.54', (2, 4, 54, 0, 0)),
215-
('3.1.1.12', (3, 1, 1, 12, 0)),
216-
('3.55.1.build12', (3, 55, 1, 'build12', 0)),
217-
('3.55.1.20190429-TEST', (3, 55, 1, 20190429, 'TEST')),
218-
('4.0-SNAPSHOT', (4, 0, 0, 0, 'SNAPSHOT')),
212+
# Test cases here adapted from the Java driver cases
213+
# (https://github.com/apache/cassandra-java-driver/blob/4.19.2/core/src/test/java/com/datastax/oss/driver/api/core/VersionTest.java)
214+
('1.2.19', (1, 2, 19, 0, 0)),
215+
('1.2', (1, 2, 0, 0, 0)),
216+
('1.2-beta1-SNAPSHOT', (1, 2, 0, 0, 'beta1-SNAPSHOT')),
217+
('1.2~beta1-SNAPSHOT', (1, 2, 0, 0, 'beta1-SNAPSHOT')),
218+
('1.2.19.2-SNAPSHOT', (1, 2, 19, 2, 'SNAPSHOT')),
219+
220+
# We also include a few test cases from the former impl of this class, mainly to note differences in behaviours
221+
222+
# Note that prerelease tags are expected to start with a hyphen or tilde so the expected tag is
223+
# lost in all cases below
224+
('3.55.1.build12', (3, 55, 1, 0, 0)),
219225
('1.0.5.4.3', (1, 0, 5, 4, 0)),
220-
('1-SNAPSHOT', (1, 0, 0, 0, 'SNAPSHOT')),
221-
('4.0.1.2.3.4.5-ABC-123-SNAP-TEST.blah', (4, 0, 1, 2, 'ABC-123-SNAP-TEST.blah')),
222-
('2.1.hello', (2, 1, 0, 0, 0)),
223-
('2.test.1', (2, 0, 0, 0, 0)),
226+
('2.1.hello', (2, 1, 0, 0, 0))
224227
]
225228

226229
for str_version, expected_result in versions:
230+
print(str_version)
227231
v = Version(str_version)
228232
self.assertEqual(str_version, str(v))
229233
self.assertEqual(v.major, expected_result[0])
@@ -232,9 +236,18 @@ def test_version_parsing(self):
232236
self.assertEqual(v.build, expected_result[3])
233237
self.assertEqual(v.prerelease, expected_result[4])
234238

235-
# not supported version formats
236-
with self.assertRaises(ValueError):
237-
Version('test.1.0')
239+
# Note that a few of these formats used to be supported when this class was based on the Python versioning scheme.
240+
# This has been updated to more directly correspond to the Cassandra versioning scheme. See CASSPYTHON-10 for more
241+
# detail.
242+
unsupported_versions = [
243+
"test.1.0",
244+
'2.test.1'
245+
]
246+
247+
for v in unsupported_versions:
248+
print(v)
249+
with self.assertRaises(ValueError):
250+
Version(v)
238251

239252
def test_version_compare(self):
240253
# just tests a bunch of versions
@@ -251,40 +264,36 @@ def test_version_compare(self):
251264

252265
# patch wins
253266
self.assertTrue(Version('2.3.1') > Version('2.3.0'))
254-
self.assertTrue(Version('2.3.1') > Version('2.3.0.4post0'))
267+
self.assertTrue(Version('2.3.1') > Version('2.3.0-4post0'))
255268
self.assertTrue(Version('2.3.1') > Version('2.3.0.44'))
256269

257270
# various
258271
self.assertTrue(Version('2.3.0.1') > Version('2.3.0.0'))
259272
self.assertTrue(Version('2.3.0.680') > Version('2.3.0.670'))
260273
self.assertTrue(Version('2.3.0.681') > Version('2.3.0.680'))
261-
self.assertTrue(Version('2.3.0.1build0') > Version('2.3.0.1')) # 4th part fallback to str cmp
262-
self.assertTrue(Version('2.3.0.build0') > Version('2.3.0.1')) # 4th part fallback to str cmp
263-
self.assertTrue(Version('2.3.0') < Version('2.3.0.build'))
264-
265-
self.assertTrue(Version('4-a') <= Version('4.0.0'))
266-
self.assertTrue(Version('4-a') <= Version('4.0-alpha1'))
267-
self.assertTrue(Version('4-a') <= Version('4.0-beta1'))
268-
self.assertTrue(Version('4.0.0') >= Version('4.0.0'))
269-
self.assertTrue(Version('4.0.0.421') >= Version('4.0.0'))
270-
self.assertTrue(Version('4.0.1') >= Version('4.0.0'))
274+
275+
# If builds are equal then a prerelease always comes before
276+
self.assertTrue(Version('2.3.0.1-SNAPSHOT') < Version('2.3.0.1'))
277+
278+
# If both have prereleases we fall back to a string compare
279+
self.assertTrue(Version('2.3.0.1-SNAPSHOT') < Version('2.3.0.1-ZNAPSHOT'))
280+
271281
self.assertTrue(Version('2.3.0') == Version('2.3.0'))
272282
self.assertTrue(Version('2.3.32') == Version('2.3.32'))
273283
self.assertTrue(Version('2.3.32') == Version('2.3.32.0'))
274-
self.assertTrue(Version('2.3.0.build') == Version('2.3.0.build'))
284+
self.assertTrue(Version('2.3.0-SNAPSHOT') == Version('2.3.0-SNAPSHOT'))
275285

276-
self.assertTrue(Version('4') == Version('4.0.0'))
277286
self.assertTrue(Version('4.0') == Version('4.0.0.0'))
278287
self.assertTrue(Version('4.0') > Version('3.9.3'))
279288

280289
self.assertTrue(Version('4.0') > Version('4.0-SNAPSHOT'))
281290
self.assertTrue(Version('4.0-SNAPSHOT') == Version('4.0-SNAPSHOT'))
282291
self.assertTrue(Version('4.0.0-SNAPSHOT') == Version('4.0-SNAPSHOT'))
283292
self.assertTrue(Version('4.0.0-SNAPSHOT') == Version('4.0.0-SNAPSHOT'))
284-
self.assertTrue(Version('4.0.0.build5-SNAPSHOT') == Version('4.0.0.build5-SNAPSHOT'))
293+
self.assertTrue(Version('4.0.0.5-SNAPSHOT') == Version('4.0.0.5-SNAPSHOT'))
285294
self.assertTrue(Version('4.1-SNAPSHOT') > Version('4.0-SNAPSHOT'))
286295
self.assertTrue(Version('4.0.1-SNAPSHOT') > Version('4.0.0-SNAPSHOT'))
287-
self.assertTrue(Version('4.0.0.build6-SNAPSHOT') > Version('4.0.0.build5-SNAPSHOT'))
296+
self.assertTrue(Version('4.0.0.6-SNAPSHOT') > Version('4.0.0.5-SNAPSHOT'))
288297
self.assertTrue(Version('4.0-SNAPSHOT2') > Version('4.0-SNAPSHOT1'))
289298
self.assertTrue(Version('4.0-SNAPSHOT2') > Version('4.0.0-SNAPSHOT1'))
290299

0 commit comments

Comments
 (0)