Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fb3ec88
updated single_source_version with a much simpler page -- essentially
ChrisBarker-NOAA Jul 25, 2024
c7fa00c
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 25, 2024
49842a8
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 25, 2024
840801f
Added links to build tools
ChrisBarker-NOAA Jul 25, 2024
3724c8d
swap prefer for require
ChrisBarker-NOAA Jul 25, 2024
5368956
replace text about __version__
ChrisBarker-NOAA Jul 25, 2024
56db0d9
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 26, 2024
9bace5d
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 26, 2024
035c2bd
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 26, 2024
dd1b70e
updated the __version__ description
ChrisBarker-NOAA Jul 26, 2024
eaf458a
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 30, 2024
29aa220
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 30, 2024
de722f6
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 30, 2024
63061bd
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 30, 2024
bfdc474
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 30, 2024
c69e2c0
a few suggestions from the PR discussion
ChrisBarker-NOAA Jul 30, 2024
ddb077c
Merge branch 'simplify_single_source' of https://github.com/ChrisBark…
ChrisBarker-NOAA Jul 30, 2024
648c427
minor formatting edit
ChrisBarker-NOAA Jul 30, 2024
b9ced45
Update source/single_source_version.rst
ChrisBarker-NOAA Jul 31, 2024
0f5d2d3
a few more edits from the PR comments, and adding it back to the inde…
ChrisBarker-NOAA Jul 31, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This guide is maintained on `github
current
installing
distributing
single_source_version
additional
projects
glossary
Expand Down
109 changes: 21 additions & 88 deletions source/single_source_version.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,113 +5,46 @@ Single-sourcing the Project Version
===================================

:Page Status: Complete
:Last Reviewed: 2015-09-08
:Last Reviewed: 2024-??

One of the challenges in building packages is that the version string can be required in multiple places.

There are a few techniques to store the version in your project code without duplicating the value stored in
``setup.py``:
* It needs to be specified when building the package (e.g. in :file:`pyproject.toml`)
- That will assure that it is properly assigned in the distribution file name, and in the installed package metadata.

#. Read the file in ``setup.py`` and parse the version with a regex. Example (
from `pip setup.py <https://github.com/pypa/pip/blob/1.5.6/setup.py#L33>`_)::
* A package may set a top level ``__version__`` attribute to provide runtime access to the version of the imported package. If this is done, the value of ``__version__`` attribute and that used by the build system to set the distribution's version should be kept in sync in :ref:`the build systems's recommended way <Build system version handling>`.

def read(*names, **kwargs):
with io.open(
os.path.join(os.path.dirname(__file__), *names),
encoding=kwargs.get("encoding", "utf8")
) as fp:
return fp.read()
In any case, The installed distribution version should be accessible using ``importlib.metadata.version("distribution_name")``.

def find_version(*file_paths):
version_file = read(*file_paths)
version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
version_file, re.M)
if version_match:
return version_match.group(1)
raise RuntimeError("Unable to find version string.")
To ensure that version numbers do not get out of sync, it is recommended that there is a single source of truth for the version number.

setup(
...
version=find_version("package", "__init__.py")
...
)
In general, the options are:

.. note::
1) If the code is in a version control system (VCS), e.g. Git, then the version can be extracted from the VCS.

This technique has the disadvantage of having to deal with complexities of regular expressions.
2) The version can be hard-coded into the :file:`pyproject.toml` file -- and the build system can copy it into other locations it may be required.

#. Use an external build tool that either manages updating both locations, or
offers an API that both locations can use.
3) The version string can be hard-coded into the source code -- either in a special purpose file, such as :file:`_version.txt`, or as a attribute in the :file:`__init__.py`, and the build system can extract it at build time.

Few tools you could use, in no particular order, and not necessarily complete:
`bumpversion <https://pypi.python.org/pypi/bumpversion>`_,
`changes <https://pypi.python.org/pypi/changes>`_, `zest.releaser <https://pypi.python.org/pypi/zest.releaser>`_.

Consult your build system's documentation for their recommended method.

#. Set the value to a ``__version__`` global variable in a dedicated module in
your project (e.g. ``version.py``), then have ``setup.py`` read and ``exec`` the
value into a variable.
.. _Build system version handling:

Using ``execfile``:
Build System Version Handling
-----------------------------

::
The following are links to some build system's documentation for handling version strings.

execfile('...sample/version.py')
# now we have a `__version__` variable
# later on we use: __version__
* `Flit <https://flit.pypa.io/en/stable/>`_

Using ``exec``:
* `Hatchling <https://hatch.pypa.io/1.9/version/>`_

::
* `PDM <https://pdm-project.org/en/latest/reference/pep621/#__tabbed_1_2>`_

version = {}
with open("...sample/version.py") as fp:
exec(fp.read(), version)
# later on we use: version['__version__']
* `Setuptools <https://setuptools.pypa.io/en/latest/userguide/distribution.html#specifying-your-project-s-version>`_

Example using this technique: `warehouse <https://github.com/pypa/warehouse/blob/master/warehouse/__about__.py>`_.
- `setuptools_scm <https://setuptools-scm.readthedocs.io/en/latest/>`_

#. Place the value in a simple ``VERSION`` text file and have both ``setup.py``
and the project code read it.

::

with open(os.path.join(mypackage_root_dir, 'VERSION')) as version_file:
version = version_file.read().strip()

An advantage with this technique is that it's not specific to Python. Any
tool can read the version.

.. warning::

With this approach you must make sure that the ``VERSION`` file is included in
all your source and binary distributions.

#. Set the value in ``setup.py``, and have the project code use the
``pkg_resources`` API.

::

import pkg_resources
assert pkg_resources.get_distribution('pip').version == '1.2.0'

Be aware that the ``pkg_resources`` API only knows about what's in the
installation metadata, which is not necessarily the code that's currently
imported.


#. Set the value to ``__version__`` in ``sample/__init__.py`` and import
``sample`` in ``setup.py``.

::

import sample
setup(
...
version=sample.__version__
...
)

Although this technique is common, beware that it will fail if
``sample/__init__.py`` imports packages from ``install_requires``
dependencies, which will very likely not be installed yet when ``setup.py``
is run.