296 lines
10 KiB
Plaintext
296 lines
10 KiB
Plaintext
PEP: 396
|
||
Title: Module Version Numbers
|
||
Version: $Revision: 65628 $
|
||
Last-Modified: $Date: 2008-08-10 09:59:20 -0400 (Sun, 10 Aug 2008) $
|
||
Author: Barry Warsaw <barry@python.org>
|
||
Status: Draft
|
||
Type: Informational
|
||
Content-Type: text/x-rst
|
||
Created: 2011-03-16
|
||
Post-History: 2011-04-05
|
||
|
||
|
||
Abstract
|
||
========
|
||
|
||
Given that it is useful and common to specify version numbers for
|
||
Python modules, and given that different ways of doing this have grown
|
||
organically within the Python community, it is useful to establish
|
||
standard conventions for module authors to adhere to and reference.
|
||
This informational PEP describes best practices for Python module
|
||
authors who want to define the version number of their Python module.
|
||
|
||
Conformance with this PEP is optional, however other Python tools
|
||
(such as ``distutils2`` [1]_) may be adapted to use the conventions
|
||
defined here.
|
||
|
||
|
||
User Stories
|
||
============
|
||
|
||
Alice is writing a new module, called ``alice``, which she wants to
|
||
share with other Python developers. ``alice`` is a simple module and
|
||
lives in one file, ``alice.py``. Alice wants to specify a version
|
||
number so that her users can tell which version they are using.
|
||
Because her module lives entirely in one file, she wants to add the
|
||
version number to that file.
|
||
|
||
Bob has written a module called ``bob`` which he has shared with many
|
||
users. ``bob.py`` contains a version number for the convenience of
|
||
his users. Bob learns about the Cheeseshop [2]_, and adds some simple
|
||
packaging using classic distutils so that he can upload *The Bob
|
||
Bundle* to the Cheeseshop. Because ``bob.py`` already specifies a
|
||
version number which his users can access programmatically, he wants
|
||
the same API to continue to work even though his users now get it from
|
||
the Cheeseshop.
|
||
|
||
Carole maintains several namespace packages, each of which are
|
||
independently developed and distributed. In order for her users to
|
||
properly specify dependencies on the right versions of her packages,
|
||
she specifies the version numbers in the namespace package's
|
||
``setup.py`` file. Because Carol wants to have to update one version
|
||
number per package, she specifies the version number in her module and
|
||
has the ``setup.py`` extract the module version number when she builds
|
||
the *sdist* archive.
|
||
|
||
David maintains a package in the standard library, and also produces
|
||
standalone versions for other versions of Python. The standard
|
||
library copy defines the version number in the module, and this same
|
||
version number is used for the standalone distributions as well.
|
||
|
||
|
||
Rationale
|
||
=========
|
||
|
||
Python modules, both in the standard library and available from third
|
||
parties, have long included version numbers. There are established
|
||
de-facto standards for describing version numbers, and many ad-hoc
|
||
ways have grown organically over the years. Often, version numbers
|
||
can be retrieved from a module programmatically, by importing the
|
||
module and inspecting an attribute. Classic Python distutils
|
||
``setup()`` functions [3]_ describe a ``version`` argument where the
|
||
release's version number can be specified. PEP 8 [4]_ describes the
|
||
use of a module attribute called ``__version__`` for recording
|
||
"Subversion, CVS, or RCS" version strings using keyword expansion. In
|
||
the PEP author's own email archives, the earliest example of the use
|
||
of an ``__version__`` module attribute by independent module
|
||
developers dates back to 1995.
|
||
|
||
Another example of version information is the sqlite3 [5]_ module
|
||
with its ``sqlite_version_info``, ``version``, and ``version_info``
|
||
attributes. It may not be immediately obvious which attribute
|
||
contains a version number for the module, and which contains a version
|
||
number for the underlying SQLite3 library.
|
||
|
||
This informational PEP codifies established practice, and recommends
|
||
standard ways of describing module version numbers, along with some
|
||
use cases for when -- and when *not* -- to include them. Its adoption
|
||
by module authors is purely voluntary; packaging tools in the standard
|
||
library will provide optional support for the standards defined
|
||
herein, and other tools in the Python universe may comply as well.
|
||
|
||
|
||
Specification
|
||
=============
|
||
|
||
#. In general, modules in the standard library SHOULD NOT have version
|
||
numbers. They implicitly carry the version number of the Python
|
||
release they are included in.
|
||
|
||
#. On a case-by-case basis, standard library modules which are also
|
||
released in standalone form for other Python versions MAY include a
|
||
module version number when included in the standard library, and
|
||
SHOULD include a version number when packaged separately.
|
||
|
||
#. When a module includes a version number, it SHOULD be available in
|
||
the ``__version__`` attribute on that module.
|
||
|
||
#. For modules which are also packages, the module namespace SHOULD
|
||
include the ``__version__`` attribute.
|
||
|
||
#. For modules which live inside a namespace package, the sub-package
|
||
name SHOULD include the ``__version__`` attribute. The namespace
|
||
module itself SHOULD NOT include its own ``__version__`` attribute.
|
||
|
||
#. The ``__version__`` attribute's value SHOULD be a string.
|
||
|
||
#. Module version numbers SHOULD conform to the normalized version
|
||
format specified in PEP 386 [6]_.
|
||
|
||
#. Module version numbers SHOULD NOT contain version control system
|
||
supplied revision numbers, or any other semantically different
|
||
version numbers (e.g. underlying library version number).
|
||
|
||
#. The ``version`` attribute in a classic distutils ``setup.py``
|
||
file, or the PEP 345 [7]_ ``Version`` metadata field SHOULD be
|
||
derived from the ``__version__`` field, or vice versa.
|
||
|
||
|
||
Examples
|
||
========
|
||
|
||
Retrieving the version number from a third party package::
|
||
|
||
>>> import bzrlib
|
||
>>> bzrlib.__version__
|
||
'2.3.0'
|
||
|
||
Retrieving the version number from a standard library package that is
|
||
also distributed as a standalone module::
|
||
|
||
>>> import email
|
||
>>> email.__version__
|
||
'5.1.0'
|
||
|
||
Version numbers for namespace packages::
|
||
|
||
>>> import flufl.i18n
|
||
>>> import flufl.enum
|
||
>>> import flufl.lock
|
||
|
||
>>> print flufl.i18n.__version__
|
||
1.0.4
|
||
>>> print flufl.enum.__version__
|
||
3.1
|
||
>>> print flufl.lock.__version__
|
||
2.1
|
||
|
||
>>> import flufl
|
||
>>> flufl.__version__
|
||
Traceback (most recent call last):
|
||
File "<stdin>", line 1, in <module>
|
||
AttributeError: 'module' object has no attribute '__version__'
|
||
>>>
|
||
|
||
|
||
Deriving
|
||
========
|
||
|
||
Module version numbers can appear in at least two places, and
|
||
sometimes more. For example, in accordance with this PEP, they are
|
||
available programmatically on the module's ``__version__`` attribute.
|
||
In a classic distutils ``setup.py`` file, the ``setup()`` function
|
||
takes a ``version`` argument, while the distutils2 ``setup.cfg`` file
|
||
has a ``version`` key. The version number must also get into the PEP
|
||
345 metadata, preferably when the *sdist* archive is built. It's
|
||
desirable for module authors to only have to specify the version
|
||
number once, and have all the other uses derive from this single
|
||
definition.
|
||
|
||
This could be done in any number of ways, a few of which are outlined
|
||
below. These are included for illustrative purposes only and are not
|
||
intended to be definitive, complete, or all-encompassing. Other
|
||
approaches are possible, and some included below may have limitations
|
||
that prevent their use in some situations.
|
||
|
||
Let's say Elle adds this attribute to her module file ``elle.py``::
|
||
|
||
__version__ = '3.1.1'
|
||
|
||
|
||
Classic distutils
|
||
-----------------
|
||
|
||
In classic distutils, the simplest way to add the version string to
|
||
the ``setup()`` function in ``setup.py`` is to do something like
|
||
this::
|
||
|
||
from elle import __version__
|
||
setup(name='elle', version=__version__)
|
||
|
||
In the PEP author's experience however, this can fail in some cases,
|
||
such as when the module uses automatic Python 3 conversion via the
|
||
``2to3`` program (because ``setup.py`` is executed by Python 3 before
|
||
the ``elle`` module has been converted).
|
||
|
||
In that case, it's not much more difficult to write a little code to
|
||
parse the ``__version__`` from the file rather than importing it.
|
||
Without providing too much detail, it's likely that modules such as
|
||
``distutils2`` will provide a way to parse version strings from files.
|
||
E.g.::
|
||
|
||
from distutils2 import get_version
|
||
setup(name='elle', version=get_version('elle.py'))
|
||
|
||
|
||
Distutils2
|
||
----------
|
||
|
||
Because the distutils2 style ``setup.cfg`` is declarative, we can't
|
||
run any code to extract the ``__version__`` attribute, either via
|
||
import or via parsing. This PEP suggests a special key be added to
|
||
the ``[metadata]`` section of the ``setup.cfg`` file to indicate "get
|
||
the version from this file". Something like this might work::
|
||
|
||
[metadata]
|
||
version-from-file: elle.py
|
||
|
||
where ``parse`` means to use a parsing method similar to the above, on
|
||
the file named after the colon. The exact recipe for doing this will
|
||
be discussed in the appropriate distutils2 development forum.
|
||
|
||
An alternative is to only define the version number in ``setup.cfg``
|
||
and use the ``pkgutil`` module [8]_ to make it available
|
||
programmatically. E.g. in ``elle.py``::
|
||
|
||
from distutils2._backport import pkgutil
|
||
__version__ = pkgutil.get_distribution('elle').metadata['version']
|
||
|
||
|
||
PEP 376 metadata
|
||
================
|
||
|
||
PEP 376 [9]_ defines a standard for static metadata, but doesn't
|
||
describe the process by which this metadata gets created. It is
|
||
highly desirable for the derived version information to be placed into
|
||
the PEP 376 ``.dist-info`` metadata at build-time rather than
|
||
install-time. This way, the metadata will be available for
|
||
introspection even when the code is not installed.
|
||
|
||
|
||
References
|
||
==========
|
||
|
||
.. [1] Distutils2 documentation
|
||
(http://distutils2.notmyidea.org/)
|
||
|
||
.. [2] The Cheeseshop (Python Package Index)
|
||
(http://pypi.python.org)
|
||
|
||
.. [3] http://docs.python.org/distutils/setupscript.html
|
||
|
||
.. [4] PEP 8, Style Guide for Python Code
|
||
(http://www.python.org/dev/peps/pep-0008)
|
||
|
||
.. [5] sqlite3 module documentation
|
||
(http://docs.python.org/library/sqlite3.html)
|
||
|
||
.. [6] PEP 386, Changing the version comparison module in Distutils
|
||
(http://www.python.org/dev/peps/pep-0386/)
|
||
|
||
.. [7] PEP 345, Metadata for Python Software Packages 1.2
|
||
(http://www.python.org/dev/peps/pep-0345/#version)
|
||
|
||
.. [8] pkgutil - Package utilities
|
||
(http://distutils2.notmyidea.org/library/pkgutil.html)
|
||
|
||
.. [9] PEP 376, Database of Installed Python Distributions
|
||
(http://www.python.org/dev/peps/pep-0376/)
|
||
|
||
|
||
Copyright
|
||
=========
|
||
|
||
This document has been placed in the public domain.
|
||
|
||
|
||
|
||
..
|
||
Local Variables:
|
||
mode: indented-text
|
||
indent-tabs-mode: nil
|
||
sentence-end-double-space: t
|
||
fill-column: 70
|
||
coding: utf-8
|
||
End:
|