Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: updated single_source_version with a much simpler page. #1578

Closed
Closed
Changes from 6 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
110 changes: 20 additions & 90 deletions source/single_source_version.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,111 +7,41 @@ Single-sourcing the Project Version
:Page Status: Complete
:Last Reviewed: 2015-09-08
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

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``)
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved
- That will assure that it is properly assigned in the distribution file name, and in the installed package.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved
#. 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, if the maintainers think it useful, to be able to determine the version of the package from Python code without relying on a package manager's metadata, set a top level ``__version__`` attribute.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved
The value of ``__version__`` attribute and that passed to the build system can (and should) be kept in sync in :ref:`the build systems's recommended way <how_to_set_version_links>`.

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()
Should a package not set a top level ``__version__`` attribute, the version can still be accessed using ``importlib.metadata.version("distribution_name")``.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

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.")
While different projects have different needs, it's important to make sure that there is a single source of truth for the version number.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

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.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

This technique has the disadvantage of having to deal with complexities of regular expressions.
2) The version can be hard-coded into the `pyproject.toml` file -- and the build system can copy it into other locations it may be required.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

#. 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 ``_version.txt``, or as a attribute in the ``__init__.py``, and the build system can extract it at build time.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

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 documentation for how to implement your preferred method.
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

#. 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.
.. _how_to_set_version_links:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like other references use spaces. Perhaps, follow the same style?


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

::
* `Hatch <https://hatch.pypa.io/1.9/version/>`_
ChrisBarker-NOAA marked this conversation as resolved.
Show resolved Hide resolved

execfile('...sample/version.py')
# now we have a `__version__` variable
# later on we use: __version__
* `Setuptools <https://setuptools.pypa.io/en/latest/userguide/distribution.html#specifying-your-project-s-version>`_

Using ``exec``:
- `setuptools_scm <https://setuptools-scm.readthedocs.io/en/latest/>`_

::
* `Flit <https://flit.pypa.io/en/stable/>`_

version = {}
with open("...sample/version.py") as fp:
exec(fp.read(), version)
# later on we use: version['__version__']
* `PDM <https://pdm-project.org/en/latest/reference/pep621/#__tabbed_1_2>`_

Example using this technique: `warehouse <https://github.com/pypa/warehouse/blob/master/warehouse/__about__.py>`_.

#. 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.