Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
27e3b7a
docs: Remove duplicate changelog entry for v9.1.1.
seifertm Aug 17, 2025
829f25a
docs: Mark v9.1.0 and v9.1.1 as yanked in the changelog.
seifertm Aug 17, 2025
0207ee9
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 17, 2025
a0920fd
Merge pull request #1209 from seifertm/mark-recently-yanked-versions-…
RonnyPfannschmidt Aug 17, 2025
1237bdf
[pre-commit.ci] pre-commit autoupdate
pre-commit-ci[bot] Sep 8, 2025
0718f7c
Bump actions/setup-python from 5 to 6
dependabot[bot] Sep 8, 2025
dafb1c3
Bump actions/github-script from 7 to 8
dependabot[bot] Sep 8, 2025
c22ad0c
Merge pull request #1221 from pypa/dependabot/github_actions/actions/…
RonnyPfannschmidt Sep 8, 2025
e4ba2e7
Merge pull request #1214 from pypa/pre-commit-ci-update-config
RonnyPfannschmidt Sep 12, 2025
d3ee9a7
Merge pull request #1220 from pypa/dependabot/github_actions/actions/…
RonnyPfannschmidt Sep 12, 2025
6398650
[pre-commit.ci] pre-commit autoupdate
pre-commit-ci[bot] Sep 15, 2025
bfd87c5
Merge pull request #1224 from pypa/pre-commit-ci-update-config
RonnyPfannschmidt Sep 18, 2025
6a9de12
ignore dynamic version in setup.cfg
RonnyPfannschmidt Aug 19, 2025
e922ebe
test instrumentation: add dependency injected toml content to read_py…
RonnyPfannschmidt Aug 19, 2025
34b72e1
add a warning when setuptools dynamic version in pyproject is used in…
RonnyPfannschmidt Aug 19, 2025
ec99afe
use a actually user friendly deprecation for the setuptools dynamic a…
RonnyPfannschmidt Aug 24, 2025
343f791
Improve read_pyproject docstring with reStructuredText format
RonnyPfannschmidt Oct 12, 2025
3969982
Fix typos and grammar in deprecation warnings
RonnyPfannschmidt Oct 12, 2025
536fe21
Fix duplicate v9.2.0 changelog entry and improve consistency
RonnyPfannschmidt Oct 12, 2025
8c5cec9
Fix API stability check workflow to install griffe and improve reporting
RonnyPfannschmidt Oct 12, 2025
14d85c0
Install Mercurial on Windows runners via Chocolatey
RonnyPfannschmidt Oct 12, 2025
70f6942
Merge pull request #1219 from RonnyPfannschmidt/fix-1216-explicitly-d…
RonnyPfannschmidt Oct 12, 2025
20a4464
[pre-commit.ci] pre-commit autoupdate
pre-commit-ci[bot] Oct 6, 2025
ad83282
Merge pull request #1225 from pypa/pre-commit-ci-update-config
RonnyPfannschmidt Oct 12, 2025
a893634
Prepare release v9.2.1
RonnyPfannschmidt Oct 12, 2025
338f562
Merge pull request #1226 from RonnyPfannschmidt/prepare-release
RonnyPfannschmidt Oct 12, 2025
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
40 changes: 33 additions & 7 deletions .github/workflows/api-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,30 +24,56 @@ jobs:
fetch-depth: 0

- name: Setup Python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
with:
python-version: '3.11'

- name: Install dependencies
run: |
pip install -U pip setuptools
pip install -e .[test]
pip install griffe

- name: Run griffe API check
id: griffe-check
continue-on-error: true
run: |
echo "Running griffe API stability check..."
if griffe check setuptools_scm -ssrc -f github; then
echo "api_check_result=success" >> $GITHUB_OUTPUT
echo "exit_code=0" >> $GITHUB_OUTPUT
else
exit_code=$?
echo "api_check_result=warning" >> $GITHUB_OUTPUT
echo "API stability check detected changes but will not fail the build" >> $GITHUB_STEP_SUMMARY
echo "exit_code=$exit_code" >> $GITHUB_OUTPUT
exit $exit_code
fi

- name: Report API check result
if: steps.griffe-check.outputs.api_check_result == 'warning'
uses: actions/github-script@v7
if: always()
uses: actions/github-script@v8
with:
script: |
core.warning('API stability check detected breaking changes. Please review the API changes above.')
core.summary.addRaw('⚠️ API Stability Warning: Breaking changes detected in the public API')
await core.summary.write()
const result = '${{ steps.griffe-check.outputs.api_check_result }}'
const exitCode = '${{ steps.griffe-check.outputs.exit_code }}'

