Guide

This module covers the 2.0.0 version of the SemVer scheme, with additional extensions:

  • Coercing any version string into a SemVer version, through Version.coerce();
  • Comparing versions;
  • Computing next versions;
  • Modelling version range specifcations, and choosing the best match – for both its custom logic, and NPM semantics (custom range specification schemes can be added).

Version basics

Building Version instances

The core of the module is the Version class; it is usually instantiated from a version string:

>>> import semantic_version as semver
>>> v = semver.Version("0.1.1")

The version’s components are available through its attributes:

  • major, minor, patch are integers:

    >>> v.major
    0
    >>> v.minor
    1
    >>> v.patch
    1
    
  • The prerelease and build attributes are iterables of text elements:

    >>> v2 = semver.Version("0.1.1-dev+23.git2")
    >>> v2.prerelease
    ["dev"]
    >>> v2.build
    ["23", "git2"]
    

One may also build a Version from named components directly:

>>> semantic_version.Version(major=0, minor=1, patch=2)
Version('0.1.2')

In that case, major, minor and patch are mandatory, and must be integers. prerelease and build, if provided, must be tuples of strings:

>>> semantic_version.Version(major=0, minor=1, patch=2, prerelease=('alpha', '2'))
Version('0.1.2-alpha.2')

If the provided version string is invalid, a ValueError will be raised:

>>> semver.Version('0.1')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/rbarrois/dev/semantic_version/src/semantic_version/base.py", line 64, in __init__
    major, minor, patch, prerelease, build = self.parse(version_string, partial)
  File "/Users/rbarrois/dev/semantic_version/src/semantic_version/base.py", line 86, in parse
    raise ValueError('Invalid version string: %r' % version_string)
ValueError: Invalid version string: '0.1'

Working with non-SemVer version strings

Some user-supplied input might not match the semantic version scheme. For such cases, the Version.coerce method will try to convert any version-like string into a valid semver version:

>>> semver.Version.coerce('0')
Version('0.0.0')
>>> semver.Version.coerce('0.1.2.3.4')
Version('0.1.2+3.4')
>>> semver.Version.coerce('0.1.2a3')
Version('0.1.2-a3')

Comparing versions

Versions can be compared, following the SemVer scheme:

>>> semver.Version("0.1.0") < semver.Version("0.1.1")
True
>>> max(
...   semver.Version("0.1.0"),
...   semver.Version("0.2.2"),
...   semver.Version("0.1.1"),
...   semver.Version("0.2.2-rc1"),
... )
Version("0.2.2")

Note

As defined in SemVer, build metadata is ignored in comparisons, but not in equalities:

>>> semver.Version("0.1.2") <= semver.Version("0.1.2+git2")
True
>>> semver.Version("0.1.2") >= semver.Version("0.1.2+git2")
True
>>> semver.Version("0.1.2") == semver.Version("0.1.2+git2")
False

Iterating versions

One can get a new version that represents a bump in one of the version levels through the Version.next_major(), Version.next_minor() or Version.next_patch() functions:

>>> v = semver.Version('0.1.1+build')
>>> new_v = v.next_major()
>>> str(new_v)
'1.0.0'
>>> v = semver.Version('1.1.1+build')
>>> new_v = v.next_minor()
>>> str(new_v)
'1.2.0'
>>> v = semver.Version('1.1.1+build')
>>> new_v = v.next_patch()
>>> str(new_v)
'1.1.2'

Note

  • If the version includes build or prerelease metadata, that value will be empty in the next version;
  • The next patch following a version with a pre-release identifier is the same version with its prerelease and build identifiers removed: Version("0.1.1-rc1").next_patch() == Version("0.1.1")
  • Pre-release and build naming schemes are often custom and specific to a project’s internal design; thus, the library can’t provide a next_xxx method for those fields.

One may also truncate versions through the Version.truncate() method, removing components beyond the selected level:

>>> v = semver.Version("0.1.2-dev+git3")
>>> v.truncate("prerelease")
Version("0.1.2-dev")
>>> v.truncate("minor")
Version("0.1.0")

