20
20
from types import MappingProxyType
21
21
from typing import TYPE_CHECKING , Any , Callable , TypeVar , Union
22
22
23
+ from .. import _static
23
24
from .._path import StrPath
24
25
from ..errors import RemovedConfigError
25
26
from ..extension import Extension
@@ -65,10 +66,11 @@ def apply(dist: Distribution, config: dict, filename: StrPath) -> Distribution:
65
66
66
67
67
68
def _apply_project_table (dist : Distribution , config : dict , root_dir : StrPath ):
68
- project_table = config .get ("project" , {}). copy ( )
69
- if not project_table :
69
+ orig_config = config .get ("project" , {})
70
+ if not orig_config :
70
71
return # short-circuit
71
72
73
+ project_table = {k : _static .attempt_conversion (v ) for k , v in orig_config .items ()}
72
74
_handle_missing_dynamic (dist , project_table )
73
75
_unify_entry_points (project_table )
74
76
@@ -98,7 +100,11 @@ def _apply_tool_table(dist: Distribution, config: dict, filename: StrPath):
98
100
raise RemovedConfigError ("\n " .join ([cleandoc (msg ), suggestion ]))
99
101
100
102
norm_key = TOOL_TABLE_RENAMES .get (norm_key , norm_key )
101
- _set_config (dist , norm_key , value )
103
+ corresp = TOOL_TABLE_CORRESPONDENCE .get (norm_key , norm_key )
104
+ if callable (corresp ):
105
+ corresp (dist , value )
106
+ else :
107
+ _set_config (dist , corresp , value )
102
108
103
109
_copy_command_options (config , dist , filename )
104
110
@@ -143,7 +149,7 @@ def _guess_content_type(file: str) -> str | None:
143
149
return None
144
150
145
151
if ext in _CONTENT_TYPES :
146
- return _CONTENT_TYPES [ext ]
152
+ return _static . Str ( _CONTENT_TYPES [ext ])
147
153
148
154
valid = ", " .join (f"{ k } ({ v } )" for k , v in _CONTENT_TYPES .items ())
149
155
msg = f"only the following file extensions are recognized: { valid } ."
@@ -165,10 +171,11 @@ def _long_description(
165
171
text = val .get ("text" ) or expand .read_files (file , root_dir )
166
172
ctype = val ["content-type" ]
167
173
168
- _set_config (dist , "long_description" , text )
174
+ # XXX: Is it completely safe to assume static?
175
+ _set_config (dist , "long_description" , _static .Str (text ))
169
176
170
177
if ctype :
171
- _set_config (dist , "long_description_content_type" , ctype )
178
+ _set_config (dist , "long_description_content_type" , _static . Str ( ctype ) )
172
179
173
180
if file :
174
181
dist ._referenced_files .add (file )
@@ -178,10 +185,12 @@ def _license(dist: Distribution, val: dict, root_dir: StrPath | None):
178
185
from setuptools .config import expand
179
186
180
187
if "file" in val :
181
- _set_config (dist , "license" , expand .read_files ([val ["file" ]], root_dir ))
188
+ # XXX: Is it completely safe to assume static?
189
+ value = expand .read_files ([val ["file" ]], root_dir )
190
+ _set_config (dist , "license" , _static .Str (value ))
182
191
dist ._referenced_files .add (val ["file" ])
183
192
else :
184
- _set_config (dist , "license" , val ["text" ])
193
+ _set_config (dist , "license" , _static . Str ( val ["text" ]) )
185
194
186
195
187
196
def _people (dist : Distribution , val : list [dict ], _root_dir : StrPath | None , kind : str ):
@@ -197,19 +206,17 @@ def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind
197
206
email_field .append (str (addr ))
198
207
199
208
if field :
200
- _set_config (dist , kind , ", " .join (field ))
209
+ _set_config (dist , kind , _static . Str ( ", " .join (field ) ))
201
210
if email_field :
202
- _set_config (dist , f"{ kind } _email" , ", " .join (email_field ))
211
+ _set_config (dist , f"{ kind } _email" , _static . Str ( ", " .join (email_field ) ))
203
212
204
213
205
214
def _project_urls (dist : Distribution , val : dict , _root_dir : StrPath | None ):
206
215
_set_config (dist , "project_urls" , val )
207
216
208
217
209
218
def _python_requires (dist : Distribution , val : str , _root_dir : StrPath | None ):
210
- from packaging .specifiers import SpecifierSet
211
-
212
- _set_config (dist , "python_requires" , SpecifierSet (val ))
219
+ _set_config (dist , "python_requires" , _static .SpecifierSet (val ))
213
220
214
221
215
222
def _dependencies (dist : Distribution , val : list , _root_dir : StrPath | None ):
@@ -237,9 +244,14 @@ def _noop(_dist: Distribution, val: _T) -> _T:
237
244
return val
238
245
239
246
247
+ def _identity (val : _T ) -> _T :
248
+ return val
249
+
250
+
240
251
def _unify_entry_points (project_table : dict ):
241
252
project = project_table
242
- entry_points = project .pop ("entry-points" , project .pop ("entry_points" , {}))
253
+ given = project .pop ("entry-points" , project .pop ("entry_points" , {}))
254
+ entry_points = dict (given ) # Avoid problems with static
243
255
renaming = {"scripts" : "console_scripts" , "gui_scripts" : "gui_scripts" }
244
256
for key , value in list (project .items ()): # eager to allow modifications
245
257
norm_key = json_compatible_key (key )
@@ -333,6 +345,14 @@ def _get_previous_gui_scripts(dist: Distribution) -> list | None:
333
345
return value .get ("gui_scripts" )
334
346
335
347
348
+ def _set_static_list_metadata (attr : str , dist : Distribution , val : list ) -> None :
349
+ """Apply distutils metadata validation but preserve "static" behaviour"""
350
+ meta = dist .metadata
351
+ setter , getter = getattr (meta , f"set_{ attr } " ), getattr (meta , f"get_{ attr } " )
352
+ setter (val )
353
+ setattr (meta , attr , _static .List (getter ()))
354
+
355
+
336
356
def _attrgetter (attr ):
337
357
"""
338
358
Similar to ``operator.attrgetter`` but returns None if ``attr`` is not found
@@ -386,6 +406,12 @@ def _acessor(obj):
386
406
See https://packaging.python.org/en/latest/guides/packaging-namespace-packages/.
387
407
""" ,
388
408
}
409
+ TOOL_TABLE_CORRESPONDENCE = {
410
+ # Fields with corresponding core metadata need to be marked as static:
411
+ "obsoletes" : partial (_set_static_list_metadata , "obsoletes" ),
412
+ "provides" : partial (_set_static_list_metadata , "provides" ),
413
+ "platforms" : partial (_set_static_list_metadata , "platforms" ),
414
+ }
389
415
390
416
SETUPTOOLS_PATCHES = {
391
417
"long_description_content_type" ,
@@ -422,17 +448,17 @@ def _acessor(obj):
422
448
_RESET_PREVIOUSLY_DEFINED : dict = {
423
449
# Fix improper setting: given in `setup.py`, but not listed in `dynamic`
424
450
# dict: pyproject name => value to which reset
425
- "license" : {} ,
426
- "authors" : [] ,
427
- "maintainers" : [] ,
428
- "keywords" : [] ,
429
- "classifiers" : [] ,
430
- "urls" : {} ,
431
- "entry-points" : {} ,
432
- "scripts" : {} ,
433
- "gui-scripts" : {} ,
434
- "dependencies" : [] ,
435
- "optional-dependencies" : {} ,
451
+ "license" : _static . EMPTY_DICT ,
452
+ "authors" : _static . EMPTY_LIST ,
453
+ "maintainers" : _static . EMPTY_LIST ,
454
+ "keywords" : _static . EMPTY_LIST ,
455
+ "classifiers" : _static . EMPTY_LIST ,
456
+ "urls" : _static . EMPTY_DICT ,
457
+ "entry-points" : _static . EMPTY_DICT ,
458
+ "scripts" : _static . EMPTY_DICT ,
459
+ "gui-scripts" : _static . EMPTY_DICT ,
460
+ "dependencies" : _static . EMPTY_LIST ,
461
+ "optional-dependencies" : _static . EMPTY_DICT ,
436
462
}
437
463
438
464
0 commit comments