22

If I do sphinx-quickstart I get asked about the version of the project.

I want to avoid to have two places for the version of my project.

How to do this in the python packing world?

guettli
  • 23,964
  • 63
  • 293
  • 556
  • 1
    So, did it any of the answers work for you? If so, please accept the respective answer. If not, what's the issue? – Lukas Graf Oct 07 '14 at 16:55
  • Thank you for asking. I am not in a hurry with this topic. I want a canonical answer. I will ask on dist-sig: https://www.python.org/community/sigs/current/distutils-sig – guettli Oct 08 '14 at 07:24
  • 1
    @guetti's link to the "single sourcing the package version" section of the Python packaging docs seems to be broken - current link is https://packaging.python.org/en/latest/guides/single-sourcing-package-version/ – phlummox Mar 27 '22 at 11:23

4 Answers4

27

The easiest (and probably cleanest) way is to define __version__ for the __init__.py of your top-level package, and then import that package and read the version in both setup.py and your Sphinx project's conf.py.

So lets say your project is called myproject.

Move your current version out of setup.py, and make it a variable in myproject/__init__.py instead:

myproject/__init__.py:

# import foo
# ...

__version__ = '1.5'

Import myproject in your project's setup.py, and replace the hardcoded version with myproject.__version__:

setup.py:

from setuptools import setup
from myproject import __version__


project = "myproject"

setup(
    name=project,
    version=__version__,
    # ...
)

In your Sphinx project's conf.py, do the same. So edit the generated conf.py along these lines:

docs/conf.py:

from myproject import __version__

# ...

# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = version

For an example of a library that does this pretty much exactly like this, have a look at the requests module (__init__.py | setup.py | conf.py).

This will take care of the auto-generated texts where the project version is used (like the links to the front page of the documentation). If you want to use your version in specific custom places, you can use the rst_epilog directive to dynamically insert configuration values defined in conf.py.

Community
  • 1
  • 1
Lukas Graf
  • 26,817
  • 8
  • 73
  • 87
  • 11
    In another SO question it has been pointed out that if you import some dependencies in myproject/__init__.py, and then do `from myproject import __version__` in setup.py, a user won't be able to install the package without installing the dependencies first: http://stackoverflow.com/questions/2058802/how-can-i-get-the-version-defined-in-setup-py-setuptools-in-my-package – Seppo Enarvi Apr 26 '16 at 10:16
  • We also had a problem with third-party imports and solved it by introducing a separate package containing only meta information. Have a look at this answer: https://stackoverflow.com/a/52381178/1600678 – marko.ristin Sep 18 '18 at 07:33
8

Maybe an even cleaner option is to actually build sphinx from the setup.py command as described in http://www.sphinx-doc.org/en/master/setuptools.html:

setup.py

# this is only necessary when not using setuptools/distribute
from sphinx.setup_command import BuildDoc
cmdclass = {'build_sphinx': BuildDoc}

name = 'My project'
version = '1.2'
release = '1.2.0'
setup(
    name=name,
    author='Bernard Montgomery',
    version=release,
    cmdclass=cmdclass,
    # these are optional and override conf.py settings
    command_options={
        'build_sphinx': {
            'project': ('setup.py', name),
            'version': ('setup.py', version),
            'release': ('setup.py', release),
            'source_dir': ('setup.py', 'doc')}},
)

Then build documentation using

$ python setup.py build_sphinx

Benefits:

  • Makes setup.py the single source of version number
  • Avoids having to make packages out of your project folders unnecessarily
SuperGeo
  • 727
  • 5
  • 22
  • 2
    You have to install Sphinx manually for the setup.py to work. So you can't use `pip3 -e .` anymore. – Pierre.Sassoulas Feb 05 '20 at 16:05
  • @Pierre.Sassoulas that's a very good point, thanks for pointing that out. However, this issue may disappear if using `setup.cfg` or a `pyproject.toml`, right? The Sphinx doc has [a section](https://www.sphinx-doc.org/en/master/usage/advanced/setuptools.html#using-setuptools-integration) about it. – Edouard Berthe Feb 03 '22 at 21:39
4

You could have a look at bumpversion module:

"A small command line tool to simplify releasing software by updating all version strings in your source code by the correct increment"

You may use a configuration file .bumpversion.cfg for complex multi-file operations.

aflp91
  • 611
  • 5
  • 11
1

Another way is integrating setuptools_scm in your project. This way you can

from setuptools_scm import get_version

version = get_version()

in your conf.py

Ali Cirik
  • 1,217
  • 12
  • 20