3

I use pbr for packaging. It takes the version from git tags and applies that to setup.py

Now I also want to have the version available inside the package. For instance to have a __version__ attribute. Can I use the pbr library for this?

There is another library: versioneer that also extracts the version from the git tags, but that would add an extra requirement. I would prefer to get this functionality from pbr

Roy Prins
  • 2,790
  • 2
  • 28
  • 47
  • Isn't `pbr` already an "extra requirement"? – Bailey Parker Jan 22 '18 at 14:25
  • It is a requirement, but it is one that I will use for packaging anyways. It gets a lot of its info from git, satisfying the DRY principle (authors, ChangeLog, etc). One of the things it does is generate a version for use in setup.py. Merely looking for a way to leverage that. – Roy Prins Jan 22 '18 at 15:14

3 Answers3

6

If you are fine with pbr being a runtime dependency of your package, then you can use the VersionInfo class from pbr.version:

from pbr.version import VersionInfo

package_name='my_package'
info = VersionInfo(package_name)

print(info.version_string())

(See also How to load package version into __version__ variable if you are using pbr?)

jonathanverner
  • 303
  • 3
  • 12
2

Consider setuptools_scm, which pulls a version from a git or hg tag when available, or generates an appropriate dev release version (e.g., hgvs-1.2.5.dev6+hb5d989061852.d20181124). The version is written into package metadata as with a hardcoded version. No non-standard runtime support is needed.

Although I've used setuptools_scm for many projects, I've not used PBR. I was curious and worked up this simple demo:

snafu$ head setup.py setup.cfg pbrversiontest/*.py
==> setup.py <==
from setuptools import setup

setup(
    setup_requires=[
        "pbr",
        "setuptools_scm"
    ],
    pbr=True,
    use_scm_version=True,
)

==> setup.cfg <==
[metadata]
name = pbrversiontest
summary = test whether we can use pbr and setuptools_scm

[files]
packages =
    pbrversiontest

==> pbrversiontest/__init__.py <==
# This is straight from setuptools_scm README
from pkg_resources import get_distribution, DistributionNotFound
try:
    __version__ = get_distribution(__name__).version
except DistributionNotFound:
    # package is not installed
    pass

==> pbrversiontest/__main__.py <==
# this isn't required -- only for the demo
import pbrversiontest

print("version is " + pbrversiontest.__version__)

In the development directory, you might have a session like so:

snafu$ python3.6 -mvenv venv/3.6
snafu$ source venv/3.6/bin/activate
(3.6) snafu$ git tag 0.1.2
(3.6) snafu$ pip install -e .
(3.6) snafu$ python -m pbrversiontest 
version is 0.1.2
(3.6) snafu$ pip install wheel
(3.6) snafu$ python setup.py bdist_wheel
...
creating 'dist/pbrversiontest-0.1.2-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
...
(3.6) snafu$ unzip -l dist/pbrversiontest-0.1.2-py3-none-any.whl 
Archive:  dist/pbrversiontest-0.1.2-py3-none-any.whl
  Length      Date    Time    Name
---------  ---------- -----   ----
      192  2018-11-25 05:26   pbrversiontest/__init__.py
       73  2018-11-25 05:46   pbrversiontest/__main__.py
       33  2018-11-25 06:06   pbrversiontest-0.1.2.dist-info/AUTHORS
      217  2018-11-25 06:06   pbrversiontest-0.1.2.dist-info/METADATA
       92  2018-11-25 06:06   pbrversiontest-0.1.2.dist-info/WHEEL
       47  2018-11-25 06:06   pbrversiontest-0.1.2.dist-info/pbr.json
       15  2018-11-25 06:06   pbrversiontest-0.1.2.dist-info/top_level.txt
      675  2018-11-25 06:06   pbrversiontest-0.1.2.dist-info/RECORD
---------                     -------
     1344                     8 files
Reece
  • 7,616
  • 4
  • 30
  • 46
  • P.S. The advantage of a setup like this is that the scm becomes the sole/authoritative source for version tags. That makes it hard for code to misrepresent its version number (or prd/dev/rc status). – Reece Nov 28 '18 at 04:56
1

After properly setting up for setuptools and pbr, here are several ways to do it:

import pkg_resources  # part of setuptools

# I don't like this one because the version method is hidden
v1 = pkg_resources.require("my_package_name")[0].version
print('v1 {}'.format(v1))

# This is my favorite - the output without .version is just a longer string with
# both the package name, a space, and the version string
v2 = pkg_resources.get_distribution('my_package_name').version
print('v2 {}'.format(v2))

from pbr.version import VersionInfo

# This one seems to be slower, and with pyinstaller makes the exe a lot bigger
v3 = VersionInfo('my_package_name').release_string()
print('v3 {}'.format(v3))

# Update, new option as of Python 3.8 (credit: sinoroc)
# In Python 3.8, importlib.metadata is part of the stdlib,
# which removes run-time dependencies on `pbr` or `setuptools`
import importlib.metadata

__version__ = importlib.metadata.version('my_package_name')

If you want it from the command line, you can do:

py setup.py --version

Or even run the setup.py script from within a script, if the package will always be installed locally:

from subprocess import Popen, PIPE

(output, err) = Popen('py setup.py --version'.split(' '),
                      stdout=PIPE, stderr=PIPE, text=True).communicate()
if err: print('ERROR: "{}"'.format(err))
else: print('setup.py --version = {}'.format(output))

Note: See this answer for more details on using subprocess to launch and read stdout, etc., especially on older versions of Python (prior to 3.7).

You can then add __version__ to your package __init__.py like this:

__all__ = (
    '__version__',
    'my_package_name'
)

# Or substitute a different method and assign the result to __version__
import pkg_resources  # part of setuptools

__version__ = pkg_resources.get_distribution("my_package_name").version

Some other Q&A that might help with setup and details on how to update the version and other info, especially if getting info from your Git repo (version from tags, Authors, and ChangeLog info):

LightCC
  • 9,804
  • 5
  • 52
  • 92