From 0f7c71d6d90b7ccfc9157955aeff3bdebcebe798 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 5 Aug 2025 18:32:23 +0200 Subject: [PATCH] in case infer-version finds neither the config section, nor the requirement - skip action closes #1185 --- CHANGELOG.md | 6 + src/setuptools_scm/_config.py | 9 +- .../_integration/pyproject_reading.py | 7 +- src/setuptools_scm/_integration/setuptools.py | 16 ++- testing/test_integration.py | 119 ++++++++++++++++++ 5 files changed, 152 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ec68c2d..4b850cf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## v9.0.2 + +### Fixed + +- fix #1184: in case setuptools-scm is a indirect dependency and no pyproject.toml section exists - don't infer the version + ## v9.0.1 diff --git a/src/setuptools_scm/_config.py b/src/setuptools_scm/_config.py index c4ebc773..fbc53636 100644 --- a/src/setuptools_scm/_config.py +++ b/src/setuptools_scm/_config.py @@ -272,6 +272,7 @@ def from_file( dist_name: str | None = None, missing_file_ok: bool = False, missing_section_ok: bool = False, + pyproject_data: PyProjectData | None = None, **kwargs: Any, ) -> Configuration: """ @@ -291,9 +292,10 @@ def from_file( """ try: - pyproject_data = _read_pyproject( - Path(name), missing_section_ok=missing_section_ok - ) + if pyproject_data is None: + pyproject_data = _read_pyproject( + Path(name), missing_section_ok=missing_section_ok + ) except FileNotFoundError: if missing_file_ok: log.warning("File %s not found, using empty configuration", name) @@ -303,6 +305,7 @@ def from_file( project={}, section={}, is_required=False, + section_present=False, ) else: raise diff --git a/src/setuptools_scm/_integration/pyproject_reading.py b/src/setuptools_scm/_integration/pyproject_reading.py index 8e59ce4e..7c3108a3 100644 --- a/src/setuptools_scm/_integration/pyproject_reading.py +++ b/src/setuptools_scm/_integration/pyproject_reading.py @@ -22,6 +22,7 @@ class PyProjectData(NamedTuple): project: TOML_RESULT section: TOML_RESULT is_required: bool + section_present: bool @property def project_name(self) -> str | None: @@ -55,6 +56,7 @@ def read_pyproject( try: section = defn.get("tool", {})[tool_name] + section_present = True except LookupError as e: if not is_required and not missing_section_ok: # Enhanced error message that mentions both configuration options @@ -69,9 +71,12 @@ def read_pyproject( error = f"{path} does not contain a tool.{tool_name} section" log.warning("toml section missing %r", error, exc_info=True) section = {} + section_present = False project = defn.get("project", {}) - return PyProjectData(path, tool_name, project, section, is_required) + return PyProjectData( + path, tool_name, project, section, is_required, section_present + ) def get_args_for_pyproject( diff --git a/src/setuptools_scm/_integration/setuptools.py b/src/setuptools_scm/_integration/setuptools.py index e5ff73e5..7f76dfd3 100644 --- a/src/setuptools_scm/_integration/setuptools.py +++ b/src/setuptools_scm/_integration/setuptools.py @@ -4,6 +4,7 @@ import os import warnings +from pathlib import Path from typing import Any from typing import Callable @@ -149,8 +150,21 @@ def infer_version(dist: setuptools.Distribution) -> None: if dist_name == "setuptools-scm": return + # Check if setuptools-scm is configured before proceeding try: - config = _config.Configuration.from_file(dist_name=dist_name) + from .pyproject_reading import read_pyproject + + pyproject_data = read_pyproject(Path("pyproject.toml"), missing_section_ok=True) + # Only proceed if setuptools-scm is either in build_requires or has a tool section + if not pyproject_data.is_required and not pyproject_data.section_present: + return # No setuptools-scm configuration, silently return + except (FileNotFoundError, LookupError): + return # No pyproject.toml or other issues, silently return + + try: + config = _config.Configuration.from_file( + dist_name=dist_name, pyproject_data=pyproject_data + ) except LookupError as e: log.info(e, exc_info=True) else: diff --git a/testing/test_integration.py b/testing/test_integration.py index c381998c..e9383a8c 100644 --- a/testing/test_integration.py +++ b/testing/test_integration.py @@ -923,6 +923,125 @@ def test_integration_function_call_order( ) +def test_infer_version_with_build_requires_no_tool_section( + wd: WorkDir, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test that infer_version works when setuptools-scm is in build_requires but no [tool.setuptools_scm] section""" + if sys.version_info < (3, 11): + pytest.importorskip("tomli") + + # Set up a git repository with a tag + wd.commit_testfile("test") + wd("git tag 1.0.0") + monkeypatch.chdir(wd.cwd) + + # Create a pyproject.toml file with setuptools_scm in build-system.requires but NO [tool.setuptools_scm] section + pyproject_content = """ +[build-system] +requires = ["setuptools>=80", "setuptools_scm>=8"] +build-backend = "setuptools.build_meta" + +[project] +name = "test-package-infer-version" +dynamic = ["version"] +""" + wd.write("pyproject.toml", pyproject_content) + + import setuptools + + from setuptools_scm._integration.setuptools import infer_version + + # Create distribution + dist = setuptools.Distribution({"name": "test-package-infer-version"}) + + # Call infer_version - this should work because setuptools_scm is in build-system.requires + infer_version(dist) + + # Verify that version was set + assert dist.metadata.version is not None + assert dist.metadata.version == "1.0.0" + + # Verify that the marker was set + assert getattr(dist, "_setuptools_scm_version_set_by_infer", False) is True + + +def test_infer_version_with_build_requires_dash_variant_no_tool_section( + wd: WorkDir, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test that infer_version works when setuptools-scm (dash variant) is in build_requires but no [tool.setuptools_scm] section""" + if sys.version_info < (3, 11): + pytest.importorskip("tomli") + + # Set up a git repository with a tag + wd.commit_testfile("test") + wd("git tag 1.0.0") + monkeypatch.chdir(wd.cwd) + + # Create a pyproject.toml file with setuptools-scm (dash variant) in build-system.requires but NO [tool.setuptools_scm] section + pyproject_content = """ +[build-system] +requires = ["setuptools>=80", "setuptools-scm>=8"] +build-backend = "setuptools.build_meta" + +[project] +name = "test-package-infer-version-dash" +dynamic = ["version"] +""" + wd.write("pyproject.toml", pyproject_content) + + import setuptools + + from setuptools_scm._integration.setuptools import infer_version + + # Create distribution + dist = setuptools.Distribution({"name": "test-package-infer-version-dash"}) + + # Call infer_version - this should work because setuptools-scm is in build-system.requires + infer_version(dist) + + # Verify that version was set + assert dist.metadata.version is not None + assert dist.metadata.version == "1.0.0" + + # Verify that the marker was set + assert getattr(dist, "_setuptools_scm_version_set_by_infer", False) is True + + +def test_infer_version_without_build_requires_no_tool_section_silently_returns( + wd: WorkDir, monkeypatch: pytest.MonkeyPatch +) -> None: + """Test that infer_version silently returns when setuptools-scm is NOT in build_requires and no [tool.setuptools_scm] section""" + if sys.version_info < (3, 11): + pytest.importorskip("tomli") + + # Set up a git repository with a tag + wd.commit_testfile("test") + wd("git tag 1.0.0") + monkeypatch.chdir(wd.cwd) + + # Create a pyproject.toml file WITHOUT setuptools_scm in build-system.requires and NO [tool.setuptools_scm] section + pyproject_content = """ +[build-system] +requires = ["setuptools>=80", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "test-package-no-scm" +dynamic = ["version"] +""" + wd.write("pyproject.toml", pyproject_content) + + import setuptools + + from setuptools_scm._integration.setuptools import infer_version + + # Create distribution + dist = setuptools.Distribution({"name": "test-package-no-scm"}) + + infer_version(dist) + assert dist.metadata.version is None + + def test_version_keyword_no_scm_dependency_works( wd: WorkDir, monkeypatch: pytest.MonkeyPatch ) -> None: