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

You could also reverse the dependency: put the version in mylib/__init__.py, parse that file in setup.py to get the version parameter, and use git tag $(setup.py --version) on the command line to create your tag.

git tag -a v$(python setup.py --version) -m 'description of version'

Is there anything more complicated you want to do that I haven’t understood?


A classic issue when toying with keyword expansion ;)

The key is to realize that your tag is part of the release management process, not part of the development (and its version control) process.

In other word, you cannot include a release management data in a development repository, because of the loop you illustrates in your question.

You need, when generating the package (which is the "release management part"), to write that information in a file that your library will look for and use (if said file exists) for its User-Agent HTTP header.


Since this topic is still alive and sometimes gets to search results, I would like to mention another solution which first appeared in 2012 and now is more or less usable:

https://github.com/warner/python-versioneer

It works in different way than all mentioned solutions: you add git tags manually, and the library (and setup.py) reads the tags, and builds the version string dynamically.

The version string includes the latest tag, distance from that tag, current commit hash, "dirtiness", and some other info. It has few different version formats.

But it still has no branch name for so called "custom builds"; and commit distance can be confusing sometimes when two branches are based on the same commit, so it is better to tag & release only one selected branch (master).


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,
 .....
)