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: Rejected Type:
Informational Topic: Packaging Content-Type: text/x-rst Created:
16-Mar-2011 Post-History: 05-Apr-2011

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.

PEP Rejection

This PEP was formally rejected on 2021-04-14. The packaging ecosystem
has changed significantly in the intervening years since this PEP was
first written, and APIs such as importlib.metadata.version()[2] provide
for a much better experience.

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[3], 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.

Carol 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[4]
describe a version argument where the release's version number can be
specified. PEP 8 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

1.  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.
2.  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.
3.  When a module (or package) includes a version number, the version
    SHOULD be available in the __version__ attribute.
4.  For modules which live inside a namespace package, the module SHOULD
    include the __version__ attribute. The namespace package itself
    SHOULD NOT include its own __version__ attribute.
5.  The __version__ attribute's value SHOULD be a string.
6.  Module version numbers SHOULD conform to the normalized version
    format specified in PEP 386.
7.  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).
8.  The version attribute in a classic distutils setup.py file, or the
    PEP 345 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.

In consultation with the distutils-sig[6], two options are proposed.
Both entail containing the version number in a file, and declaring that
file in the setup.cfg. When the entire contents of the file contains the
version number, the version-file key will be used:

    [metadata]
    version-file: version.txt

When the version number is contained within a larger file, e.g. of
Python code, such that the file must be parsed to extract the version,
the key version-from-file will be used:

    [metadata]
    version-from-file: elle.py

A parsing method similar to that described above will be performed 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[7] 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 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

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:

[1] Distutils2 documentation (http://distutils2.notmyidea.org/)

[2] importlib.metadata
(https://docs.python.org/3/library/importlib.metadata.html#distribution-versions)

[3] The Cheeseshop (Python Package Index) (http://pypi.python.org)

[4] http://docs.python.org/distutils/setupscript.html

[5] sqlite3 module documentation
(http://docs.python.org/library/sqlite3.html)

[6] https://mail.python.org/pipermail/distutils-sig/2011-June/017862.html

[7] pkgutil - Package utilities
(http://distutils2.notmyidea.org/library/pkgutil.html)