-
Notifications
You must be signed in to change notification settings - Fork 1.4k
WIP: updated single_source_version with a much simpler page. #1578
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
Changes from 10 commits
fb3ec88
c7fa00c
49842a8
840801f
3724c8d
5368956
56db0d9
9bace5d
035c2bd
dd1b70e
eaf458a
29aa220
de722f6
63061bd
bfdc474
c69e2c0
ddb077c
648c427
b9ced45
0f5d2d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,111 +7,40 @@ Single-sourcing the Project Version | |
| :Page Status: Complete | ||
| :Last Reviewed: 2015-09-08 | ||
|
|
||
| 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.
Outdated
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.
Outdated
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 set a top level ``__version__`` attribute to provide runtime access to the version of the installed 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 <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() | ||
| In the cases where a package does not set a top level ``__version__`` attribute, the version may still 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. | ||
ChrisBarker-NOAA marked this conversation as resolved.
Outdated
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.
Outdated
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.
Outdated
Show resolved
Hide resolved
ChrisBarker-NOAA marked this conversation as resolved.
Outdated
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'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. | ||
| .. _how_to_set_version_links: | ||
|
||
|
|
||
| Using ``execfile``: | ||
| Build System Version Handling | ||
| ---------------------------- | ||
|
|
||
| :: | ||
| * `Hatch <https://hatch.pypa.io/1.9/version/>`_ | ||
ChrisBarker-NOAA marked this conversation as resolved.
Outdated
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. | ||
Uh oh!
There was an error while loading. Please reload this page.