summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphaël Barrois <raphael.barrois@polytechnique.org>2012-05-22 02:00:47 +0200
committerRaphaël Barrois <raphael.barrois@polytechnique.org>2012-05-22 02:00:47 +0200
commit4691a44b37b94f36fd8e9027137065e076c3c35c (patch)
treebe742a4a9e9c136a54d63371ec127b930a738cc7
parentaeeecf63275a2d0fea8ef38b7efeea88491576a7 (diff)
downloadsemantic-version-4691a44b37b94f36fd8e9027137065e076c3c35c.tar.gz
Introduce Spec.filter and Spec.select.
Signed-off-by: Raphaël Barrois <raphael.barrois@polytechnique.org>
-rw-r--r--README13
-rw-r--r--doc/changelog.rst12
-rw-r--r--doc/index.rst22
-rw-r--r--doc/reference.rst50
-rw-r--r--src/semantic_version/__init__.py2
-rw-r--r--src/semantic_version/base.py17
-rwxr-xr-xtests/test_base.py61
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')])
- [<SemVer(0, 1, 1, ('alpha',), ())>,
- <SemVer(0, 1, 1, (), ())>,
- <SemVer(0, 11, 1, (), ())>]
+ [<Version(0, 1, 1, ('alpha',), ())>,
+ <Version(0, 1, 1, (), ())>,
+ <Version(0, 11, 1, (), ())>]
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'))
+ <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)
+ <Version(0, 3, 0, (), ())>
+
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')
- <SemVer(1, 1, 1, [], [])>
+ <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
- <SemVer(0, 1, 1, ['rc2'], ['build4', '4'])>
+ <Version(0, 1, 1, ['rc2'], ['build4', '4'])>
>>> str(v)
'0.1.1-rc2+build4.4'
@@ -252,19 +252,19 @@ rules apply:
>>> Spec('>=1.0.0,<1.2.0,!=1.1.4')
<Spec: (
- <SpecItem: >= <~SemVer: 1 0 0 None None>>,
- <SpecItem: < <~SemVer: 1 2 0 None None>>,
- <SpecItem: != <~SemVer: 1 1 4 None None>>
+ <SpecItem: >= <~Version(1 0 0 None None)>>,
+ <SpecItem: < <~Version(1 2 0 None None)>>,
+ <SpecItem: != <~Version(1 1 4 None None)>>
)>
Version specifications may also be passed in separated arguments::
>>> Spec('>=1.0.0', '<1.2.0', '!=1.1.4,!=1.1.13')
<Spec: (
- <SpecItem: >= <~SemVer: 1 0 0 None None>>,
- <SpecItem: < <SemVer: 1 2 0 None None>>,
- <SpecItem: != <~SemVer: 1 1 4 None None>>
- <SpecItem: != <~SemVer: 1 1 13 None None>>
+ <SpecItem: >= <~Version(1 0 0 None None)>>,
+ <SpecItem: < <Version(1 2 0 None None)>>,
+ <SpecItem: != <~Version(1 1 4 None None)>>
+ <SpecItem: != <~Version(1 1 13 None None)>>
)>
@@ -290,6 +290,36 @@ rules apply:
:type version: :class:`Version`
:rtype: ``bool``
+
+ .. method:: filter(self, versions)
+
+ Extract all compatible :class:`versions <Version>` 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')])
+ <Version(0, 1, 3, (), ())>
+
+ :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')
- <SpecItem: >= <SemVer(0, 1, 1, [], [])>>
+ <SpecItem: >= <Version(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'))