From 92f4a2321d0d9fb802e7152f52365fc96c5ec4c5 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Sun, 24 Feb 2013 00:55:28 +1000 Subject: [PATCH] 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 --- pep-0426.txt | 183 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 117 insertions(+), 66 deletions(-) diff --git a/pep-0426.txt b/pep-0426.txt index 43fd3868e..844766446 100644 --- a/pep-0426.txt +++ b/pep-0426.txt @@ -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, + .devN, 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 ==========