From 4691a44b37b94f36fd8e9027137065e076c3c35c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Barrois?= Date: Tue, 22 May 2012 02:00:47 +0200 Subject: Introduce Spec.filter and Spec.select. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Raphaël Barrois --- README | 13 +++++++-- doc/changelog.rst | 12 +++++++- doc/index.rst | 22 ++++++++++++++- doc/reference.rst | 50 +++++++++++++++++++++++++------- src/semantic_version/__init__.py | 2 +- src/semantic_version/base.py | 17 +++++++++-- tests/test_base.py | 61 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 159 insertions(+), 18 deletions(-) diff --git a/README b/README index 38c5196..fb98136 100644 --- a/README +++ b/README @@ -25,9 +25,9 @@ Compare it to other versions:: >>> v < Version('0.1.2') True >>> sorted([Version('0.1.1'), Version('0.11.1'), Version('0.1.1-alpha')]) - [, - , - ] + [, + , + ] Define a simple specification:: @@ -49,6 +49,13 @@ Define complex specifications:: False +Select the best compatible version from a list:: + + >>> s = Spec('>=0.1.1,<0.2.0') + >>> s.select([Version('0.1.1'), Version('0.1.9-alpha'), Version('0.1.9-alpha+1')) + + + Framework integration ===================== diff --git a/doc/changelog.rst b/doc/changelog.rst index 7b1cb8e..10b4ad3 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -1,9 +1,19 @@ ChangeLog ========= -2.0.0 (Master) +2.1.0 (Master) -------------- +*New:* + + * Add :func:`semantic_version.Spec.filter` (filter a list of :class:`~semantic_version.Version`) + * Add :func:`semantic_version.Spec.select` (select the highest + :class:`~semantic_version.Version` from a list) + * Update :func:`semantic_version.Version.__repr__` + +2.0.0 (22/05/2012) +------------------ + *Backwards incompatible changes:* * Removed "loose" specification support diff --git a/doc/index.rst b/doc/index.rst index 952e30e..38156cf 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -9,7 +9,7 @@ python-semanticversion This small python library provides a few tools to handle `SemVer`_ in Python. -The first release (1.0.0) should handle the 2.0.0-rc1 version of the SemVer scheme. +It follows strictly the 2.0.0-rc1 version of the SemVer scheme. Getting started @@ -116,6 +116,26 @@ Combining specifications can be expressed in two ways: >>> Spec('>=0.1.1', '!=0.2.4-alpha,<0.3.0') +Using a specification +""""""""""""""""""""" + +The :func:`Spec.filter` method filters an iterable of :class:`Version`:: + + >>> s = Spec('>=0.1.0,<0.4.0') + >>> versions = (Version('0.%d.0' % i) for i in range(6)) + >>> for v in s.filter(versions): + ... print v + 0.1.0 + 0.2.0 + 0.3.0 + +It is also possible to select the 'best' version from such iterables:: + + >>> s = Spec('>=0.1.0,<0.4.0') + >>> versions = (Version('0.%d.0' % i) for i in range(6)) + >>> s.select(versions) + + Including pre-release identifiers in specifications """"""""""""""""""""""""""""""""""""""""""""""""""" diff --git a/doc/reference.rst b/doc/reference.rst index f3b9087..6ac06e0 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -53,7 +53,7 @@ Representing a version (the Version class) Constructed from a textual version string:: >>> Version('1.1.1') - + >>> str(Version('1.1.1')) '1.1.1' @@ -156,7 +156,7 @@ Representing a version (the Version class) >>> v = Version('0.1.1-rc2+build4.4') >>> v - + >>> str(v) '0.1.1-rc2+build4.4' @@ -252,19 +252,19 @@ rules apply: >>> Spec('>=1.0.0,<1.2.0,!=1.1.4') = <~SemVer: 1 0 0 None None>>, - >, - > + = <~Version(1 0 0 None None)>>, + >, + > )> Version specifications may also be passed in separated arguments:: >>> Spec('>=1.0.0', '<1.2.0', '!=1.1.4,!=1.1.13') = <~SemVer: 1 0 0 None None>>, - >, - > - > + = <~Version(1 0 0 None None)>>, + >, + > + > )> @@ -290,6 +290,36 @@ rules apply: :type version: :class:`Version` :rtype: ``bool`` + + .. method:: filter(self, versions) + + Extract all compatible :class:`versions ` from an iterable of + :class:`Version` objects. + + :param versions: The versions to filter + :type versions: iterable of :class:`Version` + :yield: :class:`Version` + + + .. method:: select(self, versions) + + Select the highest compatible version from an iterable of :class:`Version` + objects. + + .. sourcecode:: pycon + + >>> s = Spec('>=0.1.0') + >>> s.select([]) + None + >>> s.select([Version('0.1.0'), Version('0.1.3'), Version('0.1.1')]) + + + :param versions: The versions to filter + :type versions: iterable of :class:`Version` + :rtype: The highest compatible :class:`Version` if at least one of the + given versions is compatible; :class:`None` otherwise. + + .. method:: __contains__(self, version) Alias of the :func:`match` method; @@ -341,7 +371,7 @@ rules apply: Stores a version specification, defined from a string:: >>> SpecItem('>=0.1.1') - = > + = > This allows to test :class:`Version` objects against the :class:`SpecItem`:: diff --git a/src/semantic_version/__init__.py b/src/semantic_version/__init__.py index 9826d65..c03d207 100644 --- a/src/semantic_version/__init__.py +++ b/src/semantic_version/__init__.py @@ -2,7 +2,7 @@ # Copyright (c) 2012 Raphaël Barrois -__version__ = '2.0.0' +__version__ = '2.1.0-alpha' from .base import compare, match, Spec, SpecItem, Version diff --git a/src/semantic_version/base.py b/src/semantic_version/base.py index 16a1404..67ffe92 100644 --- a/src/semantic_version/base.py +++ b/src/semantic_version/base.py @@ -127,8 +127,8 @@ class Version(object): return version def __repr__(self): - return '<%sSemVer(%s, %s, %s, %r, %r)>' % ( - '~' if self.partial else '', + return '<%sVersion(%s, %s, %s, %r, %r)>' % ( + ', partial=True' if self.partial else '', self.major, self.minor, self.patch, @@ -311,6 +311,19 @@ class Spec(object): """Check whether a Version satisfies the Spec.""" return all(spec.match(version) for spec in self.specs) + def filter(self, versions): + """Filter an iterable of versions satisfying the Spec.""" + for version in versions: + if self.match(version): + yield version + + def select(self, versions): + """Select the best compatible version among an iterable of options.""" + options = list(self.filter(versions)) + if options: + return max(options) + return None + def __contains__(self, version): if isinstance(version, Version): return self.match(version) diff --git a/tests/test_base.py b/tests/test_base.py index 22690b6..9bc19e8 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -343,6 +343,67 @@ class SpecTestCase(unittest.TestCase): self.assertEqual(slist1, slist2) self.assertFalse(slist1 == spec_list_text) + def test_filter_empty(self): + s = base.Spec('>=0.1.1') + res = tuple(s.filter(())) + self.assertEqual((), res) + + def test_filter_incompatible(self): + s = base.Spec('>=0.1.1,!=0.1.4') + res = tuple(s.filter([ + base.Version('0.1.0'), + base.Version('0.1.4'), + base.Version('0.1.4-alpha'), + ])) + self.assertEqual((), res) + + def test_filter_compatible(self): + s = base.Spec('>=0.1.1,!=0.1.4,<0.2.0') + res = tuple(s.filter([ + base.Version('0.1.0'), + base.Version('0.1.1'), + base.Version('0.1.5'), + base.Version('0.1.4-alpha'), + base.Version('0.1.2'), + base.Version('0.2.0-rc1'), + base.Version('3.14.15'), + ])) + + expected = ( + base.Version('0.1.1'), + base.Version('0.1.5'), + base.Version('0.1.2'), + ) + + self.assertEqual(expected, res) + + def test_select_empty(self): + s = base.Spec('>=0.1.1') + self.assertIsNone(s.select(())) + + def test_select_incompatible(self): + s = base.Spec('>=0.1.1,!=0.1.4') + res = s.select([ + base.Version('0.1.0'), + base.Version('0.1.4'), + base.Version('0.1.4-alpha'), + ]) + self.assertIsNone(res) + + def test_select_compatible(self): + s = base.Spec('>=0.1.1,!=0.1.4,<0.2.0') + res = s.select([ + base.Version('0.1.0'), + base.Version('0.1.1'), + base.Version('0.1.5'), + base.Version('0.1.4-alpha'), + base.Version('0.1.2'), + base.Version('0.2.0-rc1'), + base.Version('3.14.15'), + ]) + + self.assertEqual(base.Version('0.1.5'), res) + def test_contains(self): self.assertFalse('ii' in base.Spec('>=0.1.1')) -- cgit v1.2.1