diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c6a1968e..c30f2bce 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,9 @@ +v3.4.0 +====== + +* fix #181 - add support for projects built under setuptools declarative config + by way of the setuptools.finalize_distribution_options hook in Setuptools 42. + * fix #305 - ensure the git file finder closes filedescriptors even when errors happen v3.3.3 diff --git a/README.rst b/README.rst index f55c6df8..ebad26de 100644 --- a/README.rst +++ b/README.rst @@ -13,9 +13,63 @@ It also handles file finders for the supported SCMs. .. image:: https://tidelift.com/badges/github/pypa/setuptools_scm :target: https://tidelift.com/subscription/pkg/pypi-setuptools_scm?utm_source=pypi-setuptools_scm&utm_medium=readme +``pyproject.toml`` usage +------------------------ + +The preferred way to configure ``setuptools_scm`` is to author +settings in a ``tool.setuptools_scm`` section of ``pyproject.toml``. + +This feature requires Setuptools 42 or later, released in Nov, 2019. +If your project needs to support build from sdist on older versions +of Setuptools, you will need to also implement the ``setup.py usage`` +for those legacy environments. + +First, ensure that ``setuptools_scm`` is present during the project's +built step by specifying it as one of the build requirements. + +.. code:: ini + + # pyproject.toml + [build-system] + requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] + +Note that the ``toml`` extra must be supplied. + +That will be sufficient to require ``setuptools_scm`` for projects +that support PEP 518 (`pip `_ and +`pep517 `_). Many tools, +especially those that invoke ``setup.py`` for any reason, may +continue to rely on ``setup_requires``. For maximum compatibility +with those uses, consider also including a ``setup_requires`` directive +(described below in ``setup.py usage`` and ``setup.cfg``). + +To enable version inference, add this section to your pyproject.toml: + +.. code:: ini + + # pyproject.toml + [tools.setuptools_scm] + +Including this section is comparable to supplying +``use_scm_version=True`` in ``setup.py``. Additionally, +include arbitrary keyword arguments in that section +to be supplied to ``get_version()``. For example:: + +.. code:: ini + + # pyproject.toml + [tools.setuptools_scm] + write_to = pkg/version.py + + ``setup.py`` usage ------------------ +The following settings are considered legacy behavior and +superseded by the ``pyproject.toml`` usage, but for maximal +compatibility, projects may also supply the configuration in +this older form. + To use ``setuptools_scm`` just modify your project's ``setup.py`` file like this: diff --git a/setup.py b/setup.py index 2eb533bc..ced9a3b2 100644 --- a/setup.py +++ b/setup.py @@ -68,6 +68,9 @@ def parse(root): [setuptools.file_finders] setuptools_scm = setuptools_scm.integration:find_files + [setuptools.finalize_distribution_options] + setuptools_scm = setuptools_scm.integration:infer_version + [setuptools_scm.parse_scm] .hg = setuptools_scm.hg:parse .git = setuptools_scm.git:parse @@ -111,6 +114,7 @@ def parse(root): "Topic :: Utilities", ], python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", + extras_require=dict(toml=["toml"]), ) if __name__ == "__main__": diff --git a/src/setuptools_scm/__init__.py b/src/setuptools_scm/__init__.py index f3886ed2..2e050061 100644 --- a/src/setuptools_scm/__init__.py +++ b/src/setuptools_scm/__init__.py @@ -146,18 +146,23 @@ def get_version( config.fallback_version = fallback_version config.parse = parse config.git_describe_command = git_describe_command + return _get_version(config) + +def _get_version(config): parsed_version = _do_parse(config) if parsed_version: version_string = format_version( - parsed_version, version_scheme=version_scheme, local_scheme=local_scheme + parsed_version, + version_scheme=config.version_scheme, + local_scheme=config.local_scheme, ) dump_version( - root=root, + root=config.root, version=version_string, - write_to=write_to, - template=write_to_template, + write_to=config.write_to, + template=config.write_to_template, ) return version_string diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index 38f79aed..d548c818 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -105,3 +105,19 @@ def tag_regex(self): @tag_regex.setter def tag_regex(self, value): self._tag_regex = _check_tag_regex(value) + + def _load(self, values): + vars(self).update(values) + return self + + @classmethod + def from_file(cls, name="pyproject.toml"): + """ + Read Configuration from pyproject.toml (or similar). + Raises exceptions when file is not found or toml is + not installed or the file has invalid format or does + not contain the [tool.setuptools_scm] section. + """ + with open(name) as strm: + defn = __import__("toml").load(strm) + return cls()._load(defn.get("tool", {})["setuptools_scm"]) diff --git a/src/setuptools_scm/integration.py b/src/setuptools_scm/integration.py index e18b3e57..c5cc25d8 100644 --- a/src/setuptools_scm/integration.py +++ b/src/setuptools_scm/integration.py @@ -1,8 +1,9 @@ from pkg_resources import iter_entry_points from .version import _warn_if_setuptools_outdated -from .utils import do -from . import get_version +from .config import Configuration +from .utils import do, trace_exception +from . import get_version, _get_version def version_keyword(dist, keyword, value): @@ -28,3 +29,11 @@ def find_files(path=""): if res: return res return [] + + +def infer_version(dist): + try: + config = Configuration.from_file() + except Exception: + return trace_exception() + dist.metadata.version = _get_version(config) diff --git a/src/setuptools_scm/utils.py b/src/setuptools_scm/utils.py index 5b59005a..d2c5b635 100644 --- a/src/setuptools_scm/utils.py +++ b/src/setuptools_scm/utils.py @@ -10,6 +10,7 @@ import os import io import platform +import traceback DEBUG = bool(os.environ.get("SETUPTOOLS_SCM_DEBUG")) @@ -25,6 +26,10 @@ def trace(*k): sys.stdout.flush() +def trace_exception(): + DEBUG and traceback.print_exc() + + def ensure_stripped_str(str_or_bytes): if isinstance(str_or_bytes, str): return str_or_bytes.strip() diff --git a/testing/test_config.py b/testing/test_config.py index eadbaf3d..38bc8f4c 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1,3 +1,5 @@ +from __future__ import unicode_literals + from setuptools_scm.config import Configuration import pytest @@ -18,3 +20,9 @@ def test_tag_regex(tag, expected_version): match = config.tag_regex.match(tag) version = match.group("version") assert version == expected_version + + +def test_config_from_pyproject(tmpdir): + fn = tmpdir / "pyproject.toml" + fn.write_text("[tool.setuptools_scm]\n", encoding="utf-8") + assert Configuration.from_file(str(fn)) diff --git a/testing/test_integration.py b/testing/test_integration.py new file mode 100644 index 00000000..a0d11430 --- /dev/null +++ b/testing/test_integration.py @@ -0,0 +1,16 @@ +import sys + +from setuptools_scm.utils import do + + +def test_pyproject_support(tmpdir, monkeypatch): + monkeypatch.delenv("SETUPTOOLS_SCM_DEBUG") + pkg = tmpdir.ensure("package", dir=42) + pkg.join("pyproject.toml").write( + """[tool.setuptools_scm] +fallback_version = "12.34" +""" + ) + pkg.join("setup.py").write("__import__('setuptools').setup()") + res = do((sys.executable, "setup.py", "--version"), pkg) + assert res == "12.34" diff --git a/tox.ini b/tox.ini index 129f9b47..b51ef3d2 100644 --- a/tox.ini +++ b/tox.ini @@ -25,9 +25,12 @@ skip_install= test: False deps= pytest + setuptools >= 42 commands= test: py.test [] selfcheck: python setup.py --version +extras = + toml [testenv:flake8] skip_install=True