@@ -31,6 +31,7 @@ class PyProjectData:
31
31
is_required : bool
32
32
section_present : bool
33
33
project_present : bool
34
+ build_requires : list [str ]
34
35
35
36
@classmethod
36
37
def for_testing (
@@ -41,6 +42,8 @@ def for_testing(
41
42
project_present : bool = False ,
42
43
project_name : str | None = None ,
43
44
has_dynamic_version : bool = True ,
45
+ build_requires : list [str ] | None = None ,
46
+ local_scheme : str | None = None ,
44
47
) -> PyProjectData :
45
48
"""Create a PyProjectData instance for testing purposes."""
46
49
project : TOML_RESULT
@@ -54,14 +57,22 @@ def for_testing(
54
57
if project_present and has_dynamic_version :
55
58
project ["dynamic" ] = ["version" ]
56
59
60
+ if build_requires is None :
61
+ build_requires = []
62
+ if local_scheme is not None :
63
+ assert section_present
64
+ section = {"local_scheme" : local_scheme }
65
+ else :
66
+ section = {}
57
67
return cls (
58
68
path = DEFAULT_PYPROJECT_PATH ,
59
69
tool_name = DEFAULT_TOOL_NAME ,
60
70
project = project ,
61
- section = {} ,
71
+ section = section ,
62
72
is_required = is_required ,
63
73
section_present = section_present ,
64
74
project_present = project_present ,
75
+ build_requires = build_requires ,
65
76
)
66
77
67
78
@classmethod
@@ -76,6 +87,7 @@ def empty(
76
87
is_required = False ,
77
88
section_present = False ,
78
89
project_present = False ,
90
+ build_requires = [],
79
91
)
80
92
81
93
@property
@@ -95,14 +107,28 @@ def should_infer(self) -> bool:
95
107
"""
96
108
Determine if setuptools_scm should infer version based on configuration.
97
109
98
- Only infer when an explicit [tool.setuptools_scm] section is present.
99
- The presence of setuptools-scm in build-system.requires or
100
- project.dynamic does NOT auto-enable inference.
110
+ Infer when:
111
+ 1. An explicit [tool.setuptools_scm] section is present, OR
112
+ 2. setuptools-scm[simple] is in build-system.requires AND
113
+ version is in project.dynamic
101
114
102
115
Returns:
103
116
True if [tool.setuptools_scm] is present, otherwise False
104
117
"""
105
- return self .section_present
118
+ # Original behavior: explicit tool section
119
+ if self .section_present :
120
+ return True
121
+
122
+ # New behavior: simple extra + dynamic version
123
+ if self .project_present :
124
+ dynamic_fields = self .project .get ("dynamic" , [])
125
+ if "version" in dynamic_fields :
126
+ if has_build_package_with_extra (
127
+ self .build_requires , "setuptools-scm" , "simple"
128
+ ):
129
+ return True
130
+
131
+ return False
106
132
107
133
108
134
def has_build_package (
@@ -115,6 +141,34 @@ def has_build_package(
115
141
return False
116
142
117
143
144
+ def has_build_package_with_extra (
145
+ requires : Sequence [str ], canonical_build_package_name : str , extra_name : str
146
+ ) -> bool :
147
+ """Check if a build dependency has a specific extra.
148
+
149
+ Args:
150
+ requires: List of requirement strings from build-system.requires
151
+ canonical_build_package_name: The canonical package name to look for
152
+ extra_name: The extra name to check for (e.g., "simple")
153
+
154
+ Returns:
155
+ True if the package is found with the specified extra
156
+ """
157
+ from .._requirement_cls import Requirement
158
+
159
+ for requirement_string in requires :
160
+ try :
161
+ requirement = Requirement (requirement_string )
162
+ package_name = extract_package_name (requirement_string )
163
+ if package_name == canonical_build_package_name :
164
+ if extra_name in requirement .extras :
165
+ return True
166
+ except Exception :
167
+ # If parsing fails, continue to next requirement
168
+ continue
169
+ return False
170
+
171
+
118
172
def read_pyproject (
119
173
path : Path = DEFAULT_PYPROJECT_PATH ,
120
174
tool_name : str = DEFAULT_TOOL_NAME ,
@@ -160,7 +214,14 @@ def read_pyproject(
160
214
project = defn .get ("project" , {})
161
215
project_present = "project" in defn
162
216
pyproject_data = PyProjectData (
163
- path , tool_name , project , section , is_required , section_present , project_present
217
+ path ,
218
+ tool_name ,
219
+ project ,
220
+ section ,
221
+ is_required ,
222
+ section_present ,
223
+ project_present ,
224
+ requires ,
164
225
)
165
226
166
227
return pyproject_data
0 commit comments