Skip to content

Commit de701c0

Browse files
revise version inference logic
- drop magic boolean parameters - drop unused exception creator - simplify calls
1 parent 5e8feec commit de701c0

File tree

4 files changed

+43
-75
lines changed

4 files changed

+43
-75
lines changed

src/setuptools_scm/_integration/setuptools.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ def version_keyword(
8989

9090
was_set_by_infer = getattr(dist, "_setuptools_scm_version_set_by_infer", False)
9191

92+
# Exit early if overrides is empty dict AND version was set by infer
93+
if overrides == {} and was_set_by_infer:
94+
return
95+
9296
# Get pyproject data (support direct injection for tests)
9397
try:
9498
pyproject_data = read_pyproject(_given_result=_given_pyproject_data)
@@ -99,12 +103,18 @@ def version_keyword(
99103
log.debug("Configuration issue in pyproject.toml: %s", e)
100104
return
101105

106+
# Pass None as current_version if overrides is truthy AND version was set by infer
107+
current_version = (
108+
None
109+
if (overrides and was_set_by_infer)
110+
else (legacy_data.version or pyproject_data.project_version)
111+
)
112+
102113
result = _get_version_inference_config(
103114
dist_name=dist_name,
104-
current_version=legacy_data.version or pyproject_data.project_version,
115+
current_version=current_version,
105116
pyproject_data=pyproject_data,
106117
overrides=overrides,
107-
was_set_by_infer=was_set_by_infer,
108118
)
109119

110120
result.apply(dist)

src/setuptools_scm/_integration/version_inference.py

Lines changed: 12 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,6 @@ def apply(self, dist: Any) -> None:
2727
from .._get_version_impl import _get_version
2828
from .._get_version_impl import _version_missing
2929

30-
# Clear version if it was set by infer_version (overrides is None means infer_version context)
31-
# OR if we have overrides (version_keyword context) and the version was set by infer_version
32-
was_set_by_infer = getattr(dist, "_setuptools_scm_version_set_by_infer", False)
33-
if was_set_by_infer and (self.overrides is None or self.overrides):
34-
dist._setuptools_scm_version_set_by_infer = False
35-
dist.metadata.version = None
36-
3730
config = _config_module.Configuration.from_file(
3831
dist_name=self.dist_name,
3932
pyproject_data=self.pyproject_data,
@@ -45,7 +38,6 @@ def apply(self, dist: Any) -> None:
4538
if maybe_version is None:
4639
_version_missing(config)
4740
else:
48-
assert dist.metadata.version is None
4941
dist.metadata.version = maybe_version
5042

5143
# Mark that this version was set by infer_version if overrides is None (infer_version context)
@@ -68,17 +60,6 @@ def apply(self, dist: Any) -> None:
6860
warnings.warn(self.message)
6961

7062

71-
@dataclass
72-
class VersionInferenceException:
73-
"""Exception that should be raised."""
74-
75-
exception: Exception
76-
77-
def apply(self, dist: Any) -> None:
78-
"""Apply exception handling to the distribution."""
79-
raise self.exception
80-
81-
8263
class VersionInferenceNoOp:
8364
"""No operation result - silent skip."""
8465

@@ -89,7 +70,6 @@ def apply(self, dist: Any) -> None:
8970
VersionInferenceResult = Union[
9071
VersionInferenceConfig, # Proceed with inference
9172
VersionInferenceError, # Show error/warning
92-
VersionInferenceException, # Raise exception
9373
VersionInferenceNoOp, # Don't infer (silent)
9474
]
9575

@@ -99,7 +79,6 @@ def get_version_inference_config(
9979
current_version: str | None,
10080
pyproject_data: PyProjectData,
10181
overrides: dict[str, Any] | None = None,
102-
was_set_by_infer: bool = False,
10382
) -> VersionInferenceResult:
10483
"""
10584
Determine whether and how to perform version inference.
@@ -109,7 +88,6 @@ def get_version_inference_config(
10988
current_version: Current version if any
11089
pyproject_data: PyProjectData from parser (None if file doesn't exist)
11190
overrides: Override configuration (None for no overrides)
112-
was_set_by_infer: Whether current version was set by infer_version
11391
11492
Returns:
11593
VersionInferenceResult with the decision and configuration
@@ -118,41 +96,33 @@ def get_version_inference_config(
11896
if dist_name is None:
11997
dist_name = pyproject_data.project_name
12098

121-
# If a version is already present, decide based on context (infer_version vs version_keyword)
99+
# Never infer a version for setuptools-scm itself
100+
if dist_name == "setuptools-scm":
101+
return VersionInferenceNoOp()
102+
103+
# If a version already exists, short-circuit by context
122104
if current_version is not None:
123-
# infer_version call (overrides is None) should be a no-op if version already exists
124105
if overrides is None:
106+
# infer_version called and a version is already present → do nothing
125107
return VersionInferenceNoOp()
126-
127-
if not was_set_by_infer:
108+
else:
109+
# version_keyword context - always warn if version already set
128110
return VersionInferenceError(
129111
f"version of {dist_name} already set",
130112
should_warn=pyproject_data.should_infer(),
131113
)
132114

133-
# Version was set by infer_version previously
134-
if overrides:
135-
# Non-empty overrides from version_keyword → re-infer with overrides
136-
return VersionInferenceConfig(
137-
dist_name=dist_name, pyproject_data=pyproject_data, overrides=overrides
138-
)
139-
# Empty overrides dict from version_keyword → keep existing version
140-
return VersionInferenceNoOp()
141-
142-
# Do not infer a version for setuptools-scm itself
143-
if dist_name == "setuptools-scm":
144-
return VersionInferenceNoOp()
145-
146-
# version_keyword path: any overrides (empty or not) mean we should infer
115+
# No version present yet
147116
if overrides is not None:
117+
# version_keyword path: any overrides (empty or not) mean we should infer
148118
return VersionInferenceConfig(
149119
dist_name=dist_name, pyproject_data=pyproject_data, overrides=overrides
150120
)
151121

152-
# infer_version path: only infer when [tool.setuptools_scm] section is present
122+
# infer_version path: only infer when [tool.setuptools_scm] is present
153123
if pyproject_data.should_infer():
154124
return VersionInferenceConfig(
155-
dist_name=dist_name, pyproject_data=pyproject_data, overrides=None
125+
dist_name=dist_name, pyproject_data=pyproject_data, overrides=overrides
156126
)
157127

158128
return VersionInferenceNoOp()

src/setuptools_scm/_types.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,5 @@ def __call__(
5555
current_version: str | None,
5656
pyproject_data: PyProjectData,
5757
overrides: dict[str, object] | None = None,
58-
was_set_by_infer: bool = False,
5958
) -> VersionInferenceApplicable: # pragma: no cover - structural type
6059
...

testing/test_version_inference.py

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,47 @@
33
from setuptools_scm._integration.pyproject_reading import PyProjectData
44
from setuptools_scm._integration.version_inference import VersionInferenceConfig
55
from setuptools_scm._integration.version_inference import VersionInferenceError
6-
from setuptools_scm._integration.version_inference import VersionInferenceException
76
from setuptools_scm._integration.version_inference import VersionInferenceNoOp
87
from setuptools_scm._integration.version_inference import get_version_inference_config
98

109

1110
class TestVersionInferenceDecision:
1211
"""Test the version inference decision logic."""
1312

14-
def test_version_already_set_by_infer_with_overrides(self) -> None:
15-
"""Test that we proceed when version was set by infer_version but overrides provided."""
13+
def test_version_already_set_with_overrides(self) -> None:
14+
"""Test that we get an error when version is already set and overrides provided."""
1615
result = get_version_inference_config(
1716
dist_name="test_package",
1817
current_version="1.0.0",
1918
pyproject_data=PyProjectData.for_testing(True, True, True),
2019
overrides={"key": "value"},
21-
was_set_by_infer=True,
2220
)
2321

24-
assert isinstance(result, VersionInferenceConfig)
25-
assert result.dist_name == "test_package"
26-
assert result.overrides == {"key": "value"}
22+
assert isinstance(result, VersionInferenceError)
23+
assert "version of test_package already set" in result.message
2724

28-
def test_version_already_set_by_infer_no_overrides(self) -> None:
25+
def test_version_already_set_no_overrides(self) -> None:
2926
"""infer_version call with existing version should be a no-op."""
3027
result = get_version_inference_config(
3128
dist_name="test_package",
3229
current_version="1.0.0",
3330
pyproject_data=PyProjectData.for_testing(True, True, True),
3431
overrides=None,
35-
was_set_by_infer=True,
3632
)
3733

3834
assert isinstance(result, VersionInferenceNoOp)
3935

40-
def test_version_already_set_by_infer_empty_overrides(self) -> None:
41-
"""Test that we don't re-infer when version was set by infer_version with empty overrides (version_keyword call)."""
36+
def test_version_already_set_empty_overrides(self) -> None:
37+
"""Test that we get an error when version is already set with empty overrides (version_keyword call)."""
4238
result = get_version_inference_config(
4339
dist_name="test_package",
4440
current_version="1.0.0",
4541
pyproject_data=PyProjectData.for_testing(True, True, True),
4642
overrides={},
47-
was_set_by_infer=True,
4843
)
4944

50-
assert isinstance(result, VersionInferenceNoOp)
45+
assert isinstance(result, VersionInferenceError)
46+
assert "version of test_package already set" in result.message
5147

5248
def test_version_already_set_by_something_else(self) -> None:
5349
"""infer_version call with existing version set by something else should be a no-op."""
@@ -56,7 +52,6 @@ def test_version_already_set_by_something_else(self) -> None:
5652
current_version="1.0.0",
5753
pyproject_data=PyProjectData.for_testing(True, True, True),
5854
overrides=None,
59-
was_set_by_infer=False,
6055
)
6156

6257
assert isinstance(result, VersionInferenceNoOp)
@@ -83,7 +78,8 @@ def test_no_setuptools_scm_config_infer_version(self) -> None:
8378
dist_name="test_package",
8479
current_version=None,
8580
pyproject_data=PyProjectData.for_testing(False, False, True),
86-
overrides=None, # infer_version call
81+
overrides=None,
82+
# infer_version call
8783
)
8884

8985
assert isinstance(result, VersionInferenceNoOp)
@@ -94,7 +90,8 @@ def test_no_setuptools_scm_config_version_keyword(self) -> None:
9490
dist_name="test_package",
9591
current_version=None,
9692
pyproject_data=PyProjectData.for_testing(False, False, True),
97-
overrides={}, # version_keyword call with use_scm_version=True
93+
overrides={},
94+
# version_keyword call with use_scm_version=True
9895
)
9996