if (result === 'success') {
core.notice('API stability check passed - no breaking changes detected')
await core.summary
.addHeading('✅ API Stability Check: Passed', 2)
.addRaw('No breaking changes detected in the public API')
.write()
} else if (result === 'warning') {
core.warning(`API stability check detected breaking changes (exit code: ${exitCode}). Please review the API changes above.`)
await core.summary
.addHeading('⚠️ API Stability Warning', 2)
.addRaw('Breaking changes detected in the public API. Please review the changes reported above.')
.addRaw(`\n\nExit code: ${exitCode}`)
.write()
} else {
core.error('API stability check failed to run properly')
await core.summary
.addHeading('❌ API Stability Check: Failed', 2)
.addRaw('The griffe check failed to execute. This may indicate griffe is not installed or there was an error.')
.write()
}
10 changes: 7 additions & 3 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
with:
fetch-depth: 0
- name: Setup python
uses: actions/setup-python@v5
uses: actions/setup-python@v6
if: matrix.python_version != 'msys2'
with:
python-version: ${{ matrix.python_version }}
Expand All @@ -68,7 +68,7 @@ jobs:
msystem: MINGW64
install: git mingw-w64-x86_64-python mingw-w64-x86_64-python-setuptools
update: true
- name: Setup GnuPG
- name: Setup GnuPG and Mercurial on Windows
# At present, the Windows VMs only come with the copy of GnuPG that's bundled
# with Git for Windows. If we want to use this version _and_ be able to set
# arbitrary GnuPG home directories, then the test would need to figure out when
Expand All @@ -84,11 +84,15 @@ jobs:
# Additionally, we'll explicitly set `gpg.program` to ensure Git for Windows
# doesn't invoke the bundled GnuPG, otherwise we'll run into
# <https://dev.gnupg.org/T5504>. See also: <https://dev.gnupg.org/T3020>.
#
# Windows runners no longer ship with Mercurial pre-installed, so we install
# it via Chocolatey using the 'hg' package.
run: |
$env:PATH = "C:\Program Files\Git\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\ProgramData\Chocolatey\bin"
[Environment]::SetEnvironmentVariable("Path", $env:PATH, "Machine")
choco install gnupg -y --no-progress
choco install gnupg hg -y --no-progress
echo "C:\Program Files (x86)\gnupg\bin" >> $env:GITHUB_PATH
echo "C:\Program Files\Mercurial\" >> $env:GITHUB_PATH
git config --system gpg.program "C:\Program Files (x86)\gnupg\bin\gpg.exe"
if: runner.os == 'Windows'
- run: uv sync --group test --group docs --extra rich
Expand Down
7 changes: 3 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,17 @@ repos:
- id: debug-statements

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.8
rev: v0.13.3
hooks:
- id: ruff-check
args: [--fix, --exit-non-zero-on-fix, --show-fixes]
- id: ruff-format

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.17.1
rev: v1.18.2
hooks:
- id: mypy
args: [--strict]
language_version: "3.10"
additional_dependencies:
- types-setuptools
- tokenize-rt==3.2.0
Expand All @@ -28,7 +27,7 @@ repos:
- rich

- repo: https://github.com/scientific-python/cookie
rev: 2025.05.02
rev: 2025.10.01
hooks:
- id: sp-repo-review

Expand Down
34 changes: 18 additions & 16 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog


## v9.2.1

### Fixed

- fix #1216: accept and create a warning for usages of `version = attr:` in setuptools config.
unfortunately dozens of projects cargo-culted that antipattern


## v9.2.0

### Added
Expand All @@ -11,31 +20,24 @@
version inference is automatically enabled with default settings.


### removed
### Removed

- unchecked simplified activation - too many projects use setups where it would fail

### changed

- refine activation logic and add unittest for the relevant cases instead of trying to speedrun setuptools

## v9.1.1

### fixed

- fix #1194: correctly handle version keyword when pyproject metadata is missing
### Changed

- refine activation logic and add unittest for the relevant cases instead of trying to speedrun setuptools

## v9.1.1
## v9.1.1 (yanked)

### fixed
### Fixed

- fix #1194: correctly handle version keyword when pyproject metadata is missing


## v9.1.0
## v9.1.0 (yanked)

### fixed
### Fixed

- complete reiteration of the decision logic for enabling version inference on setuptools_scm

Expand All @@ -47,9 +49,9 @@

## v9.0.3 (yanked)

### fixed
### Fixed

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

## v9.0.2 (yanked)

Expand Down
1 change: 0 additions & 1 deletion src/setuptools_scm/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,6 @@ def from_data(
# Handle nested SCM configuration

scm_config = ScmConfiguration.from_data(scm_data)

return cls(
relative_to=relative_to,
version_cls=version_cls,
Expand Down
20 changes: 20 additions & 0 deletions src/setuptools_scm/_integration/deprecation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import warnings

from pathlib import Path


def warn_dynamic_version(path: Path, section: str, expression: str) -> None:
warnings.warn(
f"{path}: at [{section}]\n"
f"{expression} is forcing setuptools to override the version setuptools-scm did already set\n"
"When using setuptools-scm it's invalid to use setuptools dynamic version as well, please remove it.\n"
"Setuptools-scm is responsible for setting the version, forcing setuptools to override creates errors."
)


def warn_pyproject_setuptools_dynamic_version(path: Path) -> None:
warn_dynamic_version(path, "tool.setuptools.dynamic", "version = {attr = ...}")


def warn_setup_cfg_dynamic_version(path: Path) -> None:
warn_dynamic_version(path, "metadata", "version = attr: ...")
40 changes: 29 additions & 11 deletions src/setuptools_scm/_integration/pyproject_reading.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,19 +174,23 @@ def read_pyproject(
tool_name: str = DEFAULT_TOOL_NAME,
canonical_build_package_name: str = "setuptools-scm",
_given_result: _t.GivenPyProjectResult = None,
_given_definition: TOML_RESULT | None = None,
) -> PyProjectData:
"""Read and parse pyproject configuration.

This function supports dependency injection for tests via `_given_result`.

Parameters:
- path: Path to the pyproject file
- tool_name: The tool section name (default: `setuptools_scm`)
- canonical_build_package_name: Normalized build requirement name
- _given_result: Optional testing hook. Can be:
- PyProjectData: returned directly
- InvalidTomlError | FileNotFoundError: raised directly
- None: read from filesystem
This function supports dependency injection for tests via ``_given_result``
and ``_given_definition``.

:param path: Path to the pyproject file
:param tool_name: The tool section name (default: ``setuptools_scm``)
:param canonical_build_package_name: Normalized build requirement name
:param _given_result: Optional testing hook. Can be:
- ``PyProjectData``: returned directly
- ``InvalidTomlError`` | ``FileNotFoundError``: raised directly
- ``None``: read from filesystem (default)
:param _given_definition: Optional testing hook to provide parsed TOML content.
When provided, this dictionary is used instead of reading and parsing
the file from disk. Ignored if ``_given_result`` is provided.
"""

if _given_result is not None:
Expand All @@ -195,7 +199,10 @@ def read_pyproject(
if isinstance(_given_result, (InvalidTomlError, FileNotFoundError)):
raise _given_result

defn = read_toml_content(path)
if _given_definition is not None:
defn = _given_definition
else:
defn = read_toml_content(path)

requires: list[str] = defn.get("build-system", {}).get("requires", [])
is_required = has_build_package(requires, canonical_build_package_name)
Expand Down Expand Up @@ -224,6 +231,17 @@ def read_pyproject(
requires,
)

setuptools_dynamic_version = (
defn.get("tool", {})
.get("setuptools", {})
.get("dynamic", {})
.get("version", None)
)
if setuptools_dynamic_version is not None:
from .deprecation import warn_pyproject_setuptools_dynamic_version

warn_pyproject_setuptools_dynamic_version(path)

return pyproject_data


Expand Down
5 changes: 5 additions & 0 deletions src/setuptools_scm/_integration/setup_cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ def read_setup_cfg(input: str | os.PathLike[str] = "setup.cfg") -> SetuptoolsBas

name = parser.get("metadata", "name", fallback=None)
version = parser.get("metadata", "version", fallback=None)
if version is not None and "attr" in version:
from .deprecation import warn_setup_cfg_dynamic_version

warn_setup_cfg_dynamic_version(path)
version = None
return SetuptoolsBasicData(path=path, name=name, version=version)


Expand Down
24 changes: 24 additions & 0 deletions testing/test_deprecation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Test deprecation warnings and their exact text."""

from pathlib import Path

import pytest

from setuptools_scm._integration.deprecation import warn_dynamic_version


def test_warn_dynamic_version_full_text() -> None:
"""Test the complete warning text for warn_dynamic_version function."""
test_path = Path("test_file.toml")
expected_warning = (
f"{test_path}: at [test.section]\n"
"test_expression is forcing setuptools to override the version setuptools-scm did already set\n"
"When using setuptools-scm it's invalid to use setuptools dynamic version as well, please remove it.\n"
"Setuptools-scm is responsible for setting the version, forcing setuptools to override creates errors."
)

with pytest.warns(UserWarning) as warning_info: # noqa: PT030
warn_dynamic_version(test_path, "test.section", "test_expression")

assert len(warning_info) == 1
assert str(warning_info[0].message) == expected_warning
24 changes: 24 additions & 0 deletions testing/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from setuptools_scm._integration import setuptools as setuptools_integration
from setuptools_scm._integration.pyproject_reading import PyProjectData
from setuptools_scm._integration.setup_cfg import SetuptoolsBasicData
from setuptools_scm._integration.setup_cfg import read_setup_cfg
from setuptools_scm._requirement_cls import extract_package_name

if TYPE_CHECKING:
Expand Down Expand Up @@ -457,6 +458,29 @@ def test_unicode_in_setup_cfg(tmp_path: Path) -> None:
assert data.version == "1.2.3"


@pytest.mark.issue(1216)
def test_setup_cfg_dynamic_version_warns_and_ignores(tmp_path: Path) -> None:
cfg = tmp_path / "setup.cfg"
cfg.write_text(
textwrap.dedent(
"""
[metadata]
name = example-broken
version = attr: example_broken.__version__
"""
),
encoding="utf-8",
)

with pytest.warns(
UserWarning,
match=r"setup\.cfg: at \[metadata\]",
):
legacy_data = read_setup_cfg(cfg)

assert legacy_data.version is None


def test_setup_cfg_version_prevents_inference_version_keyword(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
Expand Down
Loading
Loading