Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## v9.0.3

### fixed

- fix 1184: verify version is dynamic if the dependency is used as indicator for enabling

## v9.0.2

### Fixed
Expand Down
20 changes: 19 additions & 1 deletion src/setuptools_scm/_integration/pyproject_reading.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ class PyProjectData(NamedTuple):
def project_name(self) -> str | None:
return self.project.get("name")

def verify_dynamic_version_when_required(self) -> None:
"""Verify that dynamic=['version'] is set when setuptools-scm is used as build dependency indicator."""
if self.is_required and not self.section_present:
# When setuptools-scm is in build-system.requires but no tool section exists,
# we need to verify that dynamic=['version'] is set in the project section
dynamic = self.project.get("dynamic", [])
if "version" not in dynamic:
raise ValueError(
f"{self.path}: setuptools-scm is present in [build-system].requires "
f"but dynamic=['version'] is not set in [project]. "
f"Either add dynamic=['version'] to [project] or add a [tool.{self.tool_name}] section."
)


def has_build_package(
requires: Sequence[str], build_package_names: Sequence[str]
Expand Down Expand Up @@ -74,10 +87,15 @@ def read_pyproject(
section_present = False

project = defn.get("project", {})
return PyProjectData(
pyproject_data = PyProjectData(
path, tool_name, project, section, is_required, section_present
)

# Verify dynamic version when setuptools-scm is used as build dependency indicator
pyproject_data.verify_dynamic_version_when_required()

return pyproject_data


def get_args_for_pyproject(
pyproject: PyProjectData,
Expand Down
4 changes: 4 additions & 0 deletions src/setuptools_scm/_integration/setuptools.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ def infer_version(dist: setuptools.Distribution) -> None:
return # No setuptools-scm configuration, silently return
except (FileNotFoundError, LookupError):
return # No pyproject.toml or other issues, silently return
except ValueError as e:
# Log the error as debug info instead of raising it
log.debug("Configuration issue in pyproject.toml: %s", e)
return # Configuration issue, silently return

try:
config = _config.Configuration.from_file(
Expand Down
135 changes: 134 additions & 1 deletion testing/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ def test_pyproject_missing_setup_hook_works(wd: WorkDir, use_scm_version: str) -
[build-system]
requires=["setuptools", "setuptools_scm"]
build-backend = "setuptools.build_meta"
[tool]
[tool.setuptools_scm]
"""
),
)
Expand Down Expand Up @@ -743,6 +743,7 @@ def test_build_requires_integration_with_config_reading(wd: WorkDir) -> None:

[project]
name = "test-package"
dynamic = ["version"]
"""
),
)
Expand All @@ -763,6 +764,7 @@ def test_build_requires_integration_with_config_reading(wd: WorkDir) -> None:

[project]
name = "test-package"
dynamic = ["version"]
"""
),
)
Expand Down Expand Up @@ -1072,3 +1074,134 @@ def test_version_keyword_no_scm_dependency_works(

version_keyword(dist, "use_scm_version", True)
assert dist.metadata.version == "1.0.0"


def test_verify_dynamic_version_when_required_missing_dynamic(
wd: WorkDir, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Test that verification fails when setuptools-scm is in build-system.requires but dynamic=['version'] is missing"""
if sys.version_info < (3, 11):
pytest.importorskip("tomli")

# Change to the test directory
monkeypatch.chdir(wd.cwd)

# Create a pyproject.toml file with setuptools-scm in build-system.requires but NO dynamic=['version']
pyproject_content = """
[build-system]
requires = ["setuptools>=80", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "test-package-missing-dynamic"
# Missing: dynamic = ["version"]
"""
wd.write("pyproject.toml", pyproject_content)

from setuptools_scm._integration.pyproject_reading import read_pyproject

# This should raise a ValueError because dynamic=['version'] is missing
with pytest.raises(
ValueError, match="dynamic=\\['version'\\] is not set in \\[project\\]"
):
read_pyproject(Path("pyproject.toml"), missing_section_ok=True)


def test_verify_dynamic_version_when_required_with_tool_section(
wd: WorkDir, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Test that verification passes when setuptools-scm is in build-system.requires and [tool.setuptools_scm] section exists"""
if sys.version_info < (3, 11):
pytest.importorskip("tomli")

# Change to the test directory
monkeypatch.chdir(wd.cwd)

# Create a pyproject.toml file with setuptools-scm in build-system.requires and [tool.setuptools_scm] section
pyproject_content = """
[build-system]
requires = ["setuptools>=80", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "test-package-with-tool-section"
# Missing: dynamic = ["version"]

[tool.setuptools_scm]
"""
wd.write("pyproject.toml", pyproject_content)

from setuptools_scm._integration.pyproject_reading import read_pyproject

# This should not raise an error because [tool.setuptools_scm] section exists
pyproject_data = read_pyproject(Path("pyproject.toml"), missing_section_ok=True)
assert pyproject_data.is_required is True
assert pyproject_data.section_present is True


def test_verify_dynamic_version_when_required_with_dynamic(
wd: WorkDir, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Test that verification passes when setuptools-scm is in build-system.requires and dynamic=['version'] is set"""
if sys.version_info < (3, 11):
pytest.importorskip("tomli")

# Change to the test directory
monkeypatch.chdir(wd.cwd)

# Create a pyproject.toml file with setuptools-scm in build-system.requires and dynamic=['version']
pyproject_content = """
[build-system]
requires = ["setuptools>=80", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "test-package-with-dynamic"
dynamic = ["version"]
"""
wd.write("pyproject.toml", pyproject_content)

from setuptools_scm._integration.pyproject_reading import read_pyproject

# This should not raise an error because dynamic=['version'] is set
pyproject_data = read_pyproject(Path("pyproject.toml"), missing_section_ok=True)
assert pyproject_data.is_required is True
assert pyproject_data.section_present is False


def test_infer_version_logs_debug_when_missing_dynamic_version(
wd: WorkDir, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Test that infer_version logs debug info when setuptools-scm is in build-system.requires but dynamic=['version'] is missing"""
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 dynamic=['version']
pyproject_content = """
[build-system]
requires = ["setuptools>=80", "setuptools-scm>=8"]
build-backend = "setuptools.build_meta"

[project]
name = "test-package-missing-dynamic"
# Missing: 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-missing-dynamic"})

# This should not raise an error, but should log debug info about the configuration issue
infer_version(dist)

# Verify that version was not set due to configuration issue
assert dist.metadata.version is None
Loading