Forbid build metadata ordering (See #18)
SemVer 2.0.0 states that "Build metadata SHOULD be ignored when determining version precedence". This means that, when comparing ``0.1.0+1`` to ``0.1.0+bcd``:: >>> Version('0.1.0+1') == Version('0.1.0+bcd') False >>> Version('0.1.0+1') != Version('0.1.0+bcd') True >>> Version('0.1.0+1') < Version('0.1.0+bcd') False >>> Version('0.1.0+1') > Version('0.1.0+bcd') False >>> Version('0.1.0+1') <= Version('0.1.0+bcd') False >>> Version('0.1.0+1') >= Version('0.1.0+bcd') False >>> compare(Version('0.1.0+1'), Version('0.1.0+bcd')) NotImplemented This change also has the following effects: - When including build metadata in a ``Spec``, the only valid options are ``Spec('==0.1.0+sth')`` and ``Spec('!=0.1.0+sth')`` - The meaning of ``Spec('==0.1.0+')`` is now "Only version 0.1.0 without build metadata" - ``Spec('==0.1.0')`` now matches ``Version('0.1.0+anything')``
This commit is contained in:
parent
4aac5768db
commit
2ed3d39c29
30
ChangeLog
30
ChangeLog
|
@ -1,6 +1,36 @@
|
|||
ChangeLog
|
||||
=========
|
||||
|
||||
2.5.0 (master)
|
||||
--------------
|
||||
|
||||
*Bugfix:*
|
||||
|
||||
`#18 <https://github.com/rbarrois/python-semanticversion/issues/18>`_: According to SemVer 2.0.0, build numbers aren't ordered.
|
||||
|
||||
* Remove specs of the ``Spec('<1.1.3+')`` form
|
||||
* Comparing ``Version('0.1.0')`` to ``Version('0.1.0+bcd')`` has new
|
||||
rules::
|
||||
|
||||
>>> Version('0.1.0+1') == Version('0.1.0+bcd')
|
||||
False
|
||||
>>> Version('0.1.0+1') != Version('0.1.0+bcd')
|
||||
True
|
||||
>>> Version('0.1.0+1') < Version('0.1.0+bcd')
|
||||
False
|
||||
>>> Version('0.1.0+1') > Version('0.1.0+bcd')
|
||||
False
|
||||
>>> Version('0.1.0+1') <= Version('0.1.0+bcd')
|
||||
False
|
||||
>>> Version('0.1.0+1') >= Version('0.1.0+bcd')
|
||||
False
|
||||
>>> compare(Version('0.1.0+1'), Version('0.1.0+bcd'))
|
||||
NotImplemented
|
||||
|
||||
* :func:`semantic_version.compare` returns ``NotImplemented`` when its
|
||||
parameters differ only by build metadata
|
||||
* ``Spec('<=1.3.0')`` now matches ``Version('1.3.0+abde24fe883')``
|
||||
|
||||
2.4.2 (2015-07-02)
|
||||
------------------
|
||||
|
||||
|
|
13
README.rst
13
README.rst
|
@ -247,19 +247,18 @@ definition or (for the empty pre-release number) if a single dash is appended
|
|||
False
|
||||
|
||||
|
||||
Including build identifiers in specifications
|
||||
"""""""""""""""""""""""""""""""""""""""""""""
|
||||
Including build metadata in specifications
|
||||
""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
The same rule applies for the build identifier: comparisons will include it only
|
||||
if it was included in the :class:`Spec` definition, or - for the unnumbered build
|
||||
version - if a single + is appended to the definition(``1.0.0+``, ``1.0.0-alpha+``):
|
||||
Build metadata has no ordering; thus, the only meaningful comparison including
|
||||
build metadata is equality.
|
||||
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> Version('1.0.0+build2') in Spec('<=1.0.0') # Build identifier ignored
|
||||
>>> Version('1.0.0+build2') in Spec('<=1.0.0') # Build metadata ignored
|
||||
True
|
||||
>>> Version('1.0.0+build2') in Spec('<=1.0.0+') # Include build in checks
|
||||
>>> Version('1.0.0+build2') in Spec('==1.0.0+build2') # Include build in checks
|
||||
False
|
||||
|
||||
|
||||
|
|
|
@ -22,9 +22,13 @@ Module-level functions
|
|||
:param str v1: The first version to compare
|
||||
:param str v2: The second version to compare
|
||||
:raises: :exc:`ValueError`, if any version string is invalid
|
||||
:rtype: ``int``, -1 / 0 / 1 as for a :func:`cmp` comparison
|
||||
:rtype: ``int``, -1 / 0 / 1 as for a :func:`cmp` comparison;
|
||||
``NotImplemented`` if versions only differ by build metadata
|
||||
|
||||
|
||||
.. warning:: Since build metadata has no ordering,
|
||||
``compare(Version('0.1.1'), Version('0.1.1+3'))`` returns ``NotImplemented``
|
||||
|
||||
|
||||
.. function:: match(spec, version)
|
||||
|
||||
|
@ -107,9 +111,9 @@ Representing a version (the Version class)
|
|||
|
||||
.. attribute:: build
|
||||
|
||||
``tuple`` of ``strings``, the build component.
|
||||
``tuple`` of ``strings``, the build metadata.
|
||||
|
||||
It contains the various dot-separated identifiers in the build component.
|
||||
It contains the various dot-separated identifiers in the build metadata.
|
||||
|
||||
May be ``None`` for a :attr:`partial` version number in a ``<major>``, ``<major>.<minor>``,
|
||||
``<major>.<minor>.<patch>`` or ``<major>.<minor>.<patch>-<prerelease>`` format.
|
||||
|
@ -151,7 +155,7 @@ Representing a version (the Version class)
|
|||
For instance, ``Version('1.0', partial=True)`` means "any version beginning in ``1.0``".
|
||||
|
||||
``Version('1.0.1-alpha', partial=True)`` means "The ``1.0.1-alpha`` version or any
|
||||
ulterior build of that same version": ``1.0.1-alpha+build3`` matches, ``1.0.1-alpha.2`` doesn't.
|
||||
any release differing only in build metadata": ``1.0.1-alpha+build3`` matches, ``1.0.1-alpha.2`` doesn't.
|
||||
|
||||
Examples::
|
||||
|
||||
|
@ -246,7 +250,6 @@ The main issue with representing version specifications is that the usual syntax
|
|||
does not map well onto `SemVer`_ precedence rules:
|
||||
|
||||
* A specification of ``<1.3.4`` is not expected to allow ``1.3.4-rc2``, but strict `SemVer`_ comparisons allow it ;
|
||||
* Converting the previous specification to ``<=1.3.3`` in order to avoid ``1.3.4``
|
||||
prereleases has the issue of excluding ``1.3.3+build3`` ;
|
||||
* It may be necessary to exclude either all variations on a patch-level release
|
||||
(``!=1.3.3``) or specifically one build-level release (``1.3.3-build.434``).
|
||||
|
@ -256,7 +259,7 @@ In order to have version specification behave naturally, the rules are the follo
|
|||
|
||||
* If no pre-release number was included in the specification, pre-release numbers
|
||||
are ignored when deciding whether a version satisfies a specification.
|
||||
* If no build number was included in the specification, build numbers are ignored
|
||||
* If no build metadata was included in the specification, build metadata is ignored
|
||||
when deciding whether a version satisfies a specification.
|
||||
|
||||
This means that::
|
||||
|
@ -267,7 +270,7 @@ This means that::
|
|||
True
|
||||
>>> Version('1.1.1-rc1+build4') in Spec('<=1.1.1-rc1')
|
||||
True
|
||||
>>> Version('1.1.1-rc1+build4') in Spec('<=1.1.1-rc1+build2')
|
||||
>>> Version('1.1.1-rc1+build4') in Spec('==1.1.1-rc1+build2')
|
||||
False
|
||||
|
||||
|
||||
|
@ -285,20 +288,31 @@ rules apply:
|
|||
>>> Version('1.1.1-rc1') in Spec('<1.1.1-')
|
||||
True
|
||||
|
||||
* Setting a build separator without a build identifier (``>1.1.1+``) forces
|
||||
satisfaction tests to include both prerelease and build identifiers::
|
||||
* Setting a build metadata separator without build metadata (``<=1.1.1+``)
|
||||
forces matches "up to the build metadata"; use this to include/exclude a
|
||||
release lacking build metadata while excluding/including all other builds
|
||||
of that release
|
||||
|
||||
>>> Version('1.1.1+build2') in Spec('>1.1.1')
|
||||
False
|
||||
>>> Version('1.1.1+build2') in Spec('>1.1.1+')
|
||||
>>> Version('1.1.1') in Spec('==1.1.1+')
|
||||
True
|
||||
>>> Version('1.1.1+2') in Spec('==1.1.1+')
|
||||
False
|
||||
|
||||
|
||||
.. warning:: As stated in the `SemVer`_ specification, the ordering of build metadata is *undefined*.
|
||||
Thus, a :class:`Spec` string can only mention build metadata to include or exclude a specific version:
|
||||
|
||||
* ``==1.1.1+b1234`` includes this specific build
|
||||
* ``!=1.1.1+b1234`` excludes it (but would match ``1.1.1+b1235``
|
||||
* ``<1.1.1+b1`` is invalid
|
||||
|
||||
|
||||
.. class:: Spec(spec_string[, spec_string[, ...]])
|
||||
|
||||
Stores a list of :class:`SpecItem` and matches any :class:`Version` against all
|
||||
contained :class:`specs <SpecItem>`.
|
||||
|
||||
It is build from a comma-separated list of version specifications::
|
||||
It is built from a comma-separated list of version specifications::
|
||||
|
||||
>>> Spec('>=1.0.0,<1.2.0,!=1.1.4')
|
||||
<Spec: (
|
||||
|
@ -427,16 +441,16 @@ rules apply:
|
|||
|
||||
>>> SpecItem('>=0.1.1').match(Version('0.1.1-rc1')) # pre-release satisfy conditions
|
||||
True
|
||||
>>> Version('0.1.1+build2') in SpecItem('>=0.1.1') # build version satisfy specifications
|
||||
>>> Version('0.1.1+build2') in SpecItem('>=0.1.1') # build metadata is ignored when checking for precedence
|
||||
True
|
||||
>>>
|
||||
>>> # Use the '-' marker to include the pre-release component in checks
|
||||
>>> SpecItem('>=0.1.1-').match(Version('0.1.1-rc1')
|
||||
False
|
||||
>>>
|
||||
>>> # Use the '+' marker to include the build identifier in checks
|
||||
>>> SpecItem('<=0.1.1-alpha+').match(Version('0.1.1-alpha+build1'))
|
||||
>>> # Use the '+' marker to include the build metadata in checks
|
||||
>>> SpecItem('==0.1.1+').match(Version('0.1.1+b1234')
|
||||
False
|
||||
>>>
|
||||
|
||||
|
||||
.. rubric:: Attributes
|
||||
|
|
|
@ -290,20 +290,14 @@ class Version(object):
|
|||
return 0
|
||||
|
||||
def build_cmp(a, b):
|
||||
"""Compare build components.
|
||||
"""Compare build metadata.
|
||||
|
||||
Special rule: a version without build component has lower
|
||||
precedence than one with a build component.
|
||||
Special rule: there is no ordering on build metadata.
|
||||
"""
|
||||
if a and b:
|
||||
return identifier_list_cmp(a, b)
|
||||
elif a:
|
||||
# Versions with build field have higher precedence
|
||||
return 1
|
||||
elif b:
|
||||
return -1
|
||||
else:
|
||||
if a == b:
|
||||
return 0
|
||||
else:
|
||||
return NotImplemented
|
||||
|
||||
def make_optional(orig_cmp_fun):
|
||||
"""Convert a cmp-like function to consider 'None == *'."""
|
||||
|
@ -332,10 +326,7 @@ class Version(object):
|
|||
build_cmp,
|
||||
]
|
||||
|
||||
def __cmp__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
def __compare(self, other):
|
||||
field_pairs = zip(self, other)
|
||||
comparison_functions = self._comparison_functions(partial=self.partial or other.partial)
|
||||
comparisons = zip(comparison_functions, self, other)
|
||||
|
@ -347,44 +338,48 @@ class Version(object):
|
|||
|
||||
return 0
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
return self.__cmp__(other) == 0
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.major, self.minor, self.patch, self.prerelease, self.build))
|
||||
|
||||
def __ne__(self, other):
|
||||
def __cmp__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
return self.__compare(other)
|
||||
|
||||
def __compare_helper(self, other, condition, notimpl_target):
|
||||
"""Helper for comparison.
|
||||
|
||||
Allows the caller to provide:
|
||||
- The condition
|
||||
- The return value if the comparison is meaningless (ie versions with
|
||||
build metadata).
|
||||
"""
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
return self.__cmp__(other) != 0
|
||||
cmp_res = self.__cmp__(other)
|
||||
if cmp_res is NotImplemented:
|
||||
return notimpl_target
|
||||
|
||||
return condition(cmp_res)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.__compare_helper(other, lambda x: x == 0, notimpl_target=False)
|
||||
|
||||
def __ne__(self, other):
|
||||
return self.__compare_helper(other, lambda x: x != 0, notimpl_target=True)
|
||||
|
||||
def __lt__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
return self.__cmp__(other) < 0
|
||||
return self.__compare_helper(other, lambda x: x < 0, notimpl_target=False)
|
||||
|
||||
def __le__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
return self.__cmp__(other) <= 0
|
||||
return self.__compare_helper(other, lambda x: x <= 0, notimpl_target=False)
|
||||
|
||||
def __gt__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
return self.__cmp__(other) > 0
|
||||
return self.__compare_helper(other, lambda x: x > 0, notimpl_target=False)
|
||||
|
||||
def __ge__(self, other):
|
||||
if not isinstance(other, self.__class__):
|
||||
return NotImplemented
|
||||
|
||||
return self.__cmp__(other) >= 0
|
||||
return self.__compare_helper(other, lambda x: x >= 0, notimpl_target=False)
|
||||
|
||||
|
||||
class SpecItem(object):
|
||||
|
@ -420,6 +415,10 @@ class SpecItem(object):
|
|||
|
||||
kind, version = match.groups()
|
||||
spec = Version(version, partial=True)
|
||||
if spec.build is not None and kind not in (cls.KIND_EQUAL, cls.KIND_NEQ):
|
||||
raise ValueError(
|
||||
"Invalid requirement specification %r: build numbers have no ordering."
|
||||
% requirement_string)
|
||||
return (kind, spec)
|
||||
|
||||
def match(self, version):
|
||||
|
|
|
@ -2,17 +2,14 @@
|
|||
# Copyright (c) 2012-2014 The python-semanticversion project
|
||||
# This code is distributed under the two-clause BSD License.
|
||||
|
||||
import sys
|
||||
|
||||
is_python2 = (sys.version_info[0] == 2)
|
||||
|
||||
if is_python2: # pragma: no cover
|
||||
base_cmp = cmp
|
||||
else: # pragma: no cover
|
||||
def base_cmp(x, y):
|
||||
if x < y:
|
||||
return -1
|
||||
elif x > y:
|
||||
return 1
|
||||
else:
|
||||
return 0
|
||||
def base_cmp(x, y):
|
||||
if x == y:
|
||||
return 0
|
||||
elif x > y:
|
||||
return 1
|
||||
elif x < y:
|
||||
return -1
|
||||
else:
|
||||
# Fix Py2's behavior: cmp(x, y) returns -1 for unorderable types
|
||||
return NotImplemented
|
||||
|
|
|
@ -64,7 +64,7 @@ class TopLevelTestCase(unittest.TestCase):
|
|||
('0.1.1', '0.1.1', 0),
|
||||
('0.1.1', '0.1.0', 1),
|
||||
('0.1.0-alpha', '0.1.0', -1),
|
||||
('0.1.0-alpha+2', '0.1.0-alpha', 1),
|
||||
('0.1.0-alpha+2', '0.1.0-alpha', NotImplemented),
|
||||
)
|
||||
|
||||
def test_compare(self):
|
||||
|
@ -179,7 +179,6 @@ class VersionTestCase(unittest.TestCase):
|
|||
'1.1.2': (1, 1, 2, None, None),
|
||||
'1.1.3-rc4.5': (1, 1, 3, ('rc4', '5'), None),
|
||||
'1.0.0-': (1, 0, 0, (), None),
|
||||
'1.0.0+': (1, 0, 0, (), ()),
|
||||
'1.0.0-rc.1+build.1': (1, 0, 0, ('rc', '1'), ('build', '1')),
|
||||
'1.0.0+0.3.7': (1, 0, 0, (), ('0', '3', '7')),
|
||||
'1.3.7+build': (1, 3, 7, (), ('build',)),
|
||||
|
@ -272,18 +271,29 @@ class VersionTestCase(unittest.TestCase):
|
|||
|
||||
|
||||
class SpecItemTestCase(unittest.TestCase):
|
||||
invalids = [
|
||||
'<=0.1.1+build3',
|
||||
'<=0.1.1+',
|
||||
'>0.2.3-rc2+',
|
||||
]
|
||||
|
||||
def test_invalids(self):
|
||||
for invalid in self.invalids:
|
||||
with self.assertRaises(ValueError, msg="SpecItem(%r) should be invalid" % invalid):
|
||||
_v = base.SpecItem(invalid)
|
||||
|
||||
components = {
|
||||
'==0.1.0': (base.SpecItem.KIND_EQUAL, 0, 1, 0, None, None),
|
||||
'==0.1.2-rc3': (base.SpecItem.KIND_EQUAL, 0, 1, 2, ('rc3',), None),
|
||||
'==0.1.2+build3.14': (base.SpecItem.KIND_EQUAL, 0, 1, 2, (), ('build3', '14')),
|
||||
'<=0.1.1+': (base.SpecItem.KIND_LTE, 0, 1, 1, (), ()),
|
||||
'<=0.1.1': (base.SpecItem.KIND_LTE, 0, 1, 1, None, None),
|
||||
'<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_NEQ, 0, 1, 1, (), ()),
|
||||
'<=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),
|
||||
'!=0.1.1+': (base.SpecItem.KIND_NEQ, 0, 1, 1, (), ()),
|
||||
'!=0.1.1+rc3': (base.SpecItem.KIND_NEQ, 0, 1, 1, (), ('rc3',)),
|
||||
'!=0.3.0': (base.SpecItem.KIND_NEQ, 0, 3, 0, None, None),
|
||||
}
|
||||
|
||||
|
@ -335,13 +345,17 @@ class SpecItemTestCase(unittest.TestCase):
|
|||
['0.2.3-rc3', '0.2.3', '0.2.3+1', '0.2.3-rc2', '0.2.3-rc2+1'],
|
||||
['0.2.3-rc1', '0.2.2'],
|
||||
),
|
||||
'>0.2.3-rc2+': (
|
||||
['0.2.3-rc3', '0.2.3', '0.2.3-rc2+1'],
|
||||
['0.2.3-rc1', '0.2.2', '0.2.3-rc2'],
|
||||
'==0.2.3+': (
|
||||
['0.2.3'],
|
||||
['0.2.3+rc1', '0.2.4', '0.2.3-rc2'],
|
||||
),
|
||||
'>2.0.0+': (
|
||||
['2.1.1', '2.0.0+b1', '3.1.4'],
|
||||
['1.9.9', '1.9.9999', '2.0.0', '2.0.0-rc4'],
|
||||
'!=0.2.3-rc2+12': (
|
||||
['0.2.3-rc3', '0.2.3', '0.2.3-rc2+1', '0.2.4', '0.2.3-rc3+12'],
|
||||
['0.2.3-rc2+12'],
|
||||
),
|
||||
'==2.0.0+b1': (
|
||||
['2.0.0+b1'],
|
||||
['2.1.1', '1.9.9', '1.9.9999', '2.0.0', '2.0.0-rc4'],
|
||||
),
|
||||
'!=0.1.1': (
|
||||
['0.1.2', '0.1.0', '1.4.2'],
|
||||
|
@ -440,13 +454,17 @@ class SpecTestCase(unittest.TestCase):
|
|||
self.assertTrue(repr(base.SpecItem(spec_text)) in repr(spec_list))
|
||||
|
||||
matches = {
|
||||
# At least 0.1.1 including pre-releases, less than 0.1.2 excluding pre-releases
|
||||
'>=0.1.1,<0.1.2': (
|
||||
['0.1.1', '0.1.1+4', '0.1.1-alpha'],
|
||||
['0.1.2-alpha', '0.1.2', '1.3.4'],
|
||||
),
|
||||
'>=0.1.0+,!=0.1.3-rc1,<0.1.4': (
|
||||
# At least 0.1.0 without pre-releases, less than 0.1.4 excluding pre-releases,
|
||||
# neither 0.1.3-rc1 nor any build of that version,
|
||||
# not 0.1.0+b3 precisely
|
||||
'>=0.1.0-,!=0.1.3-rc1,!=0.1.0+b3,<0.1.4': (
|
||||
['0.1.1', '0.1.0+b4', '0.1.2', '0.1.3-rc2'],
|
||||
['0.0.1', '0.1.4', '0.1.4-alpha', '0.1.3-rc1+4',
|
||||
['0.0.1', '0.1.0+b3', '0.1.4', '0.1.4-alpha', '0.1.3-rc1+4',
|
||||
'0.1.0-alpha', '0.2.2', '0.1.4-rc1'],
|
||||
),
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ class MatchTestCase(unittest.TestCase):
|
|||
'<=0.1.4a',
|
||||
'>0.1.1.1',
|
||||
'~0.1.2-rc23,1',
|
||||
'<0.1.2-rc1.3-14.15+build.2012-01-01.11h34',
|
||||
]
|
||||
|
||||
valid_specs = [
|
||||
|
@ -25,7 +26,7 @@ class MatchTestCase(unittest.TestCase):
|
|||
'>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-rc1.3-14.15+build.2012-01-01.11h34',
|
||||
]
|
||||
|
||||
matches = {
|
||||
|
@ -53,11 +54,19 @@ class MatchTestCase(unittest.TestCase):
|
|||
'0.1.2',
|
||||
'0.1.2+build4',
|
||||
],
|
||||
'<0.1.2+': [
|
||||
'!=0.1.2+': [
|
||||
'0.1.2+1',
|
||||
'0.1.2-rc1',
|
||||
],
|
||||
'!=0.1.2-': [
|
||||
'0.1.1',
|
||||
'0.1.2-rc1',
|
||||
'0.1.2-rc1.3.4',
|
||||
'0.1.2-rc1+build4.5',
|
||||
],
|
||||
'!=0.1.2+345': [
|
||||
'0.1.1',
|
||||
'0.1.2-rc1+345',
|
||||
'0.1.2+346',
|
||||
'0.2.3+345',
|
||||
],
|
||||
'>=0.1.1': [
|
||||
'0.1.1',
|
||||
|
@ -72,12 +81,6 @@ class MatchTestCase(unittest.TestCase):
|
|||
'0.2.0',
|
||||
'1.0.0',
|
||||
],
|
||||
'>0.1.1+': [
|
||||
'0.1.1+b2',
|
||||
'0.1.2-rc1',
|
||||
'1.1.1',
|
||||
'2.0.4',
|
||||
],
|
||||
'<0.1.1-': [
|
||||
'0.1.1-alpha',
|
||||
'0.1.1-rc4',
|
||||
|
@ -87,7 +90,8 @@ class MatchTestCase(unittest.TestCase):
|
|||
|
||||
def test_invalid(self):
|
||||
for invalid in self.invalid_specs:
|
||||
self.assertRaises(ValueError, semantic_version.Spec, invalid)
|
||||
with self.assertRaises(ValueError, msg="Spec(%r) should be invalid" % invalid):
|
||||
semantic_version.Spec(invalid)
|
||||
|
||||
def test_simple(self):
|
||||
for valid in self.valid_specs:
|
||||
|
@ -122,11 +126,9 @@ class MatchTestCase(unittest.TestCase):
|
|||
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')
|
||||
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))
|
||||
self.assertTrue(version in spec, "%r should be in %r" % (version, spec))
|
||||
|
||||
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Copyright (c) 2012-2014 The python-semanticversion project
|
||||
# This code is distributed under the two-clause BSD License.
|
||||
|
||||
import itertools
|
||||
import unittest
|
||||
|
||||
import semantic_version
|
||||
|
@ -44,12 +45,8 @@ class ComparisonTestCase(unittest.TestCase):
|
|||
'1.0.0-beta.2',
|
||||
'1.0.0-beta.11',
|
||||
'1.0.0-rc.1',
|
||||
'1.0.0-rc.1+build.1',
|
||||
'1.0.0',
|
||||
'1.0.0+0.3.7',
|
||||
'1.3.7+build',
|
||||
'1.3.7+build.2.b8f12d7',
|
||||
'1.3.7+build.11.e0f985a',
|
||||
]
|
||||
|
||||
def test_comparisons(self):
|
||||
|
@ -67,6 +64,36 @@ class ComparisonTestCase(unittest.TestCase):
|
|||
cmp_res = -1 if i < j else (1 if i > j else 0)
|
||||
self.assertEqual(cmp_res, semantic_version.compare(first, second))
|
||||
|
||||
unordered = [
|
||||
[
|
||||
'1.0.0-rc.1',
|
||||
'1.0.0-rc.1+build.1',
|
||||
],
|
||||
[
|
||||
'1.0.0',
|
||||
'1.0.0+0.3.7',
|
||||
],
|
||||
[
|
||||
'1.3.7',
|
||||
'1.3.7+build',
|
||||
'1.3.7+build.2.b8f12d7',
|
||||
'1.3.7+build.11.e0f985a',
|
||||
],
|
||||
]
|
||||
|
||||
def test_unordered(self):
|
||||
for group in self.unordered:
|
||||
for a, b in itertools.combinations(group, 2):
|
||||
v1 = semantic_version.Version(a)
|
||||
v2 = semantic_version.Version(b)
|
||||
self.assertTrue(v1 == v1, "%r != %r" % (v1, v1))
|
||||
self.assertFalse(v1 != v1, "%r != %r" % (v1, v1))
|
||||
self.assertFalse(v1 == v2, "%r == %r" % (v1, v2))
|
||||
self.assertTrue(v1 != v2, "%r !!= %r" % (v1, v2))
|
||||
self.assertFalse(v1 < v2, "%r !< %r" % (v1, v2))
|
||||
self.assertFalse(v1 <= v2, "%r !<= %r" % (v1, v2))
|
||||
self.assertFalse(v2 > v1, "%r !> %r" % (v2, v1))
|
||||
self.assertFalse(v2 >= v1, "%r !>= %r" % (v2, v1))
|
||||
|
||||
if __name__ == '__main__': # pragma: no cover
|
||||
unittest.main()
|
||||
|
|
|
@ -154,10 +154,3 @@ class FormatTests(unittest.TestCase):
|
|||
self.assertLess(Version('1.0.0-beta.2'), Version('1.0.0-beta.11'))
|
||||
self.assertLess(Version('1.0.0-beta.11'), Version('1.0.0-rc.1'))
|
||||
self.assertLess(Version('1.0.0-rc.1'), Version('1.0.0'))
|
||||
|
||||
|
||||
|
||||
class PrecedenceTestCase(unittest.TestCase):
|
||||
pass
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue