1919from tox .tox_env .api import ToxEnvCreateArgs
2020from tox .tox_env .errors import Fail
2121from tox .tox_env .package import Package , PackageToxEnv
22- from tox .tox_env .python .package import DevLegacyPackage , PythonPackageToxEnv , SdistPackage , WheelPackage
22+ from tox .tox_env .python .package import (
23+ EditableLegacyPackage ,
24+ EditablePackage ,
25+ PythonPackageToxEnv ,
26+ SdistPackage ,
27+ WheelPackage ,
28+ )
2329from tox .tox_env .register import ToxEnvRegister
2430from tox .tox_env .runner import RunToxEnv
2531
@@ -128,6 +134,9 @@ def register_run_env(self, run_env: RunToxEnv) -> Generator[tuple[str, str], Pac
128134
129135 def _setup_env (self ) -> None :
130136 super ()._setup_env ()
137+ if "editable" in self .builds :
138+ build_requires = self ._frontend .get_requires_for_build_editable ().requires
139+ self .installer .install (build_requires , PythonPackageToxEnv .__name__ , "requires_for_build_editable" )
131140 if "wheel" in self .builds :
132141 build_requires = self ._frontend .get_requires_for_build_wheel ().requires
133142 self .installer .install (build_requires , PythonPackageToxEnv .__name__ , "requires_for_build_wheel" )
@@ -151,28 +160,29 @@ def perform_packaging(self, for_env: EnvConfigSet) -> list[Package]:
151160 """build the package to install"""
152161 deps = self ._load_deps (for_env )
153162 of_type : str = for_env ["package" ]
154- if of_type == "dev -legacy" :
163+ if of_type == "editable -legacy" :
155164 self .setup ()
156165 deps = [* self .requires (), * self ._frontend .get_requires_for_build_sdist ().requires ] + deps
157- package : Package = DevLegacyPackage (self .core ["tox_root" ], deps ) # the folder itself is the package
166+ package : Package = EditableLegacyPackage (self .core ["tox_root" ], deps ) # the folder itself is the package
158167 elif of_type == "sdist" :
159168 self .setup ()
160169 with self ._pkg_lock :
161170 package = SdistPackage (self ._frontend .build_sdist (sdist_directory = self .pkg_dir ).sdist , deps )
162- elif of_type == "wheel" :
171+ elif of_type in { "wheel" , "editable" } :
163172 w_env = self ._wheel_build_envs .get (for_env ["wheel_build_env" ])
164173 if w_env is not None and w_env is not self :
165174 with w_env .display_context (self ._has_display_suspended ):
166175 return w_env .perform_packaging (for_env )
167176 else :
168177 self .setup ()
169178 with self ._pkg_lock :
170- path = self ._frontend .build_wheel (
179+ method = "build_editable" if of_type == "editable" else "build_wheel"
180+ path = getattr (self ._frontend , method )(
171181 wheel_directory = self .pkg_dir ,
172182 metadata_directory = self .meta_folder ,
173183 config_settings = self ._wheel_config_settings ,
174184 ).wheel
175- package = WheelPackage (path , deps )
185+ package = ( EditablePackage if of_type == "editable" else WheelPackage ) (path , deps )
176186 else : # pragma: no cover # for when we introduce new packaging types and don't implement
177187 raise TypeError (f"cannot handle package type { of_type } " ) # pragma: no cover
178188 return [package ]
@@ -209,38 +219,42 @@ def _load_deps_from_built_metadata(self, for_env: EnvConfigSet) -> list[Requirem
209219 # to calculate the package metadata, otherwise ourselves
210220 of_type : str = for_env ["package" ]
211221 reqs : list [Requirement ] | None = None
212- if of_type == "wheel" : # wheel packages
222+ if of_type in ( "wheel" , "editable" ) : # wheel packages
213223 w_env = self ._wheel_build_envs .get (for_env ["wheel_build_env" ])
214224 if w_env is not None and w_env is not self :
215225 with w_env .display_context (self ._has_display_suspended ):
216- reqs = w_env .get_package_dependencies () if isinstance (w_env , Pep517VirtualEnvPackager ) else []
226+ if isinstance (w_env , Pep517VirtualEnvPackager ):
227+ reqs = w_env .get_package_dependencies (for_env )
228+ else :
229+ reqs = []
217230 if reqs is None :
218- reqs = self .get_package_dependencies ()
231+ reqs = self .get_package_dependencies (for_env )
219232 extras : set [str ] = for_env ["extras" ]
220233 deps = dependencies_with_extras (reqs , extras )
221234 return deps
222235
223- def get_package_dependencies (self ) -> list [Requirement ]:
236+ def get_package_dependencies (self , for_env : EnvConfigSet ) -> list [Requirement ]:
224237 with self ._pkg_lock :
225238 if self ._package_dependencies is None : # pragma: no branch
226- self ._ensure_meta_present ()
239+ self ._ensure_meta_present (for_env )
227240 requires : list [str ] = cast (PathDistribution , self ._distribution_meta ).requires or []
228241 self ._package_dependencies = [Requirement (i ) for i in requires ] # pragma: no branch
229242 return self ._package_dependencies
230243
231- def _ensure_meta_present (self ) -> None :
244+ def _ensure_meta_present (self , for_env : EnvConfigSet ) -> None :
232245 if self ._distribution_meta is not None : # pragma: no branch
233246 return # pragma: no cover
234247 self .setup ()
235- dist_info = self ._frontend .prepare_metadata_for_build_wheel (
236- self .meta_folder ,
237- self ._wheel_config_settings ,
238- ).metadata
248+ end = self ._frontend
249+ if for_env ["package" ] == "editable" :
250+ dist_info = end .prepare_metadata_for_build_editable (self .meta_folder , self ._wheel_config_settings ).metadata
251+ else :
252+ dist_info = end .prepare_metadata_for_build_wheel (self .meta_folder , self ._wheel_config_settings ).metadata
239253 self ._distribution_meta = Distribution .at (str (dist_info ))
240254
241255 @property
242256 def _wheel_config_settings (self ) -> ConfigSettings | None :
243- return {"--global -option" : ["--bdist-dir" , str ( self . env_dir / "build" ) ]}
257+ return {"--build -option" : []}
244258
245259 def requires (self ) -> tuple [Requirement , ...]:
246260 return self ._frontend .requires
@@ -258,16 +272,17 @@ def __init__(self, root: Path, env: Pep517VirtualEnvPackager) -> None:
258272 )
259273 self .build_wheel = pkg_cache (self .build_wheel ) # type: ignore
260274 self .build_sdist = pkg_cache (self .build_sdist ) # type: ignore
275+ self .build_editable = pkg_cache (self .build_editable ) # type: ignore
261276
262277 @property
263278 def backend_cmd (self ) -> Sequence [str ]:
264279 return ["python" ] + self .backend_args
265280
266281 def _send (self , cmd : str , ** kwargs : Any ) -> tuple [Any , str , str ]:
267282 try :
268- if cmd == "prepare_metadata_for_build_wheel" :
283+ if cmd in ( "prepare_metadata_for_build_wheel" , "prepare_metadata_for_build_editable" ) :
269284 # given we'll build a wheel we might skip the prepare step
270- if "wheel" in self ._tox_env .builds :
285+ if "wheel" in self ._tox_env .builds or "editable" in self . _tox_env . builds :
271286 result = {
272287 "code" : 1 ,
273288 "exc_type" : "AvoidRedundant" ,
0 commit comments