From 4445d67341721d411be85e4ff0bbedc7ec165486 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 27 Aug 2021 10:57:04 +0200 Subject: [PATCH 1/7] fix #605: completely disallow bdist_egg - modern enough setuptools>=45 uses pip --- pyproject.toml | 2 +- setup.py | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 452a8278..2ab901eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools>=45", "wheel"] +requires = ["setuptools>=45", "wheel", "tomli"] build-backend = "setuptools.build_meta" diff --git a/setup.py b/setup.py index 80d091eb..efbe0a40 100644 --- a/setup.py +++ b/setup.py @@ -28,13 +28,8 @@ def scm_config(): has_entrypoints = os.path.isdir(egg_info) import pkg_resources - pkg_resources.require("setuptools>=45") - sys.path.insert(0, src) pkg_resources.working_set.add_entry(src) - # FIXME: remove debug - print(src) - print(pkg_resources.working_set) from setuptools_scm.hacks import parse_pkginfo from setuptools_scm.git import parse as parse_git @@ -50,13 +45,22 @@ def parse(root): version_scheme=guess_next_dev_version, local_scheme=get_local_node_and_date ) + from setuptools.command.bdist_egg import bdist_egg as original_bdist_egg + + class bdist_egg(original_bdist_egg): + def run(self): + raise SystemExit("bdist_egg is forbidden, please update to setuptools>=45") + if has_entrypoints: - return dict(use_scm_version=config) + return dict(use_scm_version=config, cmdclass={"bdist_egg": bdist_egg}) else: from setuptools_scm import get_version - return dict(version=get_version(root=here, parse=parse, **config)) + return dict( + version=get_version(root=here, parse=parse, **config), + cmdclass={"bdist_egg": bdist_egg}, + ) if __name__ == "__main__": - setuptools.setup(**scm_config()) + setuptools.setup(setup_requires=["setuptools>=45"], **scm_config()) From b68366771b104443c99c490a440080ca5ac34dc4 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 27 Aug 2021 10:58:30 +0200 Subject: [PATCH 2/7] fix #606: re-integrate and hardne toml parsing when switching to tomli i missed a part and the test env containing the toml lib did hide the issue --- src/setuptools_scm/config.py | 28 +++++++++++++++++++++++++--- src/setuptools_scm/integration.py | 26 ++++++++++---------------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index 473402e8..bede1afc 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -55,6 +55,12 @@ def _check_absolute_root(root, relative_to): return os.path.abspath(root) +def _lazy_tomli_load(data): + from tomli import loads + + return loads(data) + + class Configuration: """Global configuration model""" @@ -163,16 +169,32 @@ def tag_regex(self, value): self._tag_regex = _check_tag_regex(value) @classmethod - def from_file(cls, name="pyproject.toml", dist_name=None): + def from_file( + cls, + name: str = "pyproject.toml", + dist_name=None, # type: str | None + _load_toml=_lazy_tomli_load, + ): """ 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, encoding="UTF-8") as strm: - defn = __import__("toml").load(strm) - section = defn.get("tool", {})["setuptools_scm"] + data = strm.read() + defn = _load_toml(data) + try: + section = defn.get("tool", {})["setuptools_scm"] + except LookupError: + raise FileNotFoundError( + f"{name} does not contain a tool.setuptools_scm section" + ) from None + if "dist_name" in section: + assert dist_name is None or dist_name == section["dist_name"] + del section["dist_name"] + # todo: investigate getting dist_name from return cls(dist_name=dist_name, **section) diff --git a/src/setuptools_scm/integration.py b/src/setuptools_scm/integration.py index 29c85966..42d3ef73 100644 --- a/src/setuptools_scm/integration.py +++ b/src/setuptools_scm/integration.py @@ -1,12 +1,15 @@ +import warnings + +import setuptools + from . import _get_version from . import Configuration from .utils import do from .utils import iter_entry_points from .utils import trace -from .utils import trace_exception -def version_keyword(dist, keyword, value): +def version_keyword(dist: setuptools.Distribution, keyword, value): if not value: return if value is True: @@ -39,17 +42,7 @@ def find_files(path=""): return [] -def _args_from_toml(name="pyproject.toml"): - # todo: more sensible config initialization - # move this helper back to config and unify it with the code from get_config - import tomli - - with open(name, encoding="UTF-8") as strm: - defn = tomli.load(strm) - return defn.get("tool", {})["setuptools_scm"] - - -def infer_version(dist): +def infer_version(dist: setuptools.Distribution): trace( "finalize hook", vars(dist.metadata), @@ -57,6 +50,7 @@ def infer_version(dist): dist_name = dist.metadata.name try: config = Configuration.from_file(dist_name=dist_name) - except Exception: - return trace_exception() - dist.metadata.version = _get_version(config) + except FileNotFoundError as e: + warnings.warn(str(e)) + else: + dist.metadata.version = _get_version(config) From 63cf87492d3b5ac50aca345c9ba013d4ac2f4419 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 27 Aug 2021 11:02:14 +0200 Subject: [PATCH 3/7] update changelog to prepare for a release --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e57bd018..840be874 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,9 @@ +v6.1.1 +======= + +* fix #605: completely disallow bdist_egg - modern enough setuptools>=45 uses pip +* fix #606: re-integrate and harden toml parsing + v6.1.0 ====== From af86cfc61d97231e3a3402cd909dbf0f865f8af9 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 27 Aug 2021 11:37:37 +0200 Subject: [PATCH 4/7] fix #597: harden and expand support for figuring the distribution name --- CHANGELOG.rst | 2 ++ src/setuptools_scm/config.py | 20 +++++++++++-- testing/test_integration.py | 58 ++++++++++++++++++++++++++++-------- 3 files changed, 65 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 840be874..08a1a9d1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -3,6 +3,8 @@ v6.1.1 * fix #605: completely disallow bdist_egg - modern enough setuptools>=45 uses pip * fix #606: re-integrate and harden toml parsing +* fix #597: harden and expand support for figuring the current distribution name from + `pyproject.toml` (`project.name` or `tool.setuptools_scm.dist_name`) section or `setup.cfg` (`metadata.name`) v6.1.0 ====== diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index bede1afc..5eb3b008 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -192,9 +192,23 @@ def from_file( f"{name} does not contain a tool.setuptools_scm section" ) from None if "dist_name" in section: - assert dist_name is None or dist_name == section["dist_name"] - del section["dist_name"] - # todo: investigate getting dist_name from + if dist_name is None: + dist_name = section.pop("dist_name") + else: + assert dist_name == section["dist_name"] + del section["dist_name"] + if dist_name is None: + if "project" in defn: + # minimal pep 621 support for figuring the pretend keys + dist_name = defn["project"].get("name") + if dist_name is None: + # minimal effort to read dist_name off setup.cfg metadata + import configparser + + parser = configparser.SafeConfigParser() + parser.read(["setup.cfg"]) + dist_name = parser.get("metadata", "name", fallback=None) + return cls(dist_name=dist_name, **section) diff --git a/testing/test_integration.py b/testing/test_integration.py index 56a1a8f4..dba02d19 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -40,30 +40,64 @@ def test_pyproject_support(tmpdir, monkeypatch): assert res == "12.34" -def test_pyproject_support_with_git(tmpdir, monkeypatch, wd): - pytest.importorskip("toml") - pkg = tmpdir.join("wd") - pkg.join("pyproject.toml").write("""[tool.setuptools_scm]""") - pkg.join("setup.py").write( - "__import__('setuptools').setup(name='setuptools_scm_example')" - ) - res = do((sys.executable, "setup.py", "--version"), pkg) +PYPROJECT_FILES = { + "setup.py": "[tool.setuptools_scm]", + "setup.cfg": "[tool.setuptools_scm]", + "pyproject tool.setuptools_scm": ( + "[tool.setuptools_scm]\ndist_name='setuptools_scm_example'" + ), + "pyproject.project": ( + "[project]\nname='setuptools_scm_example'\n[tool.setuptools_scm]" + ), +} + +SETUP_PY_PLAIN = "__import__('setuptools').setup()" +SETUP_PY_WITH_NAME = "__import__('setuptools').setup(name='setuptools_scm_example')" + +SETUP_PY_FILES = { + "setup.py": SETUP_PY_WITH_NAME, + "setup.cfg": SETUP_PY_PLAIN, + "pyproject tool.setuptools_scm": SETUP_PY_PLAIN, + "pyproject.project": SETUP_PY_PLAIN, +} + +SETUP_CFG_FILES = { + "setup.py": "", + "setup.cfg": "[metadata]\nname=setuptools_scm_example", + "pyproject tool.setuptools_scm": "", + "pyproject.project": "", +} + +with_metadata_in = pytest.mark.parametrize( + "metadata_in", + ["setup.py", "setup.cfg", "pyproject tool.setuptools_scm", "pyproject.project"], +) + + +@with_metadata_in +def test_pyproject_support_with_git(wd, metadata_in): + pytest.importorskip("tomli") + wd.write("pyproject.toml", PYPROJECT_FILES[metadata_in]) + wd.write("setup.py", SETUP_PY_FILES[metadata_in]) + wd.write("setup.cfg", SETUP_CFG_FILES[metadata_in]) + res = wd((sys.executable, "setup.py", "--version")) assert res.endswith("0.1.dev0") -def test_pretend_version(tmpdir, monkeypatch, wd): +def test_pretend_version(monkeypatch, wd): monkeypatch.setenv(PRETEND_KEY, "1.0.0") assert wd.get_version() == "1.0.0" assert wd.get_version(dist_name="ignored") == "1.0.0" -def test_pretend_version_named_pyproject_integration(tmpdir, monkeypatch, wd): - test_pyproject_support_with_git(tmpdir, monkeypatch, wd) +@with_metadata_in +def test_pretend_version_named_pyproject_integration(monkeypatch, wd, metadata_in): + test_pyproject_support_with_git(wd, metadata_in) monkeypatch.setenv( PRETEND_KEY_NAMED.format(name="setuptools_scm_example".upper()), "3.2.1" ) - res = do((sys.executable, "setup.py", "--version"), tmpdir / "wd") + res = wd((sys.executable, "setup.py", "--version")) assert res.endswith("3.2.1") From c45645fd5a683b90aacd20ec9fff3c2e40f17ffa Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 27 Aug 2021 11:41:12 +0200 Subject: [PATCH 5/7] update self-install for tomli --- .github/workflows/python-tests.yml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index c2f7d5b1..aac621be 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -67,7 +67,7 @@ jobs: # self install testing needs some clarity # so its being executed without any other tools running # setuptools smaller 52 is needed too di easy_install - - run: pip install -U "setuptools<52" + - run: pip install -U "setuptools<52" tomli - run: python setup.py egg_info - run: python setup.py sdist - run: ${{ matrix.installer }} dist/* diff --git a/setup.py b/setup.py index efbe0a40..2f06aca8 100644 --- a/setup.py +++ b/setup.py @@ -63,4 +63,4 @@ def run(self): if __name__ == "__main__": - setuptools.setup(setup_requires=["setuptools>=45"], **scm_config()) + setuptools.setup(setup_requires=["setuptools>=45", "tomli"], **scm_config()) From c5d77481982bb4d635bdf9e76c38b916789092b5 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 27 Aug 2021 11:47:26 +0200 Subject: [PATCH 6/7] fixup: use ConfigParser as indicated by the deprecation --- src/setuptools_scm/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/setuptools_scm/config.py b/src/setuptools_scm/config.py index 5eb3b008..dad28a9f 100644 --- a/src/setuptools_scm/config.py +++ b/src/setuptools_scm/config.py @@ -205,7 +205,7 @@ def from_file( # minimal effort to read dist_name off setup.cfg metadata import configparser - parser = configparser.SafeConfigParser() + parser = configparser.ConfigParser() parser.read(["setup.cfg"]) dist_name = parser.get("metadata", "name", fallback=None) From c3eb223cff7a761e891b48c7b1c9a88ee030f996 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 27 Aug 2021 11:50:37 +0200 Subject: [PATCH 7/7] drop the disabled easy_install from self_install tests --- .github/workflows/python-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index aac621be..17d7410c 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -55,7 +55,7 @@ jobs: fail-fast: false matrix: python_version: [ '3.6', '3.9', 'pypy3' ] - installer: ["pip install", easy_install] + installer: ["pip install"] name: check self install - Python ${{ matrix.python_version }} via ${{ matrix.installer }} steps: - uses: actions/checkout@v1