10097
assert isinstance(result, VersionInferenceConfig)
@@ -107,7 +104,8 @@ def test_setuptools_scm_required_no_project_section_infer_version(self) -> None:
107104
dist_name="test_package",
108105
current_version=None,
109106
pyproject_data=PyProjectData.for_testing(True, False, False),
110-
overrides=None, # infer_version call
107+
overrides=None,
108+
# infer_version call
111109
)
112110

113111
assert isinstance(result, VersionInferenceNoOp)
@@ -118,7 +116,8 @@ def test_setuptools_scm_required_no_project_section_version_keyword(self) -> Non
118116
dist_name="test_package",
119117
current_version=None,
120118
pyproject_data=PyProjectData.for_testing(True, False, False),
121-
overrides={}, # version_keyword call with use_scm_version=True
119+
overrides={},
120+
# version_keyword call with use_scm_version=True
122121
)
123122

124123
assert isinstance(result, VersionInferenceConfig)
@@ -134,7 +133,8 @@ def test_setuptools_scm_required_no_project_section_version_keyword_with_config(
134133
dist_name="test_package",
135134
current_version=None,
136135
pyproject_data=PyProjectData.for_testing(True, False, False),
137-
overrides=overrides, # version_keyword call with use_scm_version={config}
136+
overrides=overrides,
137+
# version_keyword call with use_scm_version={config}
138138
)
139139

140140
assert isinstance(result, VersionInferenceConfig)
@@ -191,7 +191,6 @@ def test_version_already_set_none_dist_name(self) -> None:
191191
current_version="1.0.0",
192192
pyproject_data=PyProjectData.for_testing(True, True, True),
193193
overrides=None,
194-
was_set_by_infer=False,
195194
)
196195

197196
assert isinstance(result, VersionInferenceNoOp)
@@ -260,13 +259,3 @@ def test_error_default_warn(self) -> None:
260259
"""Test VersionInferenceError default should_warn value."""
261260
error = VersionInferenceError("test message")
262261
assert error.should_warn is False
263-
264-
265-
class TestVersionInferenceException:
266-
"""Test the VersionInferenceException dataclass."""
267-
268-
def test_exception_creation(self) -> None:
269-
"""Test creating VersionInferenceException instances."""
270-
original_exception = ValueError("test error")
271-
wrapper = VersionInferenceException(original_exception)
272-
assert wrapper.exception == original_exception

0 commit comments

Comments
 (0)