From f84d754af1ae86aaa9a891445d6ae5be36668a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Wed, 20 Mar 2013 02:02:02 +0100 Subject: Fix handling of partial versions (Closes #1). --- doc/changelog.rst | 8 ++++++++ src/semantic_version/base.py | 18 ++++++++++++++---- tests/test_base.py | 1 + tests/test_django.py | 7 +++++-- tests/test_match.py | 16 +++++++++++++++- 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 13a3336..67b6cde 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -1,6 +1,14 @@ ChangeLog ========= +2.2.0 (current) +--------------- + +*Bugfix:* + + * `#1 `_: Allow partial + versions without minor or patch level + 2.1.2 (22/05/2012) ------------------ diff --git a/src/semantic_version/base.py b/src/semantic_version/base.py index 932614c..b8e7fcf 100644 --- a/src/semantic_version/base.py +++ b/src/semantic_version/base.py @@ -58,7 +58,7 @@ def identifier_list_cmp(a, b): class Version(object): version_re = re.compile('^(\d+)\.(\d+)\.(\d+)(?:-([0-9a-zA-Z.-]+))?(?:\+([0-9a-zA-Z.-]+))?$') - partial_version_re = re.compile('^(\d+)\.(\d+)\.(\d+)(?:-([0-9a-zA-Z.-]*))?(?:\+([0-9a-zA-Z.-]*))?$') + partial_version_re = re.compile('^(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:-([0-9a-zA-Z.-]*))?(?:\+([0-9a-zA-Z.-]*))?$') def __init__(self, version_string, partial=False): major, minor, patch, prerelease, build = self.parse(version_string, partial) @@ -71,6 +71,12 @@ class Version(object): self.partial = partial + @classmethod + def _coerce(cls, value, allow_none=False): + if value is None and allow_none: + return value + return int(value) + @classmethod def parse(cls, version_string, partial=False): if not version_string: @@ -88,8 +94,8 @@ class Version(object): major, minor, patch, prerelease, build = match.groups() major = int(major) - minor = int(minor) - patch = int(patch) + minor = cls._coerce(minor, partial) + patch = cls._coerce(patch, partial) if prerelease is None: if partial and (build is None): @@ -118,7 +124,11 @@ class Version(object): return iter((self.major, self.minor, self.patch, self.prerelease, self.build)) def __str__(self): - version = '%d.%d.%d' % (self.major, self.minor, self.patch) + version = '%d' % self.major + if self.minor is not None: + version = '%s.%d' % (version, self.minor) + if self.patch is not None: + version = '%s.%d' % (version, self.patch) if self.prerelease or (self.partial and self.prerelease == () and self.build is None): version = '%s-%s' % (version, '.'.join(self.prerelease)) diff --git a/tests/test_base.py b/tests/test_base.py index 52d2e84..90dbe96 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -197,6 +197,7 @@ class SpecItemTestCase(unittest.TestCase): '<=0.1.1+': (base.SpecItem.KIND_LTE, 0, 1, 1, (), ()), '<0.1.1': (base.SpecItem.KIND_LT, 0, 1, 1, None, None), '<=0.1.1': (base.SpecItem.KIND_LTE, 0, 1, 1, None, None), + '<=0.1.1-': (base.SpecItem.KIND_LTE, 0, 1, 1, (), None), '>=0.2.3-rc2': (base.SpecItem.KIND_GTE, 0, 2, 3, ('rc2',), None), '>0.2.3-rc2+': (base.SpecItem.KIND_GT, 0, 2, 3, ('rc2',), ()), '>=2.0.0': (base.SpecItem.KIND_GTE, 2, 0, 0, None, None), diff --git a/tests/test_django.py b/tests/test_django.py index e55ec28..a2d4c9b 100644 --- a/tests/test_django.py +++ b/tests/test_django.py @@ -56,13 +56,16 @@ class DjangoFieldTestCase(unittest.TestCase): obj.full_clean() + def test_partial_spec(self): + obj = models.VersionModel(version='0.1.1', spec='==0,!=0.2') + self.assertEqual(semantic_version.Version('0.1.1'), obj.version) + self.assertEqual(semantic_version.Spec('==0,!=0.2'), obj.spec) + def test_invalid_input(self): self.assertRaises(ValueError, models.VersionModel, version='0.1.1', spec='blah') self.assertRaises(ValueError, models.VersionModel, version='0.1', spec='==0.1.1,!=0.1.1-alpha') - self.assertRaises(ValueError, models.VersionModel, - version='0.1.1', spec='==0,!=0.2') def test_partial(self): obj = models.PartialVersionModel(partial='0.1.0') diff --git a/tests/test_match.py b/tests/test_match.py index e0fd5e7..5f7a988 100755 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -11,7 +11,6 @@ class MatchTestCase(unittest.TestCase): invalid_specs = [ '', '!0.1', - '<0.1', '<=0.1.4a', '>0.1.1.1', '~0.1.2-rc23,1', @@ -20,6 +19,7 @@ class MatchTestCase(unittest.TestCase): valid_specs = [ '==0.1.0', '<=0.1.1', + '<0.1', '>0.1.2-rc1', '>=0.1.2-rc1.3.4', '==0.1.2+build42-12.2012-01-01.12h23', @@ -101,6 +101,20 @@ class MatchTestCase(unittest.TestCase): version = semantic_version.Version('0.1.1-rc1+4.2') self.assertTrue(version in spec, "%r should be in %r" % (version, spec)) + def test_prerelease_check(self): + strict_spec = semantic_version.Spec('>=0.1.1-') + lax_spec = semantic_version.Spec('>=0.1.1') + version = semantic_version.Version('0.1.1-rc1+4.2') + self.assertTrue(version in lax_spec, "%r should be in %r" % (version, lax_spec)) + self.assertFalse(version in strict_spec, "%r should not be in %r" % (version, strict_spec)) + + def test_build_check(self): + strict_spec = semantic_version.Spec('<=0.1.1-rc1+') + lax_spec = semantic_version.Spec('<=0.1.1-rc1') + version = semantic_version.Version('0.1.1-rc1+4.2') + self.assertTrue(version in lax_spec, "%r should be in %r" % (version, lax_spec)) + self.assertFalse(version in strict_spec, "%r should not be in %r" % (version, strict_spec)) + if __name__ == '__main__': # pragma: no cover unittest.main() -- cgit v1.2.1