Skip to content

Commit 9293e51

Browse files
reiterate version inference logic again
the decission tree is massively simplified
1 parent 8669af0 commit 9293e51

File tree

7 files changed

+170
-238
lines changed

7 files changed

+170
-238
lines changed

src/setuptools_scm/_integration/pyproject_reading.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class PyProjectData:
3535
@classmethod
3636
def for_testing(
3737
cls,
38+
*,
3839
is_required: bool = False,
3940
section_present: bool = False,
4041
project_present: bool = False,

src/setuptools_scm/_integration/setup_cfg.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@ def read_setup_cfg(input: str | os.PathLike[str] = "setup.cfg") -> SetuptoolsBas
2828
return SetuptoolsBasicData(path=path, name=name, version=version)
2929

3030

31-
def extract_from_legacy(dist: setuptools.Distribution) -> SetuptoolsBasicData:
32-
base = read_setup_cfg()
31+
def extract_from_legacy(
32+
dist: setuptools.Distribution,
33+
*,
34+
_given_legacy_data: SetuptoolsBasicData | None = None,
35+
) -> SetuptoolsBasicData:
36+
base = _given_legacy_data if _given_legacy_data is not None else read_setup_cfg()
3337
if base.name is None:
3438
base.name = dist.metadata.name
3539
if base.version is None:

src/setuptools_scm/_integration/setuptools.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .. import _types as _t
1212
from .pyproject_reading import PyProjectData
1313
from .pyproject_reading import read_pyproject
14+
from .setup_cfg import SetuptoolsBasicData
1415
from .setup_cfg import extract_from_legacy
1516
from .toml import InvalidTomlError
1617
from .version_inference import get_version_inference_config
@@ -69,6 +70,7 @@ def version_keyword(
6970
value: bool | dict[str, Any] | Callable[[], dict[str, Any]],
7071
*,
7172
_given_pyproject_data: _t.GivenPyProjectResult = None,
73+
_given_legacy_data: SetuptoolsBasicData | None = None,
7274
_get_version_inference_config: _t.GetVersionInferenceConfig = get_version_inference_config,
7375
) -> None:
7476
"""apply version infernce when setup(use_scm_version=...) is used
@@ -84,7 +86,7 @@ def version_keyword(
8486
"dist_name may not be specified in the setup keyword "
8587
)
8688

87-
legacy_data = extract_from_legacy(dist)
89+
legacy_data = extract_from_legacy(dist, _given_legacy_data=_given_legacy_data)
8890
dist_name: str | None = legacy_data.name
8991

9092
was_set_by_infer = getattr(dist, "_setuptools_scm_version_set_by_infer", False)
@@ -124,6 +126,7 @@ def infer_version(
124126
dist: setuptools.Distribution,
125127
*,
126128
_given_pyproject_data: _t.GivenPyProjectResult = None,
129+
_given_legacy_data: SetuptoolsBasicData | None = None,
127130
_get_version_inference_config: _t.GetVersionInferenceConfig = get_version_inference_config,
128131
) -> None:
129132
"""apply version inference from the finalize_options hook
@@ -135,7 +138,7 @@ def infer_version(
135138

136139
_log_hookstart("infer_version", dist)
137140

138-
legacy_data = extract_from_legacy(dist)
141+
legacy_data = extract_from_legacy(dist, _given_legacy_data=_given_legacy_data)
139142
dist_name = legacy_data.name
140143

141144
try:

src/setuptools_scm/_integration/version_inference.py

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
from typing import Any
66
from typing import Union
77

8+
from setuptools import Distribution
9+
810
from .. import _log
911

1012
if TYPE_CHECKING:
@@ -21,7 +23,7 @@ class VersionInferenceConfig:
2123
pyproject_data: PyProjectData | None
2224
overrides: dict[str, Any] | None
2325

24-
def apply(self, dist: Any) -> None:
26+
def apply(self, dist: Distribution) -> None:
2527
"""Apply version inference to the distribution."""
2628
from .. import _config as _config_module
2729
from .._get_version_impl import _get_version
@@ -42,34 +44,32 @@ def apply(self, dist: Any) -> None:
4244

4345
# Mark that this version was set by infer_version if overrides is None (infer_version context)
4446
if self.overrides is None:
45-
dist._setuptools_scm_version_set_by_infer = True
47+
dist._setuptools_scm_version_set_by_infer = True # type: ignore[attr-defined]
4648

4749

4850
@dataclass
49-
class VersionInferenceError:
51+
class VersionInferenceWarning:
5052
"""Error message for user."""
5153

5254
message: str
53-
should_warn: bool = False
5455

55-
def apply(self, dist: Any) -> None:
56+
def apply(self, dist: Distribution) -> None:
5657
"""Apply error handling to the distribution."""
5758
import warnings
5859

59-
if self.should_warn:
60-
warnings.warn(self.message)
60+
warnings.warn(self.message)
6161

6262

6363
class VersionInferenceNoOp:
6464
"""No operation result - silent skip."""
6565

66-
def apply(self, dist: Any) -> None:
66+
def apply(self, dist: Distribution) -> None:
6767
"""Apply no-op to the distribution."""
6868

6969

7070
VersionInferenceResult = Union[
7171
VersionInferenceConfig, # Proceed with inference
72-
VersionInferenceError, # Show error/warning
72+
VersionInferenceWarning, # Show warning
7373
VersionInferenceNoOp, # Don't infer (silent)
7474
]
7575

@@ -92,37 +92,21 @@ def get_version_inference_config(
9292
Returns:
9393
VersionInferenceResult with the decision and configuration
9494
"""
95-
# Normalize name from project metadata when not provided
96-
if dist_name is None:
97-
dist_name = pyproject_data.project_name
9895

99-
# Never infer a version for setuptools-scm itself
100-
if dist_name == "setuptools-scm":
101-
return VersionInferenceNoOp()
96+
config = VersionInferenceConfig(
97+
dist_name=dist_name,
98+
pyproject_data=pyproject_data,
99+
overrides=overrides,
100+
)
101+
102+
inference_implied = pyproject_data.should_infer() or overrides is not None
102103

103-
# If a version already exists, short-circuit by context
104-
if current_version is not None:
105-
if overrides is None:
106-
# infer_version called and a version is already present → do nothing
107-
return VersionInferenceNoOp()
104+
if inference_implied:
105+
if current_version is None:
106+
return config
108107
else:
109-
# version_keyword context - always warn if version already set
110-
return VersionInferenceError(
108+
return VersionInferenceWarning(
111109
f"version of {dist_name} already set",
112-
should_warn=pyproject_data.should_infer(),
113110
)
114-
115-
# No version present yet
116-
if overrides is not None:
117-
# version_keyword path: any overrides (empty or not) mean we should infer
118-
return VersionInferenceConfig(
119-
dist_name=dist_name, pyproject_data=pyproject_data, overrides=overrides
120-
)
121-
122-
# infer_version path: only infer when [tool.setuptools_scm] is present
123-
if pyproject_data.should_infer():
124-
return VersionInferenceConfig(
125-
dist_name=dist_name, pyproject_data=pyproject_data, overrides=overrides
126-
)
127-
128-
return VersionInferenceNoOp()
111+
else:
112+
return VersionInferenceNoOp()

src/setuptools_scm/_types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from typing import Tuple
1111
from typing import Union
1212

13+
from setuptools import Distribution
14+
1315
if TYPE_CHECKING:
1416
import sys
1517

@@ -42,7 +44,7 @@
4244
class VersionInferenceApplicable(Protocol):
4345
"""A result object from version inference decision that can be applied to a dist."""
4446

45-
def apply(self, dist: object) -> None: # pragma: no cover - structural type
47+
def apply(self, dist: Distribution) -> None: # pragma: no cover - structural type
4648
...
4749

4850

testing/test_integration.py

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -672,35 +672,37 @@ def test_unicode_in_setup_cfg(tmp_path: Path) -> None:
672672
def test_setup_cfg_version_prevents_inference_version_keyword(
673673
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
674674
) -> None:
675-
# Legacy project with version in setup.cfg
676-
cfg = tmp_path / "setup.cfg"
677-
cfg.write_text(
678-
textwrap.dedent(
679-
"""
680-
[metadata]
681-
name = legacy-proj
682-
version = 0.9.0
683-
"""
684-
),
685-
encoding="utf-8",
686-
)
687-
688-
# No pyproject.toml
675+
# Legacy project setup - we construct the data directly since files are not read anyway
689676
monkeypatch.chdir(tmp_path)
690677

691678
dist = create_clean_distribution("legacy-proj")
692679

693-
# Using keyword should detect an existing version via setup.cfg and avoid inferring
680+
# Using keyword should detect an existing version via legacy data and avoid inferring
694681
from setuptools_scm._integration import setuptools as setuptools_integration
695682
from setuptools_scm._integration.pyproject_reading import PyProjectData
683+
from setuptools_scm._integration.setup_cfg import SetuptoolsBasicData
696684

697-
setuptools_integration.version_keyword(
698-
dist,
699-
"use_scm_version",
700-
True,
701-
_given_pyproject_data=PyProjectData.empty(tmp_path / "pyproject.toml"),
685+
# Construct PyProjectData directly without requiring build backend inference
686+
pyproject_data = PyProjectData.for_testing(
687+
is_required=False, # setuptools-scm not required
688+
section_present=False, # no [tool.setuptools_scm] section
689+
project_present=False, # no [project] section
702690
)
703691

692+
# Construct legacy data with version from setup.cfg
693+
legacy_data = SetuptoolsBasicData(
694+
path=tmp_path / "setup.cfg", name="legacy-proj", version="0.9.0"
695+
)
696+
697+
with pytest.warns(UserWarning, match="version of legacy-proj already set"):
698+
setuptools_integration.version_keyword(
699+
dist,
700+
"use_scm_version",
701+
True,
702+
_given_pyproject_data=pyproject_data,
703+
_given_legacy_data=legacy_data,
704+
)
705+
704706
# setuptools_scm should not set a version when setup.cfg already provided one
705707
assert dist.metadata.version is None
706708

0 commit comments

Comments
 (0)