Important
This PEP has been withdrawn.
×
Refer toAccessing version information at runtime andSingle-sourcing the Project Version in the PythonPackaging User Guide for up to date recommendations onaccessing package version information at runtime,and on defining runtime__version__ attributes which areautomatically kept consistent with package distribution metadata
Given that it is useful and common to specify version numbers forPython modules, and given that different ways of doing this have grownorganically within the Python community, it is useful to establishstandard conventions for module authors to adhere to and reference.This informational PEP describes best practices for Python moduleauthors who want to define the version number of their Python module.
Conformance with this PEP is optional, however other Python tools(such asdistutils2[1]) may be adapted to use the conventionsdefined here.
This PEP was formally rejected on 2021-04-14. The packaging ecosystemhas changed significantly in the intervening years since this PEP wasfirst written, and APIs such asimportlib.metadata.version()[11]provide for a much better experience.
This rejection was reclassified as a withdrawal on 2024-10-21,as the previous state was being misinterpreted[12] as suggestingthatno modules should be defining__version__ attributes,which definitely isn’t the case.
Modules are still free to define__version__ if they choose to.However, choosingnot to do so won’t interfere with looking upthe version information for installed distribution packages, so anInformational PEP isn’t the right tool to document communityconventions around the use of module__version__ attributes(they’re better covered as part of the Python Packaging User Guide).
Alice is writing a new module, calledalice, which she wants toshare with other Python developers.alice is a simple module andlives in one file,alice.py. Alice wants to specify a versionnumber so that her users can tell which version they are using.Because her module lives entirely in one file, she wants to add theversion number to that file.
Bob has written a module calledbob which he has shared with manyusers.bob.py contains a version number for the convenience ofhis users. Bob learns about the Cheeseshop[2], and adds some simplepackaging using classic distutils so that he can uploadThe BobBundle to the Cheeseshop. Becausebob.py already specifies aversion number which his users can access programmatically, he wantsthe same API to continue to work even though his users now get it fromthe Cheeseshop.
Carol maintains several namespace packages, each of which areindependently developed and distributed. In order for her users toproperly specify dependencies on the right versions of her packages,she specifies the version numbers in the namespace package’ssetup.py file. Because Carol wants to have to update one versionnumber per package, she specifies the version number in her module andhas thesetup.py extract the module version number when she buildsthesdist archive.
David maintains a package in the standard library, and also producesstandalone versions for other versions of Python. The standardlibrary copy defines the version number in the module, and this sameversion number is used for the standalone distributions as well.
Python modules, both in the standard library and available from thirdparties, have long included version numbers. There are establishedde facto standards for describing version numbers, and many ad-hocways have grown organically over the years. Often, version numberscan be retrieved from a module programmatically, by importing themodule and inspecting an attribute. Classic Python distutilssetup() functions[3] describe aversion argument where therelease’s version number can be specified.PEP 8 describes theuse of a module attribute called__version__ for recording“Subversion, CVS, or RCS” version strings using keyword expansion. Inthe PEP author’s own email archives, the earliest example of the useof an__version__ module attribute by independent moduledevelopers dates back to 1995.
Another example of version information is the sqlite3[5] modulewith itssqlite_version_info,version, andversion_infoattributes. It may not be immediately obvious which attributecontains a version number for the module, and which contains a versionnumber for the underlying SQLite3 library.
This informational PEP codifies established practice, and recommendsstandard ways of describing module version numbers, along with someuse cases for when – and whennot – to include them. Its adoptionby module authors is purely voluntary; packaging tools in the standardlibrary will provide optional support for the standards definedherein, and other tools in the Python universe may comply as well.
__version__ attribute.__version__ attribute. The namespacepackage itself SHOULD NOT include its own__version__attribute.__version__ attribute’s value SHOULD be a string.version attribute in a classic distutilssetup.pyfile, or thePEP 345Version metadata field SHOULD bederived from the__version__ field, or vice versa.Retrieving the version number from a third party package:
>>>importbzrlib>>>bzrlib.__version__'2.3.0'
Retrieving the version number from a standard library package that isalso distributed as a standalone module:
>>>importemail>>>email.__version__'5.1.0'
Version numbers for namespace packages:
>>>importflufl.i18n>>>importflufl.enum>>>importflufl.lock>>>printflufl.i18n.__version__1.0.4>>>printflufl.enum.__version__3.1>>>printflufl.lock.__version__2.1>>>importflufl>>>flufl.__version__Traceback (most recent call last): File"<stdin>", line1, in<module>AttributeError:'module' object has no attribute '__version__'>>>
Module version numbers can appear in at least two places, andsometimes more. For example, in accordance with this PEP, they areavailable programmatically on the module’s__version__ attribute.In a classic distutilssetup.py file, thesetup() functiontakes aversion argument, while the distutils2setup.cfg filehas aversion key. The version number must also get into the PEP345 metadata, preferably when thesdist archive is built. It’sdesirable for module authors to only have to specify the versionnumber once, and have all the other uses derive from this singledefinition.
This could be done in any number of ways, a few of which are outlinedbelow. These are included for illustrative purposes only and are notintended to be definitive, complete, or all-encompassing. Otherapproaches are possible, and some included below may have limitationsthat prevent their use in some situations.
Let’s say Elle adds this attribute to her module fileelle.py:
__version__='3.1.1'
In classic distutils, the simplest way to add the version string tothesetup() function insetup.py is to do something likethis:
fromelleimport__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 the2to3 program (becausesetup.py is executed by Python 3 beforetheelle module has been converted).
In that case, it’s not much more difficult to write a little code toparse the__version__ from the file rather than importing it.Without providing too much detail, it’s likely that modules such asdistutils2 will provide a way to parse version strings from files.E.g.:
fromdistutils2importget_versionsetup(name='elle',version=get_version('elle.py'))
Because the distutils2 stylesetup.cfg is declarative, we can’trun any code to extract the__version__ attribute, either viaimport or via parsing.
In consultation with the distutils-sig[9], two options areproposed. Both entail containing the version number in a file, anddeclaring that file in thesetup.cfg. When the entire contents ofthe file contains the version number, theversion-file key will beused:
[metadata]version-file:version.txt
When the version number is contained within a larger file, e.g. ofPython code, such that the file must be parsed to extract the version,the keyversion-from-file will be used:
[metadata]version-from-file:elle.py
A parsing method similar to that described above will be performed onthe file named after the colon. The exact recipe for doing this willbe discussed in the appropriate distutils2 development forum.
An alternative is to only define the version number insetup.cfgand use thepkgutil module[8] to make it availableprogrammatically. E.g. inelle.py:
fromdistutils2._backportimportpkgutil__version__=pkgutil.get_distribution('elle').metadata['version']
PEP 376 defines a standard for static metadata, but doesn’tdescribe the process by which this metadata gets created. It ishighly desirable for the derived version information to be placed intothePEP 376.dist-info metadata at build-time rather thaninstall-time. This way, the metadata will be available forintrospection even when the code is not installed.
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0396.rst
Last modified:2025-02-01 08:50:23 GMT