PEP 426: Propose Ruby's "pessimistic version constraints"

- makes the default handling of version specifiers match Ruby's ~> operator
- explicitly base == and != on string prefix matching
- cleaned up various examples related to the version specifiers
- give the version specifiers section more structure
This commit is contained in:
Nick Coghlan 2013-02-24 00:55:28 +10:00
parent 6f23bce51b
commit 92f4a2321d
1 changed files with 117 additions and 66 deletions

View File

@ -861,7 +861,7 @@ ordered as shown::
Within a post-release (``1.0.post1``), the following suffixes are permitted
and are ordered as shown::
devN, <no suffix>
.devN, <no suffix>
Note that ``devN`` and ``postN`` must always be preceded by a dot, even
when used immediately following a numeric version (e.g. ``1.0.dev456``,
@ -976,8 +976,9 @@ Date based versions
As with other incompatible version schemes, date based versions can be
stored in the ``Private-Version`` field. Translating them to a compliant
version is straightforward: the simplest approach is to subtract the year
of the first release from the major component in the release number.
public version is straightforward: the simplest approach is to subtract
the year before the first release from the major component in the release
number.
Version specifiers
@ -987,61 +988,106 @@ A version specifier consists of a series of version clauses, separated by
commas. Each version clause consists of an optional comparison operator
followed by a version identifier. For example::
0.9, >= 1.0, != 1.3.4, < 2.0, ~= 2.0
0.9, >= 1.0, != 1.3.4, < 2.0
Each version identifier must be in the standard format described in
`Version scheme`_.
The comma (",") is equivalent to a logical **and** operator.
Comparison operators must be one of ``<``, ``>``, ``<=``, ``>=``, ``==``,
``!=`` or ``~=``.
The ``==`` and ``!=`` operators are strict - in order to match, the
version supplied must exactly match the specified version, with no
additional trailing suffix. When no comparison operator is provided,
it is equivalent to ``==``.
The special ``~=`` operator is equivalent to using the following pair
of version clauses::
>= V, < V+1
where ``V+1`` is the next version after ``V``, as determined by
incrementing the last numeric component in ``V`` (for example, if ``V ==
1.0a3``, then ``V+1 == 1.0a4``, while if ``V == 1.0``, then ``V+1 ==
1.1``). In other words, this operator matches any release that starts
with the mentioned components.
This approach makes it easy to depend on a particular release series
simply by naming it in a version specifier, without requiring any
additional annotation. For example, the following pairs of version
specifiers are equivalent::
~= 2
>= 2, < 3
~= 3.3
>= 3.3, < 3.4
Whitespace between a conditional operator and the following version
identifier is optional, as is the whitespace around the commas.
Compatible release
------------------
A compatible release clause omits the comparison operator and matches any
version that is expected to be compatible with the specified version.
For a given release identifier ``V.N``, the compatible release clause is
equivalent to the pair of comparison clauses::
>= V.N, < V+1
where ``V+1`` is the next version after ``V``, as determined by
incrementing the last numeric component in ``V``. For example,
the following version clauses are approximately equivalent::
2.2
>= 2.2, < 3.dev0
1.4.5
>= 1.4.5, < 1.5.dev0
The difference between the two is that using a compatible release clause
does *not* count as `explicitly mentioning a pre-release`__.
__ `Handling of pre-releases`_
If a pre-release, post-release or developmental release is named in a
compatible release clause as ``V.N.suffix``, then the suffix is ignored
when determining the upper limit of compatibility::
2.2.post3
>= 2.2.post3, < 3.dev0
1.4.5a4
>= 1.4.5a4, < 1.5.dev0
Version comparisons
-------------------
A version comparison clause includes a comparison operator and a version
identifier, and will match any version where the comparison is true.
Comparison clauses are only needed to cover cases which cannot be handled
with an appropriate compatible release clause, including coping with
dependencies which do not have a robust backwards compatibility policy
and thus break the assumptions of a compatible release clause.
The defined comparison operators are ``<``, ``>``, ``<=``, ``>=``, ``==``,
and ``!=``.
The ordered comparison operators ``<``, ``>``, ``<=``, ``>=`` are based
on the consistent ordering defined by the standard `Version scheme`_.
The ``==`` and ``!=`` operators are based on string comparisons - in order
to match, the version being checker must start with exactly that sequence of
characters.
.. note::
The use of ``==`` when defining dependencies for published distributions
is strongly discouraged, as it greatly complicates the deployment of
security fixes (the strict version comparison operator is intended
primarily for use when defining dependencies for particular
applications while using a shared distribution index).
Handling of pre-releases
------------------------
Pre-releases of any kind, including developmental releases, are implicitly
excluded from all version specifiers, *unless* a pre-release or developmental
developmental release is explicitly mentioned in one of the clauses. For
example, this specifier implicitly excludes all pre-releases and development
release is explicitly mentioned in one of the clauses. For example, these
specifiers implicitly exclude all pre-releases and development
releases of later versions::
2.2
>= 1.0
While these specifiers would include them::
While these specifiers would include at least some of them::
2.2.dev0
2.2, != 2.3b2
>= 1.0a1
>= 1.0c1
>= 1.0, != 1.0b2
>= 1.0, < 2.0.dev123
Dependency resolution tools should use the above rules by default, but
should also allow users to request the following alternative behaviours:
@ -1054,34 +1100,26 @@ controlled on a per-distribution basis.
Post-releases and purely numeric releases receive no special treatment -
they are always included unless explicitly excluded.
Given the above rules, projects which include the ``.0`` suffix for
the first release in a series, such as ``2.5.0``, can easily refer
specifically to that version with the clause ``==2.5.0``, while the clause
``~=2.5`` refers to that entire series.
Some examples:
Examples
--------
* ``Requires-Dist: zope.interface (~=3.1)``: any version that starts with 3.1,
* ``Requires-Dist: zope.interface (3.1)``: version 3.1 or later, but not
version 4.0 or later. Excludes pre-releases and developmental releases.
* ``Requires-Dist: zope.interface (3.1.0)``: version 3.1.0 or later, but not
version 3.2.0 or later. Excludes pre-releases and developmental releases.
* ``Requires-Dist: zope.interface (==3.1)``: any version that starts
with 3.1, excluding pre-releases and developmental releases.
* ``Requires-Dist: zope.interface (3.1.0,!=3.1.3)``: version 3.1.0 or later,
but not version 3.1.3 and not version 3.2.0 or later. Excludes pre-releases
and developmental releases. For this particular project, this means: "any
version of the 3.1 series but not 3.1.3". This is equivalent to:
``>=3.1, !=3.1.3, <3.2``.
* ``Requires-Python: 2.6``: Any version of Python 2.6 or 2.7. It
automatically excludes Python 3 or later.
* ``Requires-Python: 3.2, < 3.3``: Specifically requires Python 3.2,
excluding pre-releases.
* ``Requires-Dist: zope.interface (==3.1)``: equivalent to ``Requires-Dist:
zope.interface (3.1)``.
* ``Requires-Dist: zope.interface (~=3.1.0)``: any version that starts with
3.1.0, excluding pre-releases. Since that particular project doesn't
use more than 3 digits, it also means "only the 3.1.0 release".
* ``Requires-Python: 3``: Any Python 3 version, excluding pre-releases.
* ``Requires-Python: >=2.6,<3``: Any version of Python 2.6 or 2.7, including
post-releases (if they were used for Python). It excludes pre releases of
Python 3.
* ``Requires-Python: ~=2.6.2``: Equivalent to ">=2.6.2,<2.6.3". So this includes
only Python 2.6.2. Of course, if Python was numbered with 4 digits, it would
include all versions of the 2.6.2 series, excluding pre-releases.
* ``Requires-Python: ~=2.5``: Equivalent to ">=2.5,<2.6".
* ``Requires-Dist: zope.interface (~=3.1,!=3.1.3)``: any version that starts
with 3.1, excluding pre-releases of 3.1 *and* excluding any version that
starts with "3.1.3". For this particular project, this means: "any version
of the 3.1 series but not 3.1.3". This is equivalent to:
">=3.1,!=3.1.3,<3.2".
* ``Requires-Python: >=3.3a1``: Any version of Python 3.3+, including
* ``Requires-Python: 3.3a1``: Any version of Python 3.3+, including
pre-releases like 3.4a1.
@ -1437,10 +1475,10 @@ Changing the interpretation of version specifiers
The previous interpretation of version specifiers made it very easy to
accidentally download a pre-release version of a dependency. This in
turn made it difficult for developers to publish pre-release versions
of software to the Python Package Index, as leaving the package set as
public would lead to users inadvertently downloading pre-release software,
while hiding it would defeat the purpose of publishing it for user
testing.
of software to the Python Package Index, as even marking the package as
hidden wasn't enough to keep automated tools from downloading it, and also
made it harder for users to obtain the test release manually through the
main PyPI web interface.
The previous interpretation also excluded post-releases from some version
specifiers for no adequately justified reason.
@ -1449,6 +1487,16 @@ The updated interpretation is intended to make it difficult to accidentally
accept a pre-release version as satisfying a dependency, while allowing
pre-release versions to be explicitly requested when needed.
The "some forward compatibility assumed" default version constraint is
taken directly from the Ruby community's "pessimistic version constraint"
operator [4]_ to allow projects to take a cautious approach to forward
compatibility promises, while still easily setting a minimum required
version for their dependencies. It is made the default behaviour rather
than needing a separate operator in order to explicitly discourage
overspecification of dependencies by library developers. The explicit
comparison operators remain available to cope with dependencies with
unreliable or non-existent backwards compatibility policies.
Packaging, build and installation dependencies
----------------------------------------------
@ -1546,6 +1594,9 @@ justifications for needing such a standard can be found in PEP 386.
.. [3] Version compatibility analysis script:
http://hg.python.org/peps/file/default/pep-0426/pepsort.py
.. [4] Pessimistic version constraint
http://docs.rubygems.org/read/chapter/16
Appendix A
==========