Reputation: 5363
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
.
Upvotes: 415
Views: 324872
Reputation: 13831
Lots of work toward uniform versioning and in support of conventions has been completed since this question was first asked. Palatable options are now detailed in the Python Packaging User Guide. Also noteworthy is that version number schemes are relatively strict in Python per PEP 440, and so keeping things sane is critical if your package will be released to the Python Package Index (PyPI).
Here's a shortened breakdown of versioning options:
setup.py
(setuptools) and get the version.__init__.py
as well as source control), e.g. bump2version, changes or zest.releaser.__version__
global variable in a specific module.setup.py
release, and use importlib.metadata to pick it up at runtime. (Warning, there are pre-3.8 and post-3.8 versions.)__version__
in sample/__init__.py
and import sample in setup.py
.NOTE that (7) might be the most modern approach (build metadata is independent of code, published by automation). Also NOTE that if setup is used for package release that a simple python3 setup.py --version
will report the version directly.
Upvotes: 14
Reputation: 11932
This solution was derived from this article.
The use case - python GUI package distributed via PyInstaller. Needs to show version info.
Here is the structure of the project packagex
packagex
├── packagex
│ ├── __init__.py
│ ├── main.py
│ └── _version.py
├── packagex.spec
├── LICENSE
├── README.md
├── .bumpversion.cfg
├── requirements.txt
├── setup.cfg
└── setup.py
where setup.py
is
# setup.py
import os
import setuptools
about = {}
with open("packagex/_version.py") as f:
exec(f.read(), about)
os.environ["PBR_VERSION"] = about["__version__"]
setuptools.setup(
setup_requires=["pbr"],
pbr=True,
version=about["__version__"],
)
packagex/_version.py
contains just
__version__ = "0.0.1"
and packagex/__init__.py
from ._version import __version__
and for .bumpversion.cfg
[bumpversion]
current_version = 0.0.1
commit = False
tag = False
message = Bump version to v{new_version}
tag_message = v{new_version}
[bumpversion:file:packagex/_version.py]
And here is a GitHub project with this setup.
Upvotes: 5
Reputation: 777
Add __version__="0.0.1"
to your main __init__.py
:
import .submodule
__version__ = '0.0.1'
If your library is called mylib
, this is the single source of truth for your version number which you can directly access by mylib.__version__
. But you still need to add it to your setup.py
file.
In your setup.py
, you need to read the __init__.py
as a text file:
import re
from setuptools import setup, find_packages
with open("mylib/__init__.py") as f:
version= re.findall("__version__.*(\d.\d.\d).*", f.read())[0]
setup(
name="mylib",
version=version,
packages=find_packages()
)
There are different ways to read the __version__
variable from __init__.py
as a text file. Check this one as an example: Single-sourcing the package version
Please keep in mind that you need the avoid the following points:
__version__
from __init__.py
because this loads all sub-modules with their dependencies that are not available at the setup time and will fail.__version__
in a separate file (e.g. version.py
) if you are going to import it from your main directory (i.e. where your __init__.py
is located) because it will automatically trigger __init__.py
which will lead to the same issue mentioned in point 1.Upvotes: 1
Reputation: 3548
Seeing so many answers is a clean sign that there is no standard way.
So here's another: Poetry and importlib
I use the poetry-dynamic-versioning plugin to set the version on poetry build
.
Then in the __init__.py
of my package I have:
from importlib.metadata import version
__version__ = version(__name__)
Of course this requires to have a proper package structure and build process.
Upvotes: 5
Reputation: 5156
If your project uses git or mercurial as its SCM, I would recommend the following:
Configure your __init__.py
as shown below so that it always sets the __version__
attribute correctly - both in development mode, non-pip development mode (python path updated by IDE), or production mode (pip install)
try:
# -- Distribution mode --
# import from _version.py generated by setuptools_scm during release
from ._version import version as __version__
except ImportError:
# -- Source mode --
# use setuptools_scm to get the current version from src using git
from setuptools_scm import get_version as _gv
from os import path as _path
__version__ = _gv(_path.join(_path.dirname(__file__), _path.pardir))
then, choose a way to generate the _version.py
file that is required in production (the one imported on line 4 above).
a. I personally do this in setup.py
:
setup(
...
use_scm_version={'write_to': '%s/_version.py' % <pkgname>}
)
b. Alternately if you prefer to configure your project using a pyproject.toml
or setup.cfg
file, you can check the documentation in setuptools_scm to find how this is done. For example with pyproject.toml
it is like this:
[tool.setuptools_scm]
write_to = "<pkgname>/_version.py"
c. Finally, an alternative is to execute a script manually when you wish to create releases. This script must run after git-tagging your project and before publishing it.
from setuptools_scm import get_version
get_version('.', write_to='<pkg_name>/_version.py')
you can for example run this as a single commandline in your continuous integration process:
> python -c "from setuptools_scm import get_version;get_version('.', write_to='<pkg_name>/_version.py')"
This pattern (1+2a) has worked successfully for me for dozens of published packages over the past years (for example makefun), so I can warmly recommend it.
Note: I originally provided the above tip in a separate web page here.
EDIT: setuptools_scm
's documentation states that write_to
is now deprecated (but still supported at the time of writing). They recommend to use version_file
instead.
Upvotes: -1
Reputation: 362796
No, there isn't a standard way to embed the version string in a Python package so that it's accessible as an attribute. A standard was proposed in PEP 396 – Module Version Numbers, but that PEP has been rejected in 2021.
What has been standardized:
pyproject.toml
(see Declaring project metadata and PEP 621 – Storing project metadata in pyproject.toml
)A couple of examples of non-standard approaches are a VERSION
tuple:
>>> import django
>>> django.VERSION
(4, 2, 4, 'final', 0)
or a __version__
string:
>>> import requests
>>> requests.__version__
'2.31.0'
Increasingly, you'll find that many projects don't store a version attribute at all. Regardless of whether a project keeps a version attribute burnt into the source code, the recommended way to retrieve a package version reliably is by using stdlib importlib.metadata
:
>>> from importlib.metadata import version
>>> version("django")
'4.2.4'
>>> version("requests")
'2.31.0'
If you've historically provided a version attribute for your package, and you wish to remove it but need to allow a deprecation period for backwards-compatibility reasons, you may use a module level __getattr__
fallback:
# myproj/__init__.py
import warnings
from importlib.metadata import version
def __getattr__(name: str):
if name == "__version__":
warnings.warn(
f"Accessing myproj.__version__ is deprecated and will be "
"removed in a future release. Use importlib.metadata directly.",
DeprecationWarning,
stacklevel=2,
)
return version("myproj")
raise AttributeError(f"module {__name__} has no attribute {name}")
This is the approach used by python-attrs
23.1.0 for example.
$ PYTHONWARNINGS=always python3 -c 'from attrs import __version__'
<string>:1: DeprecationWarning: Accessing attrs.__version__ is deprecated and will be removed in a future release. Use importlib.metadata directly to query for attrs's packaging metadata.
Don't bother using the module __getattr__
approach unless you're deprecating an existing version attribute from an old project. For new projects, my recommendation is not to provide a version attribute in the source code in the first place, so that the single source of truth for the version string is in the package metadata.
Upvotes: 4
Reputation: 490
Setuptools now offers a way to dynamically get version in pyproject.toml
Reproducing the example here, you can create something like the following in your pyproject.toml
# ...
[project]
name = "my_package"
dynamic = ["version"]
# ...
[tool.setuptools.dynamic]
version = {attr = "my_package.__version__"}
Upvotes: 5
Reputation: 21089
There is a slightly simpler alternative to some of the other answers:
__version_info__ = ('1', '2', '3')
__version__ = '.'.join(__version_info__)
(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()
and str()
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.
Upvotes: 31
Reputation: 19916
Not directly an answer to your question, but you should consider naming it __version__
, not version
.
This is almost a quasi-standard. Many modules in the standard library use __version__
, and this is also used in lots of 3rd-party modules, so it's the quasi-standard.
Usually, __version__
is a string, but sometimes it's also a float or tuple.
As mentioned by S.Lott (Thank you!), PEP 8 says it explicitly:
Module Level Dunder Names
Module level "dunders" (i.e. names with two leading and two trailing underscores) such as
__all__
,__author__
,__version__
, etc. should be placed after the module docstring but before any import statements except from__future__
imports.
You should also make sure that the version number conforms to the format described in PEP 440 (PEP 386 a previous version of this standard).
Upvotes: 209
Reputation: 4431
I prefer to read the package version from installation environment.
This is my src/foo/_version.py
:
from pkg_resources import get_distribution
__version__ = get_distribution('foo').version
Makesure foo
is always already installed, that's why a src/
layer is required to prevent foo
imported without installation.
In the setup.py
, I use setuptools-scm to generate the version automatically.
Update in 2022.7.5:
There is another way, which is my faviourate now. Use setuptools-scm
to generate a _version.py
file.
setup(
...
use_scm_version={
'write_to':
'src/foo/_version.py',
'write_to_template':
'"""Generated version file."""\n'
'__version__ = "{version}"\n',
},
)
Upvotes: 1
Reputation: 25302
Per the deferred [STOP PRESS: rejected] PEP 396 (Module Version Numbers), there is a proposed way to do this. It describes, with rationale, an (admittedly optional) standard for modules to follow. Here's a snippet:
- When a module (or package) includes a version number, the version SHOULD be available in the
__version__
attribute.
- For modules which live inside a namespace package, the module SHOULD include the
__version__
attribute. The namespace package itself SHOULD NOT include its own__version__
attribute.
- The
__version__
attribute's value SHOULD be a string.
Upvotes: 33
Reputation: 170508
After 13+ years of writing Python code and managing various packages, I came to the conclusion that DIY is maybe not the best approach.
I started using the pbr
package for dealing with versioning in my packages. If you are using git as your SCM, this will fit into your workflow like magic, saving your weeks of work (you will be surprised about how complex the issue can be).
As of today, pbr has 12M mongthly downloads, and reaching this level didn't include any dirty tricks. It was only one thing -- fixing a common packaging problem in a very simple way.
pbr
can do more of the package maintenance burden, and is not limited to versioning, but it does not force you to adopt all its benefits.
So to give you an idea about how it looks to adopt pbr in one commit have a look switching packaging to pbr
Probably you would observed that the version is not stored at all in the repository. PBR does detect it from Git branches and tags.
No need to worry about what happens when you do not have a git repository because pbr does "compile" and cache the version when you package or install the applications, so there is no runtime dependency on git.
Here is the best solution I've seen so far and it also explains why:
Inside yourpackage/version.py
:
# Store the version here so:
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '0.12'
Inside yourpackage/__init__.py
:
from .version import __version__
Inside setup.py
:
exec(open('yourpackage/version.py').read())
setup(
...
version=__version__,
...
If you know another approach that seems to be better let me know.
Upvotes: 144
Reputation: 11659
setuptools
and pbr
There is not a standard way to manage version, 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 the pbr
extension. This is now my standard way of managing version.
Setting up your project for full packaging may be overkill for simple projects, but if you need to manage version, you are probably at the right level to just set everything up. Doing so also makes your package releasable at PyPi so everyone can download and use it with Pip.
PBR moves most metadata out of the setup.py
tools and into a setup.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 like pyinstaller
if needed (if so, you will probably need this info), and separates the metadata from the other package management/setup scripts. You can directly update the version string in setup.cfg
manually, and it will be pulled into the *.egg-info
folder when building your package releases. Your scripts can then access the version from the metadata using various methods (these processes are outlined in sections below).
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. For version specifically, it will create a version string for the current commit based on git tags in the repo.
setup.py
and a setup.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 (using setup.py
)
setuptools
will pull the latest info in real-time using setup.py
:
python setup.py --version
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.
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 the setup.py --version
command and then stores that version info into the package.egg-info
folder in a set of files that store distribution metadata.
Note on process to update version meta-data:
If you are not using pbr to pull version data from git, then just update your setup.cfg directly with new version info (easy enough, but make sure this is a standard part of your release process).
If you are using git, and you don't need to create a source or binary distribution (using
python setup.py sdist
or one of thepython setup.py bdist_xxx
commands) the simplest way to update the git repo info into your<mypackage>.egg-info
metadata folder is to just run thepython setup.py install
command. This will run all the PBR functions related to pulling metadata from the git repo and update your local.egg-info
folder, install script executables for any entry-points you have defined, and other functions you can see from the output when you run this command.Note that the
.egg-info
folder is generally excluded from being stored in the git repo itself in standard Python.gitignore
files (such as from Gitignore.IO), as it can be generated from your source. If it is excluded, make sure you have a standard "release process" to get the metadata updated locally before release, and any package you upload to PyPi.org or otherwise distribute must include this data to have the correct version. If you want the Git repo to contain this info, you can exclude specific files from being ignored (i.e. add!*.egg-info/PKG_INFO
to.gitignore
)
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:
## This one is a new built-in as of Python 3.8.0 should become the standard
from importlib.metadata import version
v0 = version("mypackage")
print('v0 {}'.format(v0))
## I don't like this one because the version method is hidden
import pkg_resources # part of setuptools
v1 = pkg_resources.require("mypackage")[0].version
print('v1 {}'.format(v1))
# Probably best for pre v3.8.0 - the output without .version is just a longer string with
# both the package name, a space, and the version string
import pkg_resources # part of setuptools
v2 = pkg_resources.get_distribution('mypackage').version
print('v2 {}'.format(v2))
## This one seems to be slower, and with pyinstaller makes the exe a lot bigger
from pbr.version import VersionInfo
v3 = VersionInfo('mypackage').release_string()
print('v3 {}'.format(v3))
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:
__all__ = (
'__version__',
'my_package_name'
)
import pkg_resources # part of setuptools
__version__ = pkg_resources.get_distribution("mypackage").version
Upvotes: 5
Reputation: 3807
_version.txt
in the same folder as __init__.py
and write version as a single line:0.8.2
_version.txt
in __init__.py
: import os
def get_version():
with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), "_version.txt")) as f:
return f.read().strip()
__version__ = get_version()
Upvotes: 0
Reputation: 4689
After several hours of trying to find the simplest reliable solution, here are the parts:
create a version.py file INSIDE the folder of your package "/mypackage":
# Store the version here so:
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '1.2.7'
in setup.py:
exec(open('mypackage/version.py').read())
setup(
name='mypackage',
version=__version__,
in the main folder init.py:
from .version import __version__
The exec()
function runs the script outside of any imports, since setup.py is run before the module can be imported. You still only need to manage the version number in one file in one place, but unfortunately it is not in setup.py. (that's the downside, but having no import bugs is the upside)
Upvotes: 4
Reputation: 7222
arrow handles it in an interesting way.
Now (since 2e5031b)
In arrow/__init__.py
:
__version__ = 'x.y.z'
In setup.py
:
from arrow import __version__
setup(
name='arrow',
version=__version__,
# [...]
)
Before
In arrow/__init__.py
:
__version__ = 'x.y.z'
VERSION = __version__
In setup.py
:
def grep(attrname):
pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
strval, = re.findall(pattern, file_text)
return strval
file_text = read(fpath('arrow/__init__.py'))
setup(
name='arrow',
version=grep('__version__'),
# [...]
)
Upvotes: 6
Reputation: 2412
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:
__version__ = "3.6.5"
And so your setup.py opens the file and parses it, with code like:
import re
VERSIONFILE="myniftyapp/_version.py"
verstrline = open(VERSIONFILE, "rt").read()
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
mo = re.search(VSRE, verstrline, re.M)
if mo:
verstr = mo.group(1)
else:
raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))
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:
from _version import __version__
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.
Upvotes: 175
Reputation: 877
version.py
file only with __version__ = <VERSION>
param in the file. In the setup.py
file import the __version__
param and put it's value in the setup.py
file like this:
version=__version__
setup.py
file with version=<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 your setup.py
file if you don't have it already.
You should use a short script like this every time you bump a version:
bumpversion (patch|minor|major) - choose only one option
git push
git push --tags
Then add one file per repo called: .bumpversion.cfg
:
[bumpversion]
current_version = <CURRENT_TAG>
commit = True
tag = True
tag_name = {new_version}
[bumpversion:file:<RELATIVE_PATH_TO_SETUP_FILE>]
Note:
__version__
parameter under version.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
or git reset
everything in your repo, otherwise you'll get a dirty repo error. Upvotes: -1
Reputation: 117028
Many of these solutions here ignore git
version tags which still means you have to track version in multiple places (bad). I approached this with the following goals:
git
repogit tag
/push
and setup.py upload
steps with a single command that takes no inputs.From a make release
command, the last tagged version in the git repo is found and incremented. The tag is pushed back to origin
.
The Makefile
stores the version in src/_version.py
where it will be read by setup.py
and also included in the release. Do not check _version.py
into source control!
setup.py
command reads the new version string from package.__version__
.
# remove optional 'v' and trailing hash "v1.0-N-HASH" -> "v1.0-N"
git_describe_ver = $(shell git describe --tags | sed -E -e 's/^v//' -e 's/(.*)-.*/\1/')
git_tag_ver = $(shell git describe --abbrev=0)
next_patch_ver = $(shell python versionbump.py --patch $(call git_tag_ver))
next_minor_ver = $(shell python versionbump.py --minor $(call git_tag_ver))
next_major_ver = $(shell python versionbump.py --major $(call git_tag_ver))
.PHONY: ${MODULE}/_version.py
${MODULE}/_version.py:
echo '__version__ = "$(call git_describe_ver)"' > $@
.PHONY: release
release: test lint mypy
git tag -a $(call next_patch_ver)
$(MAKE) ${MODULE}/_version.py
python setup.py check sdist upload # (legacy "upload" method)
# twine upload dist/* (preferred method)
git push origin master --tags
The release
target always increments the 3rd version digit, but you can use the next_minor_ver
or next_major_ver
to increment the other digits. The commands rely on the versionbump.py
script that is checked into the root of the repo
"""An auto-increment tool for version strings."""
import sys
import unittest
import click
from click.testing import CliRunner # type: ignore
__version__ = '0.1'
MIN_DIGITS = 2
MAX_DIGITS = 3
@click.command()
@click.argument('version')
@click.option('--major', 'bump_idx', flag_value=0, help='Increment major number.')
@click.option('--minor', 'bump_idx', flag_value=1, help='Increment minor number.')
@click.option('--patch', 'bump_idx', flag_value=2, default=True, help='Increment patch number.')
def cli(version: str, bump_idx: int) -> None:
"""Bumps a MAJOR.MINOR.PATCH version string at the specified index location or 'patch' digit. An
optional 'v' prefix is allowed and will be included in the output if found."""
prefix = version[0] if version[0].isalpha() else ''
digits = version.lower().lstrip('v').split('.')
if len(digits) > MAX_DIGITS:
click.secho('ERROR: Too many digits', fg='red', err=True)
sys.exit(1)
digits = (digits + ['0'] * MAX_DIGITS)[:MAX_DIGITS] # Extend total digits to max.
digits[bump_idx] = str(int(digits[bump_idx]) + 1) # Increment the desired digit.
# Zero rightmost digits after bump position.
for i in range(bump_idx + 1, MAX_DIGITS):
digits[i] = '0'
digits = digits[:max(MIN_DIGITS, bump_idx + 1)] # Trim rightmost digits.
click.echo(prefix + '.'.join(digits), nl=False)
if __name__ == '__main__':
cli() # pylint: disable=no-value-for-parameter
This does the heavy lifting how to process and increment the version number from git
.
The my_module/_version.py
file is imported into my_module/__init__.py
. Put any static install config here that you want distributed with your module.
from ._version import __version__
__author__ = ''
__email__ = ''
The last step is to read the version info from the my_module
module.
from setuptools import setup, find_packages
pkg_vars = {}
with open("{MODULE}/_version.py") as fp:
exec(fp.read(), pkg_vars)
setup(
version=pkg_vars['__version__'],
...
...
)
Of course, for all of this to work you'll have to have at least one version tag in your repo to start.
git tag -a v0.0.1
Upvotes: 17
Reputation: 1
If you use CVS (or RCS) and want a quick solution, you can use:
__version__ = "$Revision: 1.1 $"[11:-2]
__version_info__ = tuple([int(s) for s in __version__.split(".")])
(Of course, the revision number will be substituted for you by CVS.)
This gives you a print-friendly version and a version info that you can use to check that the module you are importing has at least the expected version:
import my_module
assert my_module.__version_info__ >= (1, 1)
Upvotes: -3
Reputation: 920
I use a JSON file in the package dir. This fits Zooko's requirements.
Inside pkg_dir/pkg_info.json
:
{"version": "0.1.0"}
Inside setup.py
:
from distutils.core import setup
import json
with open('pkg_dir/pkg_info.json') as fp:
_info = json.load(fp)
setup(
version=_info['version'],
...
)
Inside pkg_dir/__init__.py
:
import json
from os.path import dirname
with open(dirname(__file__) + '/pkg_info.json') as fp:
_info = json.load(fp)
__version__ = _info['version']
I also put other information in pkg_info.json
, like author. I
like to use JSON because I can automate management of metadata.
Upvotes: 16
Reputation: 2855
Also worth noting is that as well as __version__
being a semi-std. in python so is __version_info__
which is a tuple, in the simple cases you can just do something like:
__version__ = '1.2.3'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])
...and you can get the __version__
string from a file, or whatever.
Upvotes: 7
Reputation: 75785
There doesn't seem to be a standard way to embed a version string in a python package. Most packages I've seen use some variant of your solution, i.e. eitner
Embed the version in setup.py
and have setup.py
generate a module (e.g. version.py
) containing only version info, that's imported by your package, or
The reverse: put the version info in your package itself, and import that to set the version in setup.py
Upvotes: 6
Reputation: 747
For what it's worth, if you're using NumPy distutils, numpy.distutils.misc_util.Configuration
has a make_svn_version_py()
method that embeds the revision number inside package.__svn_version__
in the variable version
.
Upvotes: -4