Range specifications

Comparing version numbers isn’t always enough; in many situations, one needs to define a range of acceptable versions.

That notion is not defined in SemVer; moreover, several systems exists, with their own notations.

The semantic_version package provides a couple of implementations for these notions:

  • SimpleSpec is a simple implementation, with reasonable expectations;
  • NpmSpec sticks to the NPM specification.

Further schemes can be built in a similar manner, relying on the BaseSpec class for basics.

Core API

The core API is provided by the BaseSpec class.

Note

These examples use SimpleSpec in order to be easily reproduced by users, but only exhibit the standard parts of the interface.

It is possible to check whether a given Version matches a BaseSpec through match():

>>> s = semver.SimpleSpec(">=0.1.1")
>>> s.match(Version("0.1.1"))
True
>>> s.match(Version("0.1.0"))
False

This feature is also available through the in keyword:

>>> s = semver.SimpleSpec(">=0.1.1")
>>> Version("0.1.1") in s
True
>>> Version("0.1.0") in s
False

A specification can filter compatible values from an iterable of versions with filter():

>>> s = semver.SimpleSpec(">=0.2.1")
>>> versions = [
...   Version("0.1.0"),
...   Version("0.2.0"),
...   Version("0.3.0"),
...   Version("0.4.0"),
... ]
>>> list(s.filter(versions))
[Version("0.3.0"), Version("0.4.0")]

It can also select the “best” version from such an iterable through select():

>>> s = semver.SimpleSpec(">=0.2.1")
>>> versions = [
...   Version("0.1.0"),
...   Version("0.2.0"),
...   Version("0.3.0"),
...   Version("0.4.0"),
... ]
>>> s.select(versions)
Version("0.4.0")

The SimpleSpec scheme

The SimpleSpec provides a hopefully intuitive version range specification scheme:

  • A specification expression is composed of comma-separated clauses;
  • Each clause can be:
    • An equality match (== or !=);
    • A comparison (>, >=, < , <=);
    • A compatible release clause, PyPI style (~=2.2 for >=2.2.0,<3.0.0);
    • An NPM style clause:
      • ~1.2.3 for >=1.2.3,<1.3.0;
      • ^1.3.4 for >=1.3.4,<2.0.0;
  • The range in each clause may include a wildcard:
    • ==0.1.* maps to >=0.1.0,<0.2.0;
    • ==1.* or ==1.*.* map to >=1.0.0,<2.0.0

Special matching rules

When testing a Version against a SimpleSpec, comparisons are adjusted for common user expectations; thus, a pre-release version (1.0.0-alpha) will not satisfy the ==1.0.0 SimpleSpec.

Pre-release identifiers will only be compared if included in the BaseSpec definition or (for the empty pre-release number) if a single dash is appended (1.0.0-):

>>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0')  # No pre-release identifier
False
>>> Version('0.1.0-alpha') in SimpleSpec('<0.1.0-')  # Include pre-release in checks
True

Build metadata has no ordering; thus, the only meaningful comparison including build metadata is equality:

>>> Version('1.0.0+build2') in SimpleSpec('<=1.0.0')   # Build metadata ignored
True
>>> Version('1.0.0+build1') in SimpleSpec('==1.0.0+build2')  # Include build in checks
False

Note

The full documentation is provided in the reference section for the SimpleSpec class.

The NpmSpec scheme

The NpmSpec class implements the full NPM specification (from https://github.com/npm/node-semver#ranges):

>>> semver.Version("0.1.2") in semver.NpmSpec("0.1.0-alpha.2 .. 0.2.4")
True
>>> semver.Version('0.1.2') in semver.NpmSpec('>=0.1.1 <0.1.3 || 2.x')
True
>>> semver.Version('2.3.4') in semver.NpmSpec('>=0.1.1 <0.1.3 || 2.x')
True

Using with Django

The semantic_version.django_fields module provides django fields to store Version or BaseSpec objects.

More documentation is available in the Interaction with Django section.