Skip to content

Commit

Permalink
Fix python-semver#241: Implement tilde and caret matching
Browse files Browse the repository at this point in the history
* Introduce Spec class to deal with such comparisons
* Improve documentation
* Simplify code in Version.match (delegates to Spec.match)
  • Loading branch information
tomschr committed Nov 1, 2023
1 parent 74b0b2e commit 065f013
Show file tree
Hide file tree
Showing 11 changed files with 1,094 additions and 201 deletions.
25 changes: 25 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,29 @@ Version Handling :mod:`semver.version`

.. autoclass:: semver.version.Version
:members:
:inherited-members:
:special-members: __iter__, __eq__, __ne__, __lt__, __le__, __gt__, __ge__, __getitem__, __hash__, __repr__, __str__


Version Regular Expressions :mod:`semver.versionregex`
------------------------------------------------------

.. automodule:: semver.versionregex

.. autoclass:: semver.versionregex.VersionRegex
:members:
:private-members:


Spec Handling :mod:`semver.spec`
--------------------------------

.. automodule:: semver.spec

.. autoclass:: semver.spec.Spec
:members: match
:private-members: _caret, _tilde
:special-members: __eq__, __ne__, __lt__, __le__, __gt__, __ge__, __repr__, __str__

.. autoclass:: semver.spec.InvalidSpecifier

2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,12 @@ def find_version(*file_paths):
(None, "inventories/pydantic.inv"),
),
}

# Avoid side-effects (namely that documentations local references can
# suddenly resolve to an external location.)
intersphinx_disabled_reftypes = ["*"]


# -- Options for HTML output ----------------------------------------------

# The theme to use for HTML and HTML Help pages. See the documentation for
Expand Down
101 changes: 97 additions & 4 deletions docs/usage/compare-versions-through-expression.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Currently, the match expression supports the following operators:
* ``<=`` smaller or equal than
* ``==`` equal
* ``!=`` not equal
* ``~`` for tilde ranges, see :ref:`tilde_expressions`
* ``^`` for caret ranges, see :ref:`caret_expressions`

That gives you the following possibilities to express your condition:

Expand All @@ -31,10 +33,10 @@ That gives you the following possibilities to express your condition:
False
If no operator is specified, the match expression is interpreted as a
version to be compared for equality. This allows handling the common
case of version compatibility checking through either an exact version
or a match expression very easy to implement, as the same code will
handle both cases:
version to be compared for equality with the ``==`` operator.
This allows handling the common case of version compatibility checking
through either an exact version or a match expression very easy to
implement, as the same code will handle both cases:

.. code-block:: python
Expand All @@ -43,3 +45,94 @@ handle both cases:
True
>>> version.match("3.5.1")
False
Using the :class:`Spec <semver.spec.Spec>` class
------------------------------------------------

The :class:`Spec <semver.spec.Spec>` class is the underlying object
which makes comparison possible.

It supports comparisons through usual Python operators:

.. code-block:: python
>>> Spec("1.2") > '1.2.1'
True
>>> Spec("1.3") == '1.3.10'
False
If you need to reuse a ``Spec`` object, use the :meth:`match <semver.spec.Spec.match>` method:

.. code-block:: python
>>> spec = Spec(">=1.2.3")
>>> spec.match("1.3.1")
True
>>> spec.match("1.2.1")
False
.. _tilde_expressions:

Using tilde expressions
-----------------------

Tilde expressions are "approximately equivalent to a version".
They are expressions like ``~1``, ``~1.2``, or ``~1.2.3``.
Tilde expression freezes major and minor numbers. They are used if
you want to avoid potentially incompatible changes, but want to accept bug fixes.

Internally they are converted into two comparisons:

* ``~1`` is converted into ``>=1.0.0 <(1+1).0.0`` which is ``>=1.0.0 <2.0.0``
* ``~1.2`` is converted into ``>=1.2.0 <1.(2+1).0`` which is ``>=1.2.0 <1.3.0``
* ``~1.2.3`` is converted into ``>=1.2.3 <1.(2+1).0`` which is ``>=1.2.3 <1.3.0``

Only if both comparisions are true, the tilde expression as whole is true
as in the following examples:

.. code-block:: python
>>> version = Version(1, 2, 0)
>>> version.match("~1.2") # same as >=1.2.0 AND <1.3.0
True
>>> version.match("~1.3.2") # same as >=1.3.2 AND <1.4.0
False
.. _caret_expressions:

Using caret expressions
-----------------------

Care expressions are "compatible with a version".
They are expressions like ``^1``, ``^1.2``, or ``^1.2.3``.
Care expressions freezes the major number only.

Internally they are converted into two comparisons:

* ``^1`` is converted into ``>=1.0.0 <2.0.0``
* ``^1.2`` is converted into ``>=1.2.0 <2.0.0``
* ``^1.2.3`` is converted into ``>=1.2.3 <2.0.0``

.. code-block:: python
>>> version = Version(1, 2, 0)
>>> version.match("^1.2") # same as >=1.2.0 AND <2.0.0
True
>>> version.match("^1.3")
False
It is possible to add placeholders to the care expression. Placeholders
are ``x``, ``X``, or ``*`` and are replaced by zeros like in the following examples:

.. code-block:: python
>>> version = Version(1, 2, 3)
>>> version.match("^1.x") # same as >=1.0.0 AND <2.0.0
True
>>> version.match("^1.2.x") # same as >=1.2.0 AND <2.0.0
True
>>> version.match("^1.3.*") # same as >=1.3.0 AND <2.0.0
False
1 change: 1 addition & 0 deletions src/semver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
main,
)
from .version import Version, VersionInfo
from .spec import Spec
from .__about__ import (
__version__,
__author__,
Expand Down
Loading

0 comments on commit 065f013

Please sign in to comment.