2020from types import MappingProxyType
2121from typing import TYPE_CHECKING , Any , Callable , TypeVar , Union
2222
23+ from .. import _static
2324from .._path import StrPath
2425from ..errors import RemovedConfigError
2526from ..extension import Extension
@@ -65,10 +66,11 @@ def apply(dist: Distribution, config: dict, filename: StrPath) -> Distribution:
6566
6667
6768def _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 :
7071 return # short-circuit
7172
73+ project_table = {k : _static .attempt_conversion (v ) for k , v in orig_config .items ()}
7274 _handle_missing_dynamic (dist , project_table )
7375 _unify_entry_points (project_table )
7476
@@ -98,7 +100,11 @@ def _apply_tool_table(dist: Distribution, config: dict, filename: StrPath):
98100 raise RemovedConfigError ("\n " .join ([cleandoc (msg ), suggestion ]))
99101
100102 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 )
102108
103109 _copy_command_options (config , dist , filename )
104110
@@ -143,7 +149,7 @@ def _guess_content_type(file: str) -> str | None:
143149 return None
144150
145151 if ext in _CONTENT_TYPES :
146- return _CONTENT_TYPES [ext ]
152+ return _static . Str ( _CONTENT_TYPES [ext ])
147153
148154 valid = ", " .join (f"{ k } ({ v } )" for k , v in _CONTENT_TYPES .items ())
149155 msg = f"only the following file extensions are recognized: { valid } ."
@@ -165,10 +171,11 @@ def _long_description(
165171 text = val .get ("text" ) or expand .read_files (file , root_dir )
166172 ctype = val ["content-type" ]
167173
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 ))
169176
170177 if ctype :
171- _set_config (dist , "long_description_content_type" , ctype )
178+ _set_config (dist , "long_description_content_type" , _static . Str ( ctype ) )
172179
173180 if file :
174181 dist ._referenced_files .add (file )
@@ -178,10 +185,12 @@ def _license(dist: Distribution, val: dict, root_dir: StrPath | None):
178185 from setuptools .config import expand
179186
180187 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 ))
182191 dist ._referenced_files .add (val ["file" ])
183192 else :
184- _set_config (dist , "license" , val ["text" ])
193+ _set_config (dist , "license" , _static . Str ( val ["text" ]) )
185194
186195
187196def _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
197206 email_field .append (str (addr ))
198207
199208 if field :
200- _set_config (dist , kind , ", " .join (field ))
209+ _set_config (dist , kind , _static . Str ( ", " .join (field ) ))
201210 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 ) ))
203212
204213
205214def _project_urls (dist : Distribution , val : dict , _root_dir : StrPath | None ):
206215 _set_config (dist , "project_urls" , val )
207216
208217
209218def _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 ))
213220
214221
215222def _dependencies (dist : Distribution , val : list , _root_dir : StrPath | None ):
@@ -237,9 +244,14 @@ def _noop(_dist: Distribution, val: _T) -> _T:
237244 return val
238245
239246
247+ def _identity (val : _T ) -> _T :
248+ return val
249+
250+
240251def _unify_entry_points (project_table : dict ):
241252 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
243255 renaming = {"scripts" : "console_scripts" , "gui_scripts" : "gui_scripts" }
244256 for key , value in list (project .items ()): # eager to allow modifications
245257 norm_key = json_compatible_key (key )
@@ -333,6 +345,14 @@ def _get_previous_gui_scripts(dist: Distribution) -> list | None:
333345 return value .get ("gui_scripts" )
334346
335347
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+
336356def _attrgetter (attr ):
337357 """
338358 Similar to ``operator.attrgetter`` but returns None if ``attr`` is not found
@@ -386,6 +406,12 @@ def _acessor(obj):
386406 See https://packaging.python.org/en/latest/guides/packaging-namespace-packages/.
387407 """ ,
388408}
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+ }
389415
390416SETUPTOOLS_PATCHES = {
391417 "long_description_content_type" ,
@@ -422,17 +448,17 @@ def _acessor(obj):
422448_RESET_PREVIOUSLY_DEFINED : dict = {
423449 # Fix improper setting: given in `setup.py`, but not listed in `dynamic`
424450 # 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 ,
436462}
437463
438464
0 commit comments