Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ Frontend
--------
.. autoclass:: Frontend

Build and metadata directory arguments may be relative to the caller's current
working directory. They are passed to the backend as absolute paths, since the
backend runs from the project directory. Result paths retain the spelling of the
directory argument. Configuration settings and generic command arguments are
passed through unchanged.

.. autoclass:: OptionalHooks

Exceptions
Expand Down
14 changes: 7 additions & 7 deletions src/pyproject_api/_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ def prepare_metadata_for_build_wheel(
if self.optional_hooks["prepare_metadata_for_build_wheel"]:
basename, out, err = self._send(
cmd="prepare_metadata_for_build_wheel",
metadata_directory=metadata_directory,
metadata_directory=metadata_directory.absolute(),
config_settings=config_settings,
)
if basename is None:
Expand Down Expand Up @@ -379,7 +379,7 @@ def prepare_metadata_for_build_editable(
if self.optional_hooks["prepare_metadata_for_build_editable"]:
basename, out, err = self._send(
cmd="prepare_metadata_for_build_editable",
metadata_directory=metadata_directory,
metadata_directory=metadata_directory.absolute(),
config_settings=config_settings,
)
if basename is None:
Expand All @@ -400,7 +400,7 @@ def build_sdist(self, sdist_directory: Path, config_settings: ConfigSettings | N
sdist_directory.mkdir(parents=True, exist_ok=True)
basename, out, err = self._send(
cmd="build_sdist",
sdist_directory=sdist_directory,
sdist_directory=sdist_directory.absolute(),
config_settings=config_settings,
)
if not isinstance(basename, str):
Expand All @@ -424,9 +424,9 @@ def build_wheel(
wheel_directory.mkdir(parents=True, exist_ok=True)
basename, out, err = self._send(
cmd="build_wheel",
wheel_directory=wheel_directory,
wheel_directory=wheel_directory.absolute(),
config_settings=config_settings,
metadata_directory=metadata_directory,
metadata_directory=None if metadata_directory is None else metadata_directory.absolute(),
)
if not isinstance(basename, str):
self._unexpected_response("build_wheel", basename, str, out, err)
Expand All @@ -449,9 +449,9 @@ def build_editable(
wheel_directory.mkdir(parents=True, exist_ok=True)
basename, out, err = self._send(
cmd="build_editable",
wheel_directory=wheel_directory,
wheel_directory=wheel_directory.absolute(),
config_settings=config_settings,
metadata_directory=metadata_directory,
metadata_directory=None if metadata_directory is None else metadata_directory.absolute(),
)
if not isinstance(basename, str):
self._unexpected_response("build_editable", basename, str, out, err)
Expand Down
96 changes: 96 additions & 0 deletions tests/test_frontend.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,102 @@ def demo_pkg_inline() -> Path:
return Path(__file__).absolute().parent / "demo_pkg_inline"


@pytest.mark.parametrize("target", ["sdist", "wheel", "editable"])
@pytest.mark.parametrize("relative", [True, False], ids=["relative", "absolute"])
def test_build_output_from_caller_directory(
target: str, relative: bool, tmp_path: Path, demo_pkg_inline: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.chdir(tmp_path)
frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(demo_pkg_inline)[:-1])
directory = Path("artifacts") if relative else tmp_path / "artifacts"

result = getattr(frontend, f"build_{target}")(directory)
artifact = getattr(result, "sdist" if target == "sdist" else "wheel")

assert artifact.parent == directory
assert artifact.is_file()
assert artifact.absolute().parent == tmp_path / "artifacts"


@pytest.mark.parametrize("target", ["wheel", "editable"])
@pytest.mark.parametrize("relative", [True, False], ids=["relative", "absolute"])
def test_prepare_metadata_from_caller_directory(
target: str,
relative: bool,
tmp_path: Path,
local_builder: Callable[[str], Path],
monkeypatch: pytest.MonkeyPatch,
) -> None:
root = local_builder(r"""
from pathlib import Path

def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
metadata = Path(metadata_directory) / "demo.dist-info"
metadata.mkdir()
(metadata / "METADATA").write_text("Name: demo\nVersion: 1.0\n")
return metadata.name

prepare_metadata_for_build_editable = prepare_metadata_for_build_wheel
""")
caller = tmp_path / "caller"
caller.mkdir()
monkeypatch.chdir(caller)
frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(root)[:-1])
directory = Path("metadata") if relative else caller / "metadata"

result = getattr(frontend, f"prepare_metadata_for_build_{target}")(directory)

assert result is not None
assert result.metadata == directory / "demo.dist-info"
assert (result.metadata / "METADATA").read_text() == "Name: demo\nVersion: 1.0\n"


@pytest.mark.parametrize("target", ["wheel", "editable"])
def test_build_with_relative_metadata_directory(
target: str,
tmp_path: Path,
local_builder: Callable[[str], Path],
monkeypatch: pytest.MonkeyPatch,
) -> None:
root = local_builder("""
from pathlib import Path

def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
assert config_settings == {"source": "relative/source"}
content = (Path(metadata_directory) / "METADATA").read_text()
(Path(wheel_directory) / "demo.whl").write_text(content)
return "demo.whl"

build_editable = build_wheel
""")
caller = tmp_path / "caller"
caller.mkdir()
monkeypatch.chdir(caller)
metadata = Path("metadata")
metadata.mkdir()
(metadata / "METADATA").write_text("existing metadata")
frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(root)[:-1])

result = getattr(frontend, f"build_{target}")(
Path("artifacts"), metadata_directory=metadata, config_settings={"source": "relative/source"}
)

assert result.wheel == Path("artifacts/demo.whl")
assert result.wheel.read_text() == "existing metadata"


def test_generic_command_retains_relative_path(local_builder: Callable[[str], Path]) -> None:
root = local_builder("""
def echo(path, config_settings):
return [path, config_settings]
""")
frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(root)[:-1])

result, _, _ = frontend.send_cmd("echo", path=Path("relative/path"), config_settings={"source": "relative/source"})

assert result == [str(Path("relative/path")), {"source": "relative/source"}]


def test_backend_no_prepare_wheel(tmp_path: Path, demo_pkg_inline: Path) -> None:
frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(demo_pkg_inline)[:-1])
result = frontend.prepare_metadata_for_build_wheel(tmp_path)
Expand Down
Loading