summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRaphaël Barrois <raphael.barrois@polytechnique.org>2013-03-20 02:02:26 +0100
committerRaphaël Barrois <raphael.barrois@polytechnique.org>2013-03-20 02:02:26 +0100
commit712d74f87c07c60f2e1d27b44915d5d4bb941fe7 (patch)
treec0263f7f74b56895fb6b36fd89316a61238cd795 /src
parentf84d754af1ae86aaa9a891445d6ae5be36668a85 (diff)
downloadsemantic-version-712d74f87c07c60f2e1d27b44915d5d4bb941fe7.tar.gz
Add Version.coerce.
Some people don't use semver yet...
Diffstat (limited to 'src')
-rw-r--r--src/semantic_version/base.py80
-rw-r--r--src/semantic_version/django_fields.py11
2 files changed, 88 insertions, 3 deletions
diff --git a/src/semantic_version/base.py b/src/semantic_version/base.py
index b8e7fcf..b52c671 100644
--- a/src/semantic_version/base.py
+++ b/src/semantic_version/base.py
@@ -78,7 +78,85 @@ class Version(object):
return int(value)
@classmethod
- def parse(cls, version_string, partial=False):
+ def coerce(cls, version_string, partial=False):
+ """Coerce an arbitrary version string into a semver-compatible one.
+
+ The rule is:
+ - If not enough components, fill minor/patch with zeroes; unless
+ partial=True
+ - If more than 3 dot-separated components, extra components are "build"
+ data. If some "build" data already appeared, append it to the
+ extra components
+
+ Examples:
+ >>> Version.coerce('0.1')
+ Version(0, 1, 0)
+ >>> Version.coerce('0.1.2.3')
+ Version(0, 1, 2, (), ('3',))
+ >>> Version.coerce('0.1.2.3+4')
+ Version(0, 1, 2, (), ('3', '4'))
+ >>> Version.coerce('0.1+2-3+4_5')
+ Version(0, 1, 0, (), ('2-3', '4-5'))
+ """
+ base_re = re.compile(r'^\d+(?:\.\d+(?:\.\d+)?)?')
+
+ match = base_re.match(version_string)
+ if not match:
+ raise ValueError("Version string lacks a numerical component: %r"
+ % version_string)
+
+ version = version_string[:match.end()]
+ if not partial:
+ # We need a not-partial version.
+ while version.count('.') < 2:
+ version += '.0'
+
+ if match.end() == len(version_string):
+ return Version(version, partial=partial)
+
+ rest = version_string[match.end():]
+
+ # Cleanup the 'rest'
+ rest = re.sub(r'[^a-zA-Z0-9+.-]', '-', rest)
+
+ if rest[0] == '+':
+ # A 'build' component
+ prerelease = ''
+ build = rest[1:]
+ elif rest[0] == '.':
+ # An extra version component, probably 'build'
+ prerelease = ''
+ build = rest[1:]
+ elif rest[0] == '-':
+ rest = rest[1:]
+ if '+' in rest:
+ prerelease, build = rest.split('+', 1)
+ else:
+ prerelease, build = rest, ''
+ elif '+' in rest:
+ prerelease, build = rest.split('+', 1)
+ else:
+ prerelease, build = rest, ''
+
+ build = build.replace('+', '.')
+
+ if prerelease:
+ version = '%s-%s' % (version, prerelease)
+ if build:
+ version = '%s+%s' % (version, build)
+
+ return cls(version, partial=partial)
+
+ @classmethod
+ def parse(cls, version_string, partial=False, coerce=False):
+ """Parse a version string into a Version() object.
+
+ Args:
+ version_string (str), the version string to parse
+ partial (bool), whether to accept incomplete input
+ coerce (bool), whether to try to map the passed in string into a
+ valid Version.
+ """
if not version_string:
raise ValueError('Invalid empty version string: %r' % version_string)
diff --git a/src/semantic_version/django_fields.py b/src/semantic_version/django_fields.py
index ecc0a8f..eaf668a 100644
--- a/src/semantic_version/django_fields.py
+++ b/src/semantic_version/django_fields.py
@@ -38,6 +38,7 @@ class VersionField(BaseSemVerField):
def __init__(self, *args, **kwargs):
self.partial = kwargs.pop('partial', False)
+ self.coerce = kwargs.pop('coerce', False)
super(VersionField, self).__init__(*args, **kwargs)
def to_python(self, value):
@@ -46,7 +47,10 @@ class VersionField(BaseSemVerField):
return value
if isinstance(value, base.Version):
return value
- return base.Version(value, partial=self.partial)
+ if self.coerce:
+ return base.Version.coerce(value, partial=self.partial)
+ else:
+ return base.Version(value, partial=self.partial)
class SpecField(BaseSemVerField):
@@ -71,7 +75,10 @@ def add_south_rules():
(
(VersionField,),
[],
- {'partial': ('partial', {'default': False})},
+ {
+ 'partial': ('partial', {'default': False}),
+ 'coerce': ('coerce', {'default': False}),
+ },
),
], ["semantic_version\.django_fields"])