diff --git a/poetry/core/masonry/api.py b/poetry/core/masonry/api.py index e0a9be858..37bd9098a 100644 --- a/poetry/core/masonry/api.py +++ b/poetry/core/masonry/api.py @@ -78,3 +78,17 @@ def build_sdist( path = SdistBuilder(poetry).build(Path(sdist_directory)) return path.name + + +def build_editable( + wheel_directory: str, + config_settings: Optional[Dict[str, Any]] = None, + metadata_directory: Optional[str] = None, +) -> str: + poetry = Factory().create_poetry(Path(".").resolve(), with_dev=False) + + return WheelBuilder.make_in(poetry, Path(wheel_directory), editable=True) + + +get_requires_for_build_editable = get_requires_for_build_wheel +prepare_metadata_for_build_editable = prepare_metadata_for_build_wheel diff --git a/poetry/core/masonry/builders/wheel.py b/poetry/core/masonry/builders/wheel.py index 29bff308c..f4e805809 100644 --- a/poetry/core/masonry/builders/wheel.py +++ b/poetry/core/masonry/builders/wheel.py @@ -54,6 +54,7 @@ def __init__( target_dir: Optional[Path] = None, original: Optional[Path] = None, executable: Optional[str] = None, + editable: bool = False, ) -> None: super(WheelBuilder, self).__init__(poetry, executable=executable) @@ -62,6 +63,7 @@ def __init__( self._target_dir = target_dir or (self._poetry.file.parent / "dist") if original: self._original_path = original.parent + self._editable = editable @classmethod def make_in( @@ -70,9 +72,14 @@ def make_in( directory: Optional[Path] = None, original: Optional[Path] = None, executable: Optional[str] = None, + editable: bool = False, ) -> str: wb = WheelBuilder( - poetry, target_dir=directory, original=original, executable=executable + poetry, + target_dir=directory, + original=original, + executable=executable, + editable=editable, ) wb.build() @@ -100,12 +107,16 @@ def build(self) -> None: with zipfile.ZipFile( fd_file, mode="w", compression=zipfile.ZIP_DEFLATED ) as zip_file: - if not self._poetry.package.build_should_generate_setup(): - self._build(zip_file) - self._copy_module(zip_file) + if not self._editable: + if not self._poetry.package.build_should_generate_setup(): + self._build(zip_file) + self._copy_module(zip_file) + else: + self._copy_module(zip_file) + self._build(zip_file) else: - self._copy_module(zip_file) self._build(zip_file) + self._write_pth(zip_file) self._copy_file_scripts(zip_file) self._write_metadata(zip_file) @@ -118,6 +129,10 @@ def build(self) -> None: logger.info("Built {}".format(self.wheel_filename)) + def _write_pth(self, wheel: zipfile.ZipFile) -> None: + with self._write_to_zip(wheel, self._module.name + ".pth") as f: + f.write(str(self._original_path.parent)) + def _build(self, wheel: zipfile.ZipFile) -> None: if self._package.build_script: if not self._poetry.package.build_should_generate_setup(): diff --git a/tests/masonry/test_api.py b/tests/masonry/test_api.py index e0c73ab4c..2a5d258c0 100644 --- a/tests/masonry/test_api.py +++ b/tests/masonry/test_api.py @@ -3,6 +3,7 @@ import os import platform import sys +import zipfile from contextlib import contextmanager from pathlib import Path @@ -214,3 +215,19 @@ def test_prepare_metadata_for_build_wheel_with_bad_path_dep_succeeds(): ): api.prepare_metadata_for_build_wheel(tmp_dir) assert "does not exist" in str(err.value) + + +def test_build_editable_wheel(): + with temporary_directory() as tmp_dir, cwd(os.path.join(fixtures, "complete")): + filename = api.build_editable(tmp_dir) + validate_wheel_contents( + name="my_package", + version="1.2.3", + path=str(os.path.join(tmp_dir, filename)), + ) + + with zipfile.ZipFile(os.path.join(tmp_dir, filename)) as z: + namelist = z.namelist() + + assert "my_package.pth" in namelist + assert fixtures == z.read("my_package.pth").decode()