Is there a standard way to associate version string with a python package in such way that I could do the following?
import foo
print foo.version
I would imagine there's some way to retrieve that data without any extra hardcoding, since minor/major strings are specified in setup.py
already. Alternative solution that I found was to have import __version__
in my foo/__init__.py
and then have __version__.py
generated by setup.py
.
I use a single
_version.py
file as the "once cannonical place" to store version information:It provides a
__version__
attribute.It provides the standard metadata version. Therefore it will be detected by
pkg_resources
or other tools that parse the package metadata (EGG-INFO and/or PKG-INFO, PEP 0345).It doesn't import your package (or anything else) when building your package, which can cause problems in some situations. (See the comments below about what problems this can cause.)
There is only one place that the version number is written down, so there is only one place to change it when the version number changes, and there is less chance of inconsistent versions.
Here is how it works: the "one canonical place" to store the version number is a .py file, named "_version.py" which is in your Python package, for example in
myniftyapp/_version.py
. This file is a Python module, but your setup.py doesn't import it! (That would defeat feature 3.) Instead your setup.py knows that the contents of this file is very simple, something like:And so your setup.py opens the file and parses it, with code like:
Then your setup.py passes that string as the value of the "version" argument to
setup()
, thus satisfying feature 2.To satisfy feature 1, you can have your package (at run-time, not at setup time!) import the _version file from
myniftyapp/__init__.py
like this:Here is an example of this technique that I've been using for years.
The code in that example is a bit more complicated, but the simplified example that I wrote into this comment should be a complete implementation.
Here is example code of importing the version.
If you see anything wrong with this approach, please let me know.
I also saw another style:
arrow handles it in an interesting way.
Now (since 2e5031b)
In
arrow/__init__.py
:In
setup.py
:Before
In
arrow/__init__.py
:In
setup.py
:version.py
file only with__version__ = <VERSION>
param in the file. In thesetup.py
file import the__version__
param and put it's value in thesetup.py
file like this:version=__version__
setup.py
file withversion=<CURRENT_VERSION>
- the CURRENT_VERSION is hardcoded.Since we don't want to manually change the version in the file every time we create a new tag (ready to release a new package version), we can use the following..
I highly recommend bumpversion package. I've been using it for years to bump a version.
start by adding
version=<VERSION>
to yoursetup.py
file if you don't have it already.You should use a short script like this every time you bump a version:
Then add one file per repo called:
.bumpversion.cfg
:Note:
__version__
parameter underversion.py
file like it was suggested in other posts and update the bumpversion file like this:[bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
git commit
orgit reset
everything in your repo, otherwise you'll get a dirty repo error.Using
setuptools
andpbr
There is not a standard way, but the standard way to manage your packages is
setuptools
.The best solution I've found overall for managing version is to use
setuptools
with thepbr
extension. This is now my standard way of managing version.PBR moves most metadata out of the
setup.py
tools and into asetup.cfg
file that is then used as a source for most metadata, which can include version. This allows the metadata to be packaged into an executable using something likepyinstaller
if needed (if so, you will probably need this info), and separates the metadata from the other package management/setup scripts.When using Git for VCS/SCM, this setup is even better, as it will pull in a lot of the metadata from Git so that your repo can be your primary source of truth for some of the metadata, including version, authors, changelogs, etc.
setup.py
and asetup.cfg
file with the metadata.As PBR will pull version, author, changelog and other info directly from your git repo, so some of the metadata in
setup.cfg
can be left out and auto generated whenever a distribution is created for your package (usingsetup.py
)Real-time current version
setuptools
will pull the latest info in real-time usingsetup.py
:This will pull the latest version either from the
setup.cfg
file, or from the git repo, based on the latest commit that was made and tags that exist in the repo. This command doesn't update the version in a distribution though.Updating the version
When you create a distribution with
setup.py
(i.e.py setup.py sdist
, for example), then all the current info will be extracted and stored in the distribution. This essentially runs thesetup.py --version
command and then stores that version info into thepackage.egg-info
folder in a set of files that store distribution metadata.Accessing the version from a script
You can access the metadata from the current build within Python scripts in the package itself. For version, for example, there are several ways to do this I have found so far:
You can put one of these directly in your
__init__.py
for the package to extract the version info as follows, similar to some other answers:Though this is probably far too late, there is a slightly simpler alternative to the previous answer:
(And it would be fairly simple to convert auto-incrementing portions of version numbers to a string using
str()
.)Of course, from what I've seen, people tend to use something like the previously-mentioned version when using
__version_info__
, and as such store it as a tuple of ints; however, I don't quite see the point in doing so, as I doubt there are situations where you would perform mathematical operations such as addition and subtraction on portions of version numbers for any purpose besides curiosity or auto-incrementation (and even then,int()
andstr()
can be used fairly easily). (On the other hand, there is the possibility of someone else's code expecting a numerical tuple rather than a string tuple and thus failing.)This is, of course, my own view, and I would gladly like others' input on using a numerical tuple.
As shezi reminded me, (lexical) comparisons of number strings do not necessarily have the same result as direct numerical comparisons; leading zeroes would be required to provide for that. So in the end, storing
__version_info__
(or whatever it would be called) as a tuple of integer values would allow for more efficient version comparisons.