5
5
import sys
6
6
from collections import defaultdict
7
7
from contextlib import contextmanager
8
+ from itertools import chain
8
9
from pathlib import Path
9
10
from threading import RLock
10
11
from typing import TYPE_CHECKING , Any , Dict , Generator , Iterator , NoReturn , Optional , Sequence , cast
@@ -97,6 +98,7 @@ def __init__(self, create_args: ToxEnvCreateArgs) -> None:
97
98
super ().__init__ (create_args )
98
99
self ._frontend_ : Pep517VirtualEnvFrontend | None = None
99
100
self .builds : defaultdict [str , list [EnvConfigSet ]] = defaultdict (list )
101
+ self .call_require_hooks : set [str ] = set ()
100
102
self ._distribution_meta : PathDistribution | None = None
101
103
self ._package_dependencies : list [Requirement ] | None = None
102
104
self ._package_name : str | None = None
@@ -150,21 +152,23 @@ def meta_folder_if_populated(self) -> Path | None:
150
152
def register_run_env (self , run_env : RunToxEnv ) -> Generator [tuple [str , str ], PackageToxEnv , None ]:
151
153
yield from super ().register_run_env (run_env )
152
154
build_type = run_env .conf ["package" ]
155
+ self .call_require_hooks .add (build_type )
153
156
self .builds [build_type ].append (run_env .conf )
154
157
155
158
def _setup_env (self ) -> None :
156
159
super ()._setup_env ()
157
- if "editable" in self .builds :
160
+ if "sdist" in self .call_require_hooks or "external" in self .call_require_hooks :
161
+ self ._setup_build_requires ("sdist" )
162
+ if "wheel" in self .call_require_hooks :
163
+ self ._setup_build_requires ("wheel" )
164
+ if "editable" in self .call_require_hooks :
158
165
if not self ._frontend .optional_hooks ["build_editable" ]:
159
166
raise BuildEditableNotSupportedError
160
- build_requires = self ._frontend .get_requires_for_build_editable ().requires
161
- self ._install (build_requires , PythonPackageToxEnv .__name__ , "requires_for_build_editable" )
162
- if "wheel" in self .builds :
163
- build_requires = self ._frontend .get_requires_for_build_wheel ().requires
164
- self ._install (build_requires , PythonPackageToxEnv .__name__ , "requires_for_build_wheel" )
165
- if "sdist" in self .builds or "external" in self .builds :
166
- build_requires = self ._frontend .get_requires_for_build_sdist ().requires
167
- self ._install (build_requires , PythonPackageToxEnv .__name__ , "requires_for_build_sdist" )
167
+ self ._setup_build_requires ("editable" )
168
+
169
+ def _setup_build_requires (self , of_type : str ) -> None :
170
+ requires = getattr (self ._frontend , f"get_requires_for_build_{ of_type } " )().requires
171
+ self ._install (requires , PythonPackageToxEnv .__name__ , f"requires_for_build_{ of_type } " )
168
172
169
173
def _teardown (self ) -> None :
170
174
executor = self ._frontend .backend_executor
@@ -187,6 +191,7 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
187
191
try :
188
192
deps = self ._load_deps (for_env )
189
193
except BuildEditableNotSupportedError :
194
+ self .call_require_hooks .remove ("editable" )
190
195
targets = [e for e in self .builds .pop ("editable" ) if e ["package" ] == "editable" ]
191
196
names = ", " .join (sorted ({t .env_name for t in targets if t .env_name }))
192
197
@@ -199,6 +204,7 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
199
204
for env in targets :
200
205
env ._defined ["package" ].value = "editable-legacy" # type: ignore[attr-defined] # noqa: SLF001
201
206
self .builds ["editable-legacy" ].append (env )
207
+ self ._run_state ["setup" ] = False # force setup again as we need to provision wheel to get dependencies
202
208
deps = self ._load_deps (for_env )
203
209
of_type : str = for_env ["package" ]
204
210
if of_type == "editable-legacy" :
@@ -309,12 +315,13 @@ def get_package_name(self, for_env: EnvConfigSet) -> str:
309
315
def _ensure_meta_present (self , for_env : EnvConfigSet ) -> None :
310
316
if self ._distribution_meta is not None : # pragma: no branch
311
317
return # pragma: no cover
318
+ # even if we don't build a wheel we need the requirements for it should we want to build its metadata
319
+ target = "editable" if for_env ["package" ] == "editable" else "wheel"
320
+ self .call_require_hooks .add (target )
321
+
312
322
self .setup ()
313
- end = self ._frontend
314
- if for_env ["package" ] == "editable" :
315
- dist_info = end .prepare_metadata_for_build_editable (self .meta_folder , self ._wheel_config_settings ).metadata
316
- else :
317
- dist_info = end .prepare_metadata_for_build_wheel (self .meta_folder , self ._wheel_config_settings ).metadata
323
+ hook = getattr (self ._frontend , f"prepare_metadata_for_build_{ target } " )
324
+ dist_info = hook (self .meta_folder , self ._wheel_config_settings ).metadata
318
325
self ._distribution_meta = Distribution .at (str (dist_info ))
319
326
320
327
@property
@@ -332,12 +339,16 @@ def __init__(self, root: Path, env: Pep517VirtualEnvPackager) -> None:
332
339
self ._backend_executor_ : LocalSubProcessPep517Executor | None = None
333
340
into : dict [str , Any ] = {}
334
341
335
- for build_type in ("editable" , "sdist" , "wheel" ): # wrap build methods in a cache wrapper
342
+ for hook in chain (
343
+ (f"get_requires_for_build_{ build_type } " for build_type in ["editable" , "wheel" , "sdist" ]),
344
+ (f"prepare_metadata_for_build_{ build_type } " for build_type in ["editable" , "wheel" ]),
345
+ (f"build_{ build_type } " for build_type in ["editable" , "wheel" , "sdist" ]),
346
+ ): # wrap build methods in a cache wrapper
336
347
337
- def key (* args : Any , bound_return : str = build_type , ** kwargs : Any ) -> str : # noqa: ARG001
348
+ def key (* args : Any , bound_return : str = hook , ** kwargs : Any ) -> str : # noqa: ARG001
338
349
return bound_return
339
350
340
- setattr (self , f"build_ { build_type } " , cached (into , key = key )(getattr (self , f"build_ { build_type } " )))
351
+ setattr (self , hook , cached (into , key = key )(getattr (self , hook )))
341
352
342
353
@property
343
354
def backend_cmd (self ) -> Sequence [str ]:
0 commit comments