Permalink
Cannot retrieve contributors at this time
305 lines (228 sloc)
10.7 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
peps/pep-0396.txt
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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()`` [11]_ | |
| 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 [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. | |
| 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 [3]_ 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 | |
| ============= | |
| #. 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 (or package) includes a version number, the version | |
| SHOULD be available in the ``__version__`` attribute. | |
| #. 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. | |
| #. The ``__version__`` attribute's value SHOULD be a string. | |
| #. Module version numbers SHOULD conform to the normalized version | |
| format specified in :pep:`386`. | |
| #. 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` ``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 [9]_, 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 [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` 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 | |
| .. [5] sqlite3 module documentation | |
| (http://docs.python.org/library/sqlite3.html) | |
| .. [8] pkgutil - Package utilities | |
| (http://distutils2.notmyidea.org/library/pkgutil.html) | |
| .. [9] https://mail.python.org/pipermail/distutils-sig/2011-June/017862.html | |
| .. [11] importlib.metadata | |
| (https://docs.python.org/3/library/importlib.metadata.html#distribution-versions) | |
| 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: |