massive editing to extend the rational - thanks Pachi

This commit is contained in:
Tarek Ziadé 2009-12-02 22:14:13 +00:00
parent 1356583428
commit 8436e4f7bb
1 changed files with 79 additions and 49 deletions

View File

@ -12,20 +12,25 @@ Created: 4-June-2009
Abstract
========
This PEP proposes the inclusion of a new version comparison system in
Distutils.
This PEP proposes a new version comparison schema system in Distutils.
Motivation
==========
Distutils will soon extend the metadata standard, by including an
``install_requires``-like field from Setuptools [#requires]_ among
other changes. This field will be called ``Requires-Dist``.
In Python there are no real restriction yet on how a project should manage its
versions, and how they should be incremented.
These changes are located in PEP 345 [#pep345]_.
Distutile provides a `version` distribution meta-data field but it is freeform and
current users, such as PyPI usually consider the latest version pushed as the
`latest` one, regardless of the expected semantics.
The ``Requires-Dist`` field will allow a package to define a dependency on
Distutils will soon extend its capabilities to allow distributions to express a
dependency on other distributions through the `Requires-Dist` metadata field
(see PEP 345 [#pep345]_) and it will optionally allow to use that field to
restrict the dependency to a set of compatible versions.
The ``Requires-Dist`` field will allow a distribution to define a dependency on
another package and optionally restrict this dependency to a set of
compatible versions, so one may write::
@ -38,33 +43,45 @@ This also means that Python projects will need to follow the same convention
than the tool that will be used to install them, so they are able to compare
versions.
That's why Distutils needs to provide a robust standard and reference
version scheme, and an API to provide version comparisons.
That is why this PEP proposes, for the sake of interoperability, a standard
schema to express version information and its comparison semantics.
This PEP describes a new version scheme that will be added in Distutils.
Furthermore, this will make OS packagers work easier when repackaging standards
compliant distributions, as of now it can be difficult to decide how two
distribution versions compare.
Of course developers are **not** required to conform to this scheme, but
it is suggested to use it as a standard for interoperability between the
existing Python distributions installers.
Current status
==============
Requisites and current status
=============================
In Python there are no real restriction yet on how a project should manage
its versions, and how they should be incremented. They are no standard
either, even if they are a few conventions widely used, like having a major
and a minor revision (1.1, 1.2, etc.).
It is not in the scope of this PEP to come with an universal versioning schema,
intented to support many or all existing versioning schemas. There will always
be competing grammars, either mandated by distro or project policies or by
historical reasons and we cannot expect that to change.
Developers are free to put in the `version` meta-data of their package any
string they want, and push a new release at PyPI. This version will appear
as the `latest` for end users.
The proposed schema should be able to express the usual versioning semantics,
so it's possible to parse any alternative versioning schema and transform it
into a compliant one. This is how OS packagers usually deal with the existing
version schemas and is a preferable alternative than supporting an arbitrary
set of versioning schemas.
Some project are also using dates as their major version numbers, or a custom
versioning standard that is sometimes quite exotic.
Conformance to usual practice and conventions, as well as a simplicity are a
plus, to ease frictionless adoption and painless transition. Practicality beats
purity, sometimes.
The problem with this freedom is that the package will be harder to re-package
for OS packagers, that need to have stricter conventions. The worst case is
when a packager is unable to easily compare the versions he needs to package.
Projects have very different versioning needs, but the following are widely
considered important semantics:
1. there should be possible to express more than one versioning level
(usually this is expressed as major and minor revision and, sometimes,
also a micro revision).
2. most projects need special meaning versions for "pre-releases" (such as
"alpha", "beta", "rc"), and these have widely used aliases ("a" stands
for "alpha", "b" for "beta" and "c" for "rc").
3. some projects also need "post-releases" of regular versions,
mainly for installer work which can't be clearly expressed otherwise.
4. development versions allow packagers of unreleased work to avoid version
clash with later regular releases.
For people that want to go further and use a tool to manage their version
numbers, the two major ones are:
@ -120,6 +137,11 @@ your project::
>>> v1 > v2
False
The problem with this is that while it allows expressing requisite any
nesting level it doesn't allow to express special meaning versions
(pre and post-releases as well as development versions), as expressed in
requisites 2, 3 and 4.
The `StrictVersion` class is more strict. From the doc::
Version numbering for meticulous retentive and software idealists.
@ -166,13 +188,12 @@ numbers::
>>> v2 < v3
True
Although, it lacks a few elements to make it usable:
It adds pre-release versions, and some structure, but lacks a few semantic
elements to make it usable, such as development releases or post-release tags,
as expressed in requisites 3 and 4.
- development releases
- post-release tags
- development releases of post-release tags.
Also, notice that Distutils version classes are not really used in the community.
Notice that Distutils version classes are not really used in the community.
Setuptools
----------
@ -226,13 +247,18 @@ deal with them so they can be compared::
>>> V('FunkyVersion')
('*funkyversion', '*final')
In this schema practicality takes priority over purity, but as a result it
doesn't enforce any policy and leads to very complex semantics due to the lack
of a clear standard. It just tries to adapt to widely used conventions.
Caveats of existing systems
---------------------------
The major problem with the described version comparison tools is that they are
too permissive. Many of the versions on PyPI [#pypi]_ are obviously not useful
versions, which makes it difficult for users to grok the versioning that a
particular package was using and to provide tools on top of PyPI.
too permissive and, at the same time, aren't capable of expressing some of the
required semantics. Many of the versions on PyPI [#pypi]_ are obviously not
useful versions, which makes it difficult for users to grok the versioning that
a particular package was using and to provide tools on top of PyPI.
Distutils classes are not really used in Python projects, but the
Setuptools function is quite spread because it's used by tools like
@ -273,6 +299,7 @@ Some examples probably make it clearer::
... < V('1.0a2.1')
... < V('1.0b1.dev456')
... < V('1.0b2')
... < V('1.0b2.post345')
... < V('1.0c1.dev456')
... < V('1.0c1')
... < V('1.0.dev456')
@ -355,38 +382,41 @@ version during PyCon 2009, 4287 of them:
suggestion
- 3474 (81.04%) match when using this suggestion method
When a tool needs to work with versions, the best strategy is to use
When a tool needs to work with versions, a strategy is to use
``suggest_rational_version`` on the versions string. If this function returns
``None``, it means that the provided version is not close enough to the
standard scheme::
standard scheme. If it returns a version that slighlty differs from
the original version, it's a suggested rational version. Last, if it
returns the same string, it means that the version matches the scheme.
Here's an example of usage ::
>>> from verlib import suggest_rational_version, RationalVersion
>>> import warnings
>>> def validate_version(version):
... rversion = suggest_rational_version(version)
... if rversion is None:
... raise ValueError('Cannot work with "%s"' % version)
... if rversion != version:
... warnings.warn('"%s" is not a rational version, '
... 'it has been transformed into "%s" '
... 'for interoperability.' % (version, rversion))
... return RationalVersion(rversion)
...
>>> validate_version('2.4rc1')
__main__:8: UserWarning: "2.4rc1" is not a rational version, it has been transformed into "2.4c1" for interoperability.
RationalVersion('2.4c1')
>>> validate_version('2.4c1')
RationalVersion('2.4c1')
>>> validate_version('foo')
Traceback (most recent call last):
...
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in validate_version
ValueError: Cannot work with "foo"
>>> validate_version('1.24.33')
RationalVersion('1.24.33')
>>> validate_version('1.24.330pre1')
RationalVersion('1.24.330c1')
>>> validate_version('2008.12.11')
Traceback (most recent call last):
...
ValueError: Cannot work with "2008.12.11"
Roadmap
=======