From 5ca781f439e8d402ada7f51c31d21ba211f673b6 Mon Sep 17 00:00:00 2001 From: Dave Hall Date: Thu, 15 Oct 2015 11:41:19 +1100 Subject: [PATCH 1/4] Add support for npm/composer caret and tilde condition extensions --- CREDITS | 1 + semantic_version/base.py | 17 ++++++++++++++++- tests/test_match.py | 13 +++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CREDITS b/CREDITS index c700c77..619b6cc 100644 --- a/CREDITS +++ b/CREDITS @@ -20,6 +20,7 @@ The project has received contributions from (in alphabetical order): * Raphaƫl Barrois (https://github.com/rbarrois) * Michael Hrivnak (https://github.com/mhrivnak) * Rick Eyre (https://github.com/rickeyre) +* Dave Hall (https://github.com/skwashd) Contributor license agreement diff --git a/semantic_version/base.py b/semantic_version/base.py index 982fcc8..e001005 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -392,8 +392,11 @@ class SpecItem(object): KIND_GTE = '>=' KIND_GT = '>' KIND_NEQ = '!=' + KIND_CARET = '^' + KIND_TILDE = '~' - re_spec = re.compile(r'^(<|<=|==|>=|>|!=)(\d.*)$') + caret = re.escape('^') + re_spec = re.compile(r'^(<|<=|==|>=|>|!=|{}|~)(\d.*)$'.format(caret)) def __init__(self, requirement_string): kind, spec = self.parse(requirement_string) @@ -436,9 +439,21 @@ class SpecItem(object): return version > self.spec elif self.kind == self.KIND_NEQ: return version != self.spec + elif self.kind == self.KIND_CARET: + return self.caretCompare(version) + elif self.kind == self.KIND_TILDE: + return self.tildeCompare(version) else: # pragma: no cover raise ValueError('Unexpected match kind: %r' % self.kind) + def caretCompare(self, version): + max_version = version.next_major() + return version >= self.spec and version < max_version + + def tildeCompare(self, version): + max_version = version.next_minor() + return version >= self.spec and version < max_version + def __str__(self): return '%s%s' % (self.kind, self.spec) diff --git a/tests/test_match.py b/tests/test_match.py index 6926e0a..73a2588 100755 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -27,6 +27,8 @@ class MatchTestCase(unittest.TestCase): '>=0.1.2-rc1.3.4', '==0.1.2+build42-12.2012-01-01.12h23', '!=0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + '^0.1.2', + '~0.1.2', ] matches = { @@ -86,6 +88,17 @@ class MatchTestCase(unittest.TestCase): '0.1.1-rc4', '0.1.0+12.3', ], + '^0.1.2': [ + '0.1.2', + '0.1.2+build4.5', + '0.1.3-rc1.3', + '0.2.0', + ], + '~0.1.2': [ + '0.1.2', + '0.1.2+build4.5', + '0.1.3-rc1.3', + ], } def test_invalid(self): From 6004bfd349a0707309be52fc4d418fc579dc91d4 Mon Sep 17 00:00:00 2001 From: Dave Hall Date: Mon, 19 Oct 2015 20:14:27 +1100 Subject: [PATCH 2/4] Make regex more readable --- semantic_version/base.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/semantic_version/base.py b/semantic_version/base.py index e001005..deebbc9 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -395,8 +395,7 @@ class SpecItem(object): KIND_CARET = '^' KIND_TILDE = '~' - caret = re.escape('^') - re_spec = re.compile(r'^(<|<=|==|>=|>|!=|{}|~)(\d.*)$'.format(caret)) + re_spec = re.compile(r'^(<|<=|==|>=|>|!=|\^|~)(\d.*)$') def __init__(self, requirement_string): kind, spec = self.parse(requirement_string) From a8a75320eb274ed27227b70a3d4d090c4674237d Mon Sep 17 00:00:00 2001 From: Dave Hall Date: Mon, 19 Oct 2015 16:54:16 +1100 Subject: [PATCH 3/4] Support for alternative equals specs Composer assumes equals if no operator is used npm uses a single equals operator --- semantic_version/base.py | 6 ++++-- tests/test_match.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/semantic_version/base.py b/semantic_version/base.py index deebbc9..d0f5db8 100644 --- a/semantic_version/base.py +++ b/semantic_version/base.py @@ -389,13 +389,15 @@ class SpecItem(object): KIND_LT = '<' KIND_LTE = '<=' KIND_EQUAL = '==' + KIND_SHORTEQ = '=' + KIND_EMPTY = '' KIND_GTE = '>=' KIND_GT = '>' KIND_NEQ = '!=' KIND_CARET = '^' KIND_TILDE = '~' - re_spec = re.compile(r'^(<|<=|==|>=|>|!=|\^|~)(\d.*)$') + re_spec = re.compile(r'^(<|<=|={,2}|>=|>|!=|\^|~)(\d.*)$') def __init__(self, requirement_string): kind, spec = self.parse(requirement_string) @@ -430,7 +432,7 @@ class SpecItem(object): return version < self.spec elif self.kind == self.KIND_LTE: return version <= self.spec - elif self.kind == self.KIND_EQUAL: + elif self.kind in [self.KIND_EQUAL, self.KIND_SHORTEQ, self.KIND_EMPTY]: return version == self.spec elif self.kind == self.KIND_GTE: return version >= self.spec diff --git a/tests/test_match.py b/tests/test_match.py index 73a2588..c4452d4 100755 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -21,6 +21,8 @@ class MatchTestCase(unittest.TestCase): valid_specs = [ '*', '==0.1.0', + '=0.1.0', + '0.1.0', '<=0.1.1', '<0.1', '>0.1.2-rc1', @@ -49,6 +51,18 @@ class MatchTestCase(unittest.TestCase): '0.1.2+build42-12.2012-01-01.12h23', '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', ], + '=0.1.2': [ + '0.1.2-rc1', + '0.1.2-rc1.3.4', + '0.1.2+build42-12.2012-01-01.12h23', + '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + ], + '0.1.2': [ + '0.1.2-rc1', + '0.1.2-rc1.3.4', + '0.1.2+build42-12.2012-01-01.12h23', + '0.1.2-rc1.3-14.15+build.2012-01-01.11h34', + ], '<=0.1.2': [ '0.1.1', '0.1.2-rc1', From ec54ee92f1e9ad06d87fc864a08ceb66021c7df0 Mon Sep 17 00:00:00 2001 From: Dave Hall Date: Sun, 29 Nov 2015 23:48:59 +1100 Subject: [PATCH 4/4] Fix broken test --- tests/test_match.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_match.py b/tests/test_match.py index c4452d4..473abfc 100755 --- a/tests/test_match.py +++ b/tests/test_match.py @@ -14,7 +14,6 @@ class MatchTestCase(unittest.TestCase): '!0.1', '<=0.1.4a', '>0.1.1.1', - '~0.1.2-rc23,1', '<0.1.2-rc1.3-14.15+build.2012-01-01.11h34', ] @@ -25,6 +24,7 @@ class MatchTestCase(unittest.TestCase): '0.1.0', '<=0.1.1', '<0.1', + '1', '>0.1.2-rc1', '>=0.1.2-rc1.3.4', '==0.1.2+build42-12.2012-01-01.12h23',