summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphaël Barrois <raphael.barrois@polytechnique.org>2019-08-24 00:08:16 +0200
committerRaphaël Barrois <raphael.barrois@polytechnique.org>2019-08-26 21:34:44 +0200
commit7688b54c4a6005a75301a2e6a477f402311a6a2d (patch)
tree842637245bc856b4dcca385d8a02aa3521590b16
parentc4c6ab0e925d8cfabb68d34a10a783cb854b63a0 (diff)
downloadsemantic-version-7688b54c4a6005a75301a2e6a477f402311a6a2d.tar.gz
Add deprecations for Spec/SpecItem.
The internal features from those classes will be removed in future versions: - The `Spec` class is incompatible with the support of multiple syntaxes - The `SpecItem` class was an implementation detail, but doesn't support complex `Range` combinations.
-rw-r--r--ChangeLog6
-rw-r--r--README.rst28
-rw-r--r--docs/django.rst2
-rw-r--r--docs/reference.rst249
-rw-r--r--semantic_version/__init__.py2
-rw-r--r--semantic_version/base.py55
6 files changed, 204 insertions, 138 deletions
diff --git a/ChangeLog b/ChangeLog
index 8ef6048..31ee8aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -10,11 +10,17 @@ ChangeLog
(``Version(major=1, minor=2, patch=3)``)
* Add ``Version.truncate()`` to build a truncated copy of a ``Version``
* Add ``NpmSpec(...)``, following strict NPM matching rules (https://docs.npmjs.com/misc/semver)
+ * Add ``Spec.parse('xxx', syntax='<syntax>')`` for simpler multi-syntax support
*Bugfix:*
* Fix inconsistent behaviour regarding versions with a prerelease specification.
+*Deprecated:*
+
+ * Deprecate the ``Spec`` class (Removed in 3.1); use the ``SimpleSpec`` class instead
+ * Deprecate the internal ``SpecItem`` class (Removed in 3.0).
+
*Removed:*
* Remove support for Python2 (End of life 4 months after this release)
diff --git a/README.rst b/README.rst
index 257b2a4..2adef1b 100644
--- a/README.rst
+++ b/README.rst
@@ -64,7 +64,7 @@ This module provides classes to handle semantic versions:
- :class:`Version` represents a version number (``0.1.1-alpha+build.2012-05-15``)
- :class:`BaseSpec`-derived classes represent requirement specifications (``>=0.1.1,<0.3.0``):
- - :class:`NativeSpec` describes a natural description syntax
+ - :class:`SimpleSpec` describes a natural description syntax
- :class:`NpmSpec` is used for NPM-style range descriptions.
Versions
@@ -171,12 +171,12 @@ In that case, ``major``, ``minor`` and ``patch`` are mandatory, and must be inte
Requirement specification
-------------------------
-The :class:`NativeSpec` object describes a range of accepted versions:
+The :class:`SimpleSpec` object describes a range of accepted versions:
.. code-block:: pycon
- >>> s = NativeSpec('>=0.1.1') # At least 0.1.1
+ >>> s = SimpleSpec('>=0.1.1') # At least 0.1.1
>>> s.match(Version('0.1.1'))
True
>>> s.match(Version('0.1.1-alpha1')) # pre-release satisfy version spec
@@ -188,7 +188,7 @@ Simpler test syntax is also available using the ``in`` keyword:
.. code-block:: pycon
- >>> s = NativeSpec('==0.1.1')
+ >>> s = SimpleSpec('==0.1.1')
>>> Version('0.1.1-alpha1') in s
True
>>> Version('0.1.2') in s
@@ -199,17 +199,17 @@ Combining specifications can be expressed as follows:
.. code-block:: pycon
- >>> NativeSpec('>=0.1.1,<0.3.0')
+ >>> SimpleSpec('>=0.1.1,<0.3.0')
Using a specification
"""""""""""""""""""""
-The :func:`NativeSpec.filter` method filters an iterable of :class:`Version`:
+The :func:`SimpleSpec.filter` method filters an iterable of :class:`Version`:
.. code-block:: pycon
- >>> s = NativeSpec('>=0.1.0,<0.4.0')
+ >>> s = SimpleSpec('>=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
@@ -222,7 +222,7 @@ It is also possible to select the 'best' version from such iterables:
.. code-block:: pycon
- >>> s = NativeSpec('>=0.1.0,<0.4.0')
+ >>> s = SimpleSpec('>=0.1.0,<0.4.0')
>>> versions = (Version('0.%d.0' % i) for i in range(6))
>>> s.select(versions)
Version('0.3.0')
@@ -248,9 +248,9 @@ version-like string into a valid semver version:
Including pre-release identifiers in specifications
"""""""""""""""""""""""""""""""""""""""""""""""""""
-When testing a :class:`Version` against a :class:`NativeSpec`, comparisons are
+When testing a :class:`Version` against a :class:`SimpleSpec`, comparisons are
adjusted for common user expectations; thus, a pre-release version (``1.0.0-alpha``)
-will not satisfy the ``==1.0.0`` :class:`NativeSpec`.
+will not satisfy the ``==1.0.0`` :class:`SimpleSpec`.
Pre-release identifiers will only be compared if included in the :class:`BaseSpec`
definition or (for the empty pre-release number) if a single dash is appended
@@ -259,9 +259,9 @@ definition or (for the empty pre-release number) if a single dash is appended
.. code-block:: pycon
- >>> Version('0.1.0-alpha') in NativeSpec('<0.1.0') # No pre-release identifier
+ >>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0') # No pre-release identifier
False
- >>> Version('0.1.0-alpha') in NativeSpec('<0.1.0-') # Include pre-release in checks
+ >>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0-') # Include pre-release in checks
True
@@ -274,9 +274,9 @@ build metadata is equality.
.. code-block:: pycon
- >>> Version('1.0.0+build2') in NativeSpec('<=1.0.0') # Build metadata ignored
+ >>> Version('1.0.0+build2') in SimpleSpec('<=1.0.0') # Build metadata ignored
True
- >>> Version('1.0.0+build1') in NativeSpec('==1.0.0+build2') # Include build in checks
+ >>> Version('1.0.0+build1') in SimpleSpec('==1.0.0+build2') # Include build in checks
False
diff --git a/docs/django.rst b/docs/django.rst
index ab98e67..382b332 100644
--- a/docs/django.rst
+++ b/docs/django.rst
@@ -32,6 +32,6 @@ with their :attr:`~django.db.models.CharField.max_length` defaulting to 200.
.. attribute:: syntax
- The syntax to use for the field; defaults to ``'native'``.
+ The syntax to use for the field; defaults to ``'simple'``.
.. versionaddedd:: 2.7
diff --git a/docs/reference.rst b/docs/reference.rst
index feaffcf..62a2b3b 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -319,110 +319,21 @@ Version specifications (the Spec class)
Version specifications describe a 'range' of accepted versions:
older than, equal, similar to, …
-The main issue with representing version specifications is that the usual syntax
-does not map well onto `SemVer`_ precedence rules:
+python-semanticversion supports different syntaxes for describing version range:
-* A specification of ``<1.3.4`` is not expected to allow ``1.3.4-rc2``, but strict `SemVer`_ comparisons allow it ;
- 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``).
+- ``'simple'`` (through :class:`SimpleSpec`): A python-semantic specific syntax, which supports common patterns, and some NPM-inspired extensions;
+- ``'npm'`` (through :class:`NpmSpec`): The NPM syntax, based on https://docs.npmjs.com/misc/semver.html
-In order to have version specification behave naturally, the rules are the following:
+.. class:: BaseSpec(spec_string)
-* If no pre-release number was included in the specification, versions with a pre-release
- numbers are excluded from matching that specification.
-* If no build metadata was included in the specification, build metadata is ignored
- when deciding whether a version satisfies a specification.
-
-This means that::
-
- >>> Version('1.1.1-rc1') in Spec('<1.1.1')
- False
- >>> Version('1.1.1-rc1') in Spec('<1.1.1-rc4')
- 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')
- False
-
-
-.. note:: python-semanticversion also accepts ``"*"`` as a version spec,
- that matches all (valid) version strings.
-
-.. note:: python-semanticversion supports PyPI-style `compatible release clauses`_:
-
- * ``~=2.2`` means "Any release between 2.2.0 and 3.0.0"
- * ``~=1.4.5`` means "Any release between 1.4.5 and 1.5.0"
-
-.. note:: python-semanticversion includes support for NPM-style specs:
-
- * ``~1.2.3`` means "Any release between 1.2.3 and 1.3.0"
- * ``^1.3.4`` means "Any release between 1.3.4 and 2.0.0"
-
-In order to force matches to *strictly* compare version numbers, these additional
-rules apply:
-
-* Setting a pre-release separator without a pre-release identifier (``<=1.1.1-``)
- forces match to take into account pre-release version::
-
- >>> Version('1.1.1-rc1') in Spec('<1.1.1')
- False
- >>> Version('1.1.1-rc1') in Spec('<1.1.1-')
- True
-
-* 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') 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 built from a comma-separated list of version specifications::
-
- >>> Spec('>=1.0.0,<1.2.0,!=1.1.4')
- <Spec: (
- <SpecItem: >= Version('1.0.0', partial=True)>,
- <SpecItem: < Version('1.2.0', partial=True)>,
- <SpecItem: != Version('1.1.4', partial=True)>
- )>
-
- Version specifications may also be passed in separated arguments::
-
- >>> Spec('>=1.0.0', '<1.2.0', '!=1.1.4,!=1.1.13')
- <Spec: (
- <SpecItem: >= Version('1.0.0', partial=True)>,
- <SpecItem: < Version('1.2.0', partial=True)>,
- <SpecItem: != Version('1.1.4', partial=True)>,
- <SpecItem: != Version('1.1.13', partial=True)>,
- )>
+ Converts an expression describing a range of versions into a set of clauses,
+ and matches any :class:`Version` against those clauses.
.. rubric:: Attributes
-
- .. attribute:: specs
-
- Tuple of :class:`SpecItem`, the included specifications.
-
+ This class has no public attributes.
.. rubric:: Methods
@@ -484,15 +395,6 @@ rules apply:
>>> str(Spec('>=0.1.1,!=0.1.2'))
'>=0.1.1,!=0.1.2'
- .. method:: __iter__(self)
-
- Returns an iterator over the contained specs::
-
- >>> for spec in Spec('>=0.1.1,!=0.1.2'):
- ... print spec
- >=0.1.1
- !=0.1.2
-
.. method:: __hash__(self)
Provides a hash based solely on the hash of contained specs.
@@ -503,18 +405,99 @@ rules apply:
.. rubric:: Class methods
- .. classmethod:: parse(self, specs_string)
+ .. classmethod:: parse(self, expression, syntax='simple')
- Retrieve a ``(*specs)`` tuple from a string.
+ Retrieve a :class:`BaseSpec` object tuple from a string.
:param str requirement_string: The textual description of the specifications
+ :param str syntax: The identifier of the syntax to use for parsing
:raises: :exc:`ValueError`: if the ``requirement_string`` is invalid.
- :rtype: ``(*spec)`` tuple
+ :rtype: :class:`BaseSpec` subclass
+
+ .. versionchanged:: 2.7
+ This method used to return a tuple of :class:`SpecItem` objects.
+
+
+.. class:: SimpleSpec(spec_string)
+
+ .. versionadded:: 2.7
+ Previously reachable through :class:`Spec`.
+
+ Applies the python-semanticversion range specification:
+
+ 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 ;
+ 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``).
+
+
+ In order to have version specification behave naturally, the rules are the following:
+ * If no pre-release number was included in the specification, versions with a pre-release
+ numbers are excluded from matching that specification.
+ * If no build metadata was included in the specification, build metadata is ignored
+ when deciding whether a version satisfies a specification.
+
+ This means that::
+
+ >>> Version('1.1.1-rc1') in Spec('<1.1.1')
+ False
+ >>> Version('1.1.1-rc1') in Spec('<1.1.1-rc4')
+ 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')
+ False
+
+
+ .. note:: python-semanticversion also accepts ``"*"`` as a version spec,
+ that matches all (valid) version strings.
+
+ .. note:: python-semanticversion supports PyPI-style `compatible release clauses`_:
+
+ * ``~=2.2`` means "Any release between 2.2.0 and 3.0.0"
+ * ``~=1.4.5`` means "Any release between 1.4.5 and 1.5.0"
+
+ .. note:: python-semanticversion includes support for NPM-style specs:
+
+ * ``~1.2.3`` means "Any release between 1.2.3 and 1.3.0"
+ * ``^1.3.4`` means "Any release between 1.3.4 and 2.0.0"
+
+ In order to force matches to *strictly* compare version numbers, these additional
+ rules apply:
+
+ * Setting a pre-release separator without a pre-release identifier (``<=1.1.1-``)
+ forces match to take into account pre-release version::
+
+ >>> Version('1.1.1-rc1') in Spec('<1.1.1')
+ False
+ >>> Version('1.1.1-rc1') in Spec('<1.1.1-')
+ True
+
+ * 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') 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:: NpmSpec(spec_string)
- .. versionadded:: 2.8
+ .. versionadded:: 2.7
A NPM-compliant version matching engine, based on the https://docs.npmjs.com/misc/semver.html specification.
@@ -528,8 +511,56 @@ rules apply:
True
+.. class:: Spec(spec_string)
+
+ .. deprecated:: 2.7
+ The alias from :class:`Spec` to :class:`SimpleSpec` will be removed in 3.1.
+
+ Alias to :class:`LegacySpec`, for backwards compatibility.
+
+
+.. class:: LegacySpec(spec_string)
+
+ .. deprecated:: 2.7
+ The :class:`LegacySpec` class will be removed in 3.0; use :class:`SimpleSpec` instead.
+
+ A :class:`LegacySpec` class has the exact same behaviour as :class:`SimpleSpec`, with
+ backwards-compatible features:
+
+ It accepts version specifications passed in as separated arguments::
+
+ >>> Spec('>=1.0.0', '<1.2.0', '!=1.1.4,!=1.1.13')
+ <Spec: (
+ <SpecItem: >= Version('1.0.0', partial=True)>,
+ <SpecItem: < Version('1.2.0', partial=True)>,
+ <SpecItem: != Version('1.1.4', partial=True)>,
+ <SpecItem: != Version('1.1.13', partial=True)>,
+ )>
+
+ Its keeps a list of :class:`SpecItem` objects, based on the initial expression
+ components.
+
+ .. method:: __iter__(self)
+
+ Returns an iterator over the contained specs::
+
+ >>> for spec in Spec('>=0.1.1,!=0.1.2'):
+ ... print spec
+ >=0.1.1
+ !=0.1.2
+
+ .. rubric:: Attributes
+
+ .. attribute:: specs
+
+ Tuple of :class:`SpecItem`, the included specifications.
+
+
.. class:: SpecItem(spec_string)
+ .. deprecated:: 2.7
+ This class will be removed in 3.0.
+
.. note:: This class belong to the private python-semanticversion API.
Stores a version specification, defined from a string::
diff --git a/semantic_version/__init__.py b/semantic_version/__init__.py
index e04e0ba..6092e59 100644
--- a/semantic_version/__init__.py
+++ b/semantic_version/__init__.py
@@ -3,7 +3,7 @@
# This code is distributed under the two-clause BSD License.
-from .base import compare, match, validate, NativeSpec, NpmSpec, Spec, SpecItem, Version
+from .base import compare, match, validate, SimpleSpec, NpmSpec, Spec, SpecItem, Version
__author__ = "Raphaël Barrois <raphael.barrois+semver@polytechnique.org>"
diff --git a/semantic_version/base.py b/semantic_version/base.py
index 9b517ca..2b1a9b3 100644
--- a/semantic_version/base.py
+++ b/semantic_version/base.py
@@ -4,6 +4,7 @@
import functools
import re
+import warnings
def _to_int(value):
@@ -534,7 +535,13 @@ class SpecItem:
re_spec = re.compile(r'^(<|<=||=|==|>=|>|!=|\^|~|~=)(\d.*)$')
- def __init__(self, requirement_string):
+ def __init__(self, requirement_string, _warn=True):
+ if _warn:
+ warnings.warn(
+ "The `SpecItem` class will be removed in 3.0.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
kind, spec = self.parse(requirement_string)
self.kind = kind
self.spec = spec
@@ -568,11 +575,11 @@ class SpecItem:
@classmethod
def from_matcher(cls, matcher):
if matcher == Always():
- return cls('*')
+ return cls('*', _warn=False)
elif matcher == Never():
- return cls('<0.0.0-')
+ return cls('<0.0.0-', _warn=False)
elif isinstance(matcher, Range):
- return cls('%s%s' % (matcher.operator, matcher.target))
+ return cls('%s%s' % (matcher.operator, matcher.target), _warn=False)
def match(self, version):
return self._clause.match(version)
@@ -1002,18 +1009,10 @@ class Range(Matcher):
@BaseSpec.register_syntax
-class Spec(BaseSpec):
+class SimpleSpec(BaseSpec):
SYNTAX = 'simple'
- def __init__(self, expression, *legacy_extras):
- expression = ','.join((expression,) + legacy_extras)
- super().__init__(expression)
-
- def __iter__(self):
- for clause in self.clause:
- yield SpecItem.from_matcher(clause)
-
@classmethod
def _parse_to_clause(cls, expression):
return cls.Parser.parse(expression)
@@ -1183,6 +1182,36 @@ class Spec(BaseSpec):
return Range(Range.OP_LTE, target)
+class LegacySpec(SimpleSpec):
+ def __init__(self, *expressions):
+ warnings.warn(
+ "The Spec() class will be removed in 3.1; use SimpleSpec() instead.",
+ PendingDeprecationWarning,
+ stacklevel=2,
+ )
+
+ if len(expressions) > 1:
+ warnings.warn(
+ "Passing 2+ arguments to SimpleSpec will be removed in 3.0; concatenate them with ',' instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ expression = ','.join(expressions)
+ super().__init__(expression)
+
+ def __iter__(self):
+ warnings.warn(
+ "Iterating over the components of a SimpleSpec object will be removed in 3.0.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ for clause in self.clause:
+ yield SpecItem.from_matcher(clause)
+
+
+Spec = LegacySpec
+
+
@BaseSpec.register_syntax
class NpmSpec(BaseSpec):
SYNTAX = 'npm'