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
17 changes: 17 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,23 @@ jobs:
- name: Publish package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

upload-release-assets:
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'published'
needs: [test]
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
name: Packages
path: dist
- name: Upload release assets
uses: softprops/action-gh-release@v2
with:
files: dist/*
fail_on_unmatched_files: true

test-pypi-upload:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## v9.1.1

### fixed

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


## v9.0.3

### fixed
Expand Down
8 changes: 7 additions & 1 deletion src/setuptools_scm/_integration/setuptools.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ def _warn_on_old_setuptools(_version: str = setuptools.__version__) -> None:


def _log_hookstart(hook: str, dist: setuptools.Distribution) -> None:
log.debug("%s %s %s %r", hook, id(dist), id(dist.metadata), vars(dist.metadata))
log.debug(
"%s %s %s %r",
hook,
id(dist),
id(dist.metadata),
{**vars(dist.metadata), "long_description": ...},
)


def get_keyword_overrides(
Expand Down
5 changes: 4 additions & 1 deletion src/setuptools_scm/_integration/version_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,23 @@ def get_version_inference_config(

# Handle missing configuration
if not pyproject_data.is_required and not pyproject_data.section_present:
# If there are overrides, proceed with inference (explicit use_scm_version)
# If version_keyword was called (overrides is not None), activate setuptools_scm
# This handles both use_scm_version=True (empty {}) and use_scm_version={config}
if overrides is not None:
return VersionInferenceConfig(
dist_name=dist_name,
pyproject_data=pyproject_data,
overrides=overrides,
)
# If infer_version was called (overrides is None), only activate with config
return VersionInferenceNoOp()

# Handle missing project section when required
if (
pyproject_data.is_required
and not pyproject_data.section_present
and not pyproject_data.project_present
and overrides is None # Only return NoOp for infer_version, not version_keyword
):
return VersionInferenceNoOp()

Expand Down
35 changes: 35 additions & 0 deletions testing/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1305,3 +1305,38 @@ def test_infer_version_logs_debug_when_missing_dynamic_version(

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


@pytest.mark.issue("xmlsec-regression")
def test_xmlsec_download_regression(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Test that pip download works for xmlsec package without causing setuptools_scm regression.

This test ensures that downloading and building xmlsec from source doesn't fail
due to setuptools_scm issues when using --no-build-isolation.
"""
# Set up environment with setuptools_scm debug enabled
monkeypatch.setenv("SETUPTOOLS_SCM_DEBUG", "1")
monkeypatch.setenv("COLUMNS", "150")

# Run pip download command with no-binary and no-build-isolation
try:
subprocess.run(
[
*(sys.executable, "-m", "pip", "download"),
*("--no-binary", "xmlsec"),
"--no-build-isolation",
"-v",
"xmlsec==1.3.16",
],
cwd=tmp_path,
text=True,
timeout=300,
check=True,
)
except subprocess.CalledProcessError as e:
pytest.fail(f"pip download failed: {e}", pytrace=False)

# The success of the subprocess.run call above means the regression is fixed.
# pip download succeeded without setuptools_scm causing version conflicts.
52 changes: 48 additions & 4 deletions testing/test_version_inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,26 +81,70 @@ def test_no_pyproject_toml(self) -> None:
# and not call get_version_inference_config at all.
# This test is no longer needed as pyproject_data is always required.

def test_no_setuptools_scm_config(self) -> None:
"""Test that we don't infer when setuptools-scm is not configured."""
def test_no_setuptools_scm_config_infer_version(self) -> None:
"""Test that we don't infer when setuptools-scm is not configured and infer_version called."""
result = get_version_inference_config(
dist_name="test_package",
current_version=None,
pyproject_data=PyProjectData.for_testing(False, False, True),
overrides=None, # infer_version call
)

assert isinstance(result, VersionInferenceNoOp)

def test_setuptools_scm_required_no_project_section(self) -> None:
"""Test that we don't infer when setuptools-scm is required but no project section."""
def test_no_setuptools_scm_config_version_keyword(self) -> None:
"""Test that we DO infer when setuptools-scm is not configured but use_scm_version=True."""
result = get_version_inference_config(
dist_name="test_package",
current_version=None,
pyproject_data=PyProjectData.for_testing(False, False, True),
overrides={}, # version_keyword call with use_scm_version=True
)

assert isinstance(result, VersionInferenceConfig)
assert result.dist_name == "test_package"
assert result.overrides == {}

def test_setuptools_scm_required_no_project_section_infer_version(self) -> None:
"""Test that we don't infer when setuptools-scm is required but no project section and infer_version called."""
result = get_version_inference_config(
dist_name="test_package",
current_version=None,
pyproject_data=PyProjectData.for_testing(True, False, False),
overrides=None, # infer_version call
)

assert isinstance(result, VersionInferenceNoOp)

def test_setuptools_scm_required_no_project_section_version_keyword(self) -> None:
"""Test that we DO infer when setuptools-scm is required but no project section and use_scm_version=True."""
result = get_version_inference_config(
dist_name="test_package",
current_version=None,
pyproject_data=PyProjectData.for_testing(True, False, False),
overrides={}, # version_keyword call with use_scm_version=True
)

assert isinstance(result, VersionInferenceConfig)
assert result.dist_name == "test_package"
assert result.overrides == {}

def test_setuptools_scm_required_no_project_section_version_keyword_with_config(
self,
) -> None:
"""Test that we DO infer when setuptools-scm is required but no project section and use_scm_version={config}."""
overrides = {"version_scheme": "calver"}
result = get_version_inference_config(
dist_name="test_package",
current_version=None,
pyproject_data=PyProjectData.for_testing(True, False, False),
overrides=overrides, # version_keyword call with use_scm_version={config}
)

assert isinstance(result, VersionInferenceConfig)
assert result.dist_name == "test_package"
assert result.overrides == overrides

def test_setuptools_scm_required_with_project_section(self) -> None:
"""Test that we infer when setuptools-scm is required and project section exists."""
result = get_version_inference_config(
Expand Down
Loading