From 0daeb69f3413c7157d7ea99b68499f18ac59cd67 Mon Sep 17 00:00:00 2001 From: harbinresearcher Date: Wed, 14 Oct 2026 11:44:34 +0800 Subject: [PATCH 1/2] fix(cli): resolve output directories before invoking backends --- docs/index.rst | 3 +++ src/pyproject_api/__main__.py | 2 +- tests/test_main.py | 24 ++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 6c1ffa7..d5f767e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,6 +3,9 @@ ``pyproject-api`` aims to abstract away interaction with ``pyproject.toml`` style projects in a flexible way. +The CLI accepts a relative project directory and resolves a relative ``--outdir`` from the caller's working directory. +Without ``--outdir``, distributions are written to ``dist`` inside the project directory. + .. toctree:: :hidden: diff --git a/src/pyproject_api/__main__.py b/src/pyproject_api/__main__.py index 7e10879..2f4ef7b 100644 --- a/src/pyproject_api/__main__.py +++ b/src/pyproject_api/__main__.py @@ -65,7 +65,7 @@ def main(argv: list[str]) -> None: # ruff:ignore[undocumented-public-function] parser = main_parser() args = parser.parse_args(argv) - outdir = args.outdir or args.srcdir / "dist" + outdir = (args.outdir or args.srcdir / "dist").absolute() # we intentionally do not build editable distributions by default distributions = args.distributions or ["sdist", "wheel"] diff --git a/tests/test_main.py b/tests/test_main.py index 4d820b3..85c6544 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,6 +1,7 @@ from __future__ import annotations from pathlib import Path +from shutil import copytree from typing import TYPE_CHECKING import pytest @@ -12,6 +13,25 @@ import pytest_mock +@pytest.mark.parametrize("distribution", ["--sdist", "--wheel", "--editable"]) +@pytest.mark.parametrize("output", ["default", "relative", "absolute"]) +def test_cli_build_output_directory( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, distribution: str, output: str +) -> None: + project = tmp_path / "project" + copytree(Path(__file__).parent / "demo_pkg_inline", project) + monkeypatch.chdir(tmp_path) + cli_args = ["project", distribution] + expected_dir = project / "dist" if output == "default" else tmp_path / "artifacts" + if output != "default": + cli_args.extend(["--outdir", "artifacts" if output == "relative" else str(expected_dir)]) + + pyproject_api.__main__.main(cli_args) + + filename = "demo_pkg_inline-1.0.0.tar.gz" if distribution == "--sdist" else "demo_pkg_inline-1.0.0-py3-none-any.whl" + assert (expected_dir / filename).is_file() + + @pytest.mark.parametrize( ("cli_args", "srcdir", "outdir", "hooks"), [ @@ -24,13 +44,13 @@ ( ["src"], Path("src"), - Path("src") / "dist", + Path("src").absolute() / "dist", ["build_sdist", "build_wheel"], ), ( ["-o", "out"], Path.cwd(), - Path("out"), + Path("out").absolute(), ["build_sdist", "build_wheel"], ), ( From f82ad9396e06378c76de71cd79414467a88e8631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Tue, 13 Oct 2026 21:13:53 -0700 Subject: [PATCH 2/2] test(cli): table-drive output directory cases Pair each distribution with its artifact name and each outdir mode with its expected location, removing branching from the test body. Drop the absolute --outdir cases: that path never changed. Drop the docs note restating ordinary relative-path behavior. --- docs/index.rst | 3 --- tests/test_main.py | 38 +++++++++++++++++++++++++------------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index d5f767e..6c1ffa7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,9 +3,6 @@ ``pyproject-api`` aims to abstract away interaction with ``pyproject.toml`` style projects in a flexible way. -The CLI accepts a relative project directory and resolves a relative ``--outdir`` from the caller's working directory. -Without ``--outdir``, distributions are written to ``dist`` inside the project directory. - .. toctree:: :hidden: diff --git a/tests/test_main.py b/tests/test_main.py index 85c6544..8363769 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -13,23 +13,35 @@ import pytest_mock -@pytest.mark.parametrize("distribution", ["--sdist", "--wheel", "--editable"]) -@pytest.mark.parametrize("output", ["default", "relative", "absolute"]) -def test_cli_build_output_directory( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch, distribution: str, output: str +@pytest.mark.parametrize( + ("distribution", "filename"), + [ + pytest.param("--sdist", "demo_pkg_inline-1.0.0.tar.gz", id="sdist"), + pytest.param("--wheel", "demo_pkg_inline-1.0.0-py3-none-any.whl", id="wheel"), + pytest.param("--editable", "demo_pkg_inline-1.0.0-py3-none-any.whl", id="editable"), + ], +) +@pytest.mark.parametrize( + ("outdir_args", "outdir"), + [ + pytest.param([], "project/dist", id="default-outdir"), + pytest.param(["--outdir", "artifacts"], "artifacts", id="relative-outdir"), + ], +) +def test_cli_build_relative_to_caller( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + distribution: str, + filename: str, + outdir_args: list[str], + outdir: str, ) -> None: - project = tmp_path / "project" - copytree(Path(__file__).parent / "demo_pkg_inline", project) + copytree(Path(__file__).parent / "demo_pkg_inline", tmp_path / "project") monkeypatch.chdir(tmp_path) - cli_args = ["project", distribution] - expected_dir = project / "dist" if output == "default" else tmp_path / "artifacts" - if output != "default": - cli_args.extend(["--outdir", "artifacts" if output == "relative" else str(expected_dir)]) - pyproject_api.__main__.main(cli_args) + pyproject_api.__main__.main(["project", distribution, *outdir_args]) - filename = "demo_pkg_inline-1.0.0.tar.gz" if distribution == "--sdist" else "demo_pkg_inline-1.0.0-py3-none-any.whl" - assert (expected_dir / filename).is_file() + assert (tmp_path / outdir / filename).is_file() @pytest.mark.parametrize(