Automatic version number both in setup.py (setuptools) AND source code?

Viewed 36127

SITUATION:

I have a python library, which is controlled by git, and bundled with distutils/setuptools. And I want to automatically generate version number based on git tags, both for setup.py sdist and alike commands, and for the library itself.

For the first task I can use git describe or alike solutions (see How can I get the version defined in setup.py (setuptools) in my package?).

And when, for example, I am in a tag '0.1' and call for 'setup.py sdist', I get 'mylib-0.1.tar.gz'; or 'mylib-0.1-3-abcd.tar.gz' if I altered the code after tagging. This is fine.

THE PROBLEM IS:

The problem comes when I want to have this version number available for the library itself, so it could send it in User-Agent HTTP header as 'mylib/0.1-3-adcd'.

If I add setup.py version command as in How can I get the version defined in setup.py (setuptools) in my package?, then this version.py is generated AFTER the tag is made, since it uses the tag as a value. But in this case I need to make one more commit after the version tag is made to make the code consistent. Which, in turns, requires a new tag for further bundling.

THE QUESTION IS:

How to break this circle of dependencies (generate-commit-tag-generate-commit-tag-...)?

7 Answers

Eric's idea was the simple way to go, just in case this is useful here is the code I used (Flask's team did it this way):

import re
import ast

_version_re = re.compile(r'__version__\s+=\s+(.*)')

with open('app_name/__init__.py', 'rb') as f:
    version = str(ast.literal_eval(_version_re.search(
        f.read().decode('utf-8')).group(1)))

setup(
    name='app-name',
    version=version,
 .....
)

If you found versioneer excessively convoluted, you can try bump2version.

Just add the simple bumpversion configuration file in the root of your library. This file indicates where in your repository there are strings storing the version number. Then, to update the version in all indicated places for a minor release, just type:

bumpversion minor

Use patch or major if you want to release a patch or a major.

This is not all about bumpversion. There are other flag-options, and config options, such as tagging automatically the repository, for which you can check the official documentation.

As was mentioned in another answer, this is related to the release process and not to the development process, as such it is not a git issue in itself, but more how is your release work process.

A very simple variant is to use this:

python setup.py egg_info -b ".`date '+%Y%m%d'`git`git rev-parse --short HEAD`" build sdist

The portion between the quotes is up for customization, however I tried to follow the typical Fedora/RedHat package names.

Of note, even if egg_info implies relation to .egg, actually it's used through the toolchain, for example for bdist_wheel as well and has to be specified in the beginning.

In general, your pre-release and post-release versions should live outside setup.py or any type of import version.py. The topic about versioning and egg_info is covered in detail here.

Example:

  • v1.3.4dev.20200813gitabcdef0
  • The v1.3.4 is in setup.py or any other variation you would like
  • The dev and 20200813gitabcdef0 is generated during the build process (example above)
  • None of the files generated during build are checked in git (usually in .gitignore they are filtered by default); sometimes there is a separate "deployment" repository, or similar, completely separate from the source one

A more complex way would be to have your release work process encoded in a Makefile which is outside the scope of this question, however a good source of inspiration can be found here and here. You will find good correspondeces between Makefile targets and setup.py commands.

Related