8
8
from itertools import chain
9
9
from pathlib import Path
10
10
from threading import RLock
11
- from typing import TYPE_CHECKING , Any , Dict , Generator , Iterator , NoReturn , Optional , Sequence , cast
11
+ from typing import TYPE_CHECKING , Any , Dict , Generator , Iterator , Literal , NoReturn , Optional , Sequence , cast
12
12
13
13
from cachetools import cached
14
14
from packaging .requirements import Requirement
15
- from pyproject_api import BackendFailed , CmdStatus , Frontend
15
+ from pyproject_api import (
16
+ BackendFailed ,
17
+ CmdStatus ,
18
+ Frontend ,
19
+ MetadataForBuildEditableResult ,
20
+ MetadataForBuildWheelResult ,
21
+ )
16
22
17
23
from tox .execute .pep517_backend import LocalSubProcessPep517Executor
18
24
from tox .execute .request import StdinSource
@@ -127,6 +133,23 @@ def register_config(self) -> None:
127
133
default = lambda conf , name : self .env_dir / "dist" , # noqa: ARG005
128
134
desc = "directory where to put project packages" ,
129
135
)
136
+ for key in ("sdist" , "wheel" , "editable" ):
137
+ self ._add_config_settings (key )
138
+
139
+ def _add_config_settings (self , build_type : str ) -> None :
140
+ # config settings passed to PEP-517-compliant build backend https://peps.python.org/pep-0517/#config-settings
141
+ keys = {
142
+ "sdist" : ["get_requires_for_build_sdist" , "build_sdist" ],
143
+ "wheel" : ["get_requires_for_build_wheel" , "prepare_metadata_for_build_wheel" , "build_wheel" ],
144
+ "editable" : ["get_requires_for_build_editable" , "prepare_metadata_for_build_editable" , "build_editable" ],
145
+ }
146
+ for key in keys .get (build_type , []):
147
+ self .conf .add_config (
148
+ keys = [f"config_settings_{ key } " ],
149
+ of_type = Dict [str , str ],
150
+ default = None , # type: ignore[arg-type]
151
+ desc = f"config settings passed to the { key } backend API endpoint" ,
152
+ )
130
153
131
154
@property
132
155
def pkg_dir (self ) -> Path :
@@ -164,7 +187,8 @@ def _setup_env(self) -> None:
164
187
self ._setup_build_requires ("editable" )
165
188
166
189
def _setup_build_requires (self , of_type : str ) -> None :
167
- requires = getattr (self ._frontend , f"get_requires_for_build_{ of_type } " )().requires
190
+ settings : ConfigSettings = self .conf [f"config_settings_get_requires_for_build_{ of_type } " ]
191
+ requires = getattr (self ._frontend , f"get_requires_for_build_{ of_type } " )(config_settings = settings ).requires
168
192
self ._install (requires , PythonPackageToxEnv .__name__ , f"requires_for_build_{ of_type } " )
169
193
170
194
def _teardown (self ) -> None :
@@ -206,12 +230,15 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
206
230
of_type : str = for_env ["package" ]
207
231
if of_type == "editable-legacy" :
208
232
self .setup ()
209
- deps = [* self .requires (), * self ._frontend .get_requires_for_build_sdist ().requires , * deps ]
233
+ config_settings : ConfigSettings = self .conf ["config_settings_get_requires_for_build_sdist" ]
234
+ sdist_requires = self ._frontend .get_requires_for_build_sdist (config_settings = config_settings ).requires
235
+ deps = [* self .requires (), * sdist_requires , * deps ]
210
236
package : Package = EditableLegacyPackage (self .core ["tox_root" ], deps ) # the folder itself is the package
211
237
elif of_type == "sdist" :
212
238
self .setup ()
213
239
with self ._pkg_lock :
214
- sdist = self ._frontend .build_sdist (sdist_directory = self .pkg_dir ).sdist
240
+ config_settings = self .conf ["config_settings_build_sdist" ]
241
+ sdist = self ._frontend .build_sdist (sdist_directory = self .pkg_dir , config_settings = config_settings ).sdist
215
242
sdist = create_session_view (sdist , self ._package_temp_path )
216
243
self ._package_paths .add (sdist )
217
244
package = SdistPackage (sdist , deps )
@@ -223,11 +250,12 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
223
250
else :
224
251
self .setup ()
225
252
method = "build_editable" if of_type == "editable" else "build_wheel"
253
+ config_settings = self .conf [f"config_settings_{ method } " ]
226
254
with self ._pkg_lock :
227
255
wheel = getattr (self ._frontend , method )(
228
256
wheel_directory = self .pkg_dir ,
229
257
metadata_directory = self .meta_folder_if_populated ,
230
- config_settings = self . _wheel_config_settings ,
258
+ config_settings = config_settings ,
231
259
).wheel
232
260
wheel = create_session_view (wheel , self ._package_temp_path )
233
261
self ._package_paths .add (wheel )
@@ -313,17 +341,20 @@ def _ensure_meta_present(self, for_env: EnvConfigSet) -> None:
313
341
if self ._distribution_meta is not None : # pragma: no branch
314
342
return # pragma: no cover
315
343
# even if we don't build a wheel we need the requirements for it should we want to build its metadata
316
- target = "editable" if for_env ["package" ] == "editable" else "wheel"
344
+ target : Literal [ "editable" , "wheel" ] = "editable" if for_env ["package" ] == "editable" else "wheel"
317
345
self .call_require_hooks .add (target )
318
346
319
347
self .setup ()
320
348
hook = getattr (self ._frontend , f"prepare_metadata_for_build_{ target } " )
321
- dist_info = hook (self .meta_folder , self ._wheel_config_settings ).metadata
322
- self ._distribution_meta = Distribution .at (str (dist_info ))
323
-
324
- @property
325
- def _wheel_config_settings (self ) -> ConfigSettings | None :
326
- return {"--build-option" : []}
349
+ config : ConfigSettings = self .conf [f"config_settings_prepare_metadata_for_build_{ target } " ]
350
+ result : MetadataForBuildWheelResult | MetadataForBuildEditableResult | None = hook (self .meta_folder , config )
351
+ if result is None :
352
+ config = self .conf [f"config_settings_build_{ target } " ]
353
+ dist_info_path , _ , __ = self ._frontend .metadata_from_built (self .meta_folder , target , config )
354
+ dist_info = str (dist_info_path )
355
+ else :
356
+ dist_info = str (result .metadata )
357
+ self ._distribution_meta = Distribution .at (dist_info )
327
358
328
359
def requires (self ) -> tuple [Requirement , ...]:
329
360
return self ._frontend .requires
@@ -353,16 +384,18 @@ def backend_cmd(self) -> Sequence[str]:
353
384
354
385
def _send (self , cmd : str , ** kwargs : Any ) -> tuple [Any , str , str ]:
355
386
try :
356
- if (
357
- cmd in ("prepare_metadata_for_build_wheel" , "prepare_metadata_for_build_editable" )
358
- # given we'll build a wheel we might skip the prepare step
359
- and ("wheel" in self ._tox_env .builds or "editable" in self ._tox_env .builds )
360
- ):
387
+ if self ._can_skip_prepare (cmd ):
361
388
return None , "" , "" # will need to build wheel either way, avoid prepare
362
389
return super ()._send (cmd , ** kwargs )
363
390
except BackendFailed as exception :
364
391
raise exception if isinstance (exception , ToxBackendFailed ) else ToxBackendFailed (exception ) from exception
365
392
393
+ def _can_skip_prepare (self , cmd : str ) -> bool :
394
+ # given we'll build a wheel we might skip the prepare step
395
+ return cmd in ("prepare_metadata_for_build_wheel" , "prepare_metadata_for_build_editable" ) and (
396
+ "wheel" in self ._tox_env .builds or "editable" in self ._tox_env .builds
397
+ )
398
+
366
399
@contextmanager
367
400
def _send_msg (
368
401
self ,
0 commit comments