pyproject-api 1.11.2__tar.gz → 1.11.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.github/workflows/check.yaml +2 -2
  2. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.github/workflows/release.yaml +5 -5
  3. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.pre-commit-config.yaml +5 -5
  4. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/PKG-INFO +3 -3
  5. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/pyproject.toml +13 -13
  6. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/_frontend.py +25 -13
  7. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/_version.py +2 -2
  8. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/_via_fresh_subprocess.py +3 -2
  9. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/test_frontend.py +93 -20
  10. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/test_frontend_setuptools.py +2 -2
  11. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tox.toml +1 -1
  12. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.github/FUNDING.yaml +0 -0
  13. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.github/SECURITY.md +0 -0
  14. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.github/dependabot.yaml +0 -0
  15. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.github/release.yaml +0 -0
  16. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.gitignore +0 -0
  17. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/.readthedocs.yml +0 -0
  18. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/CODE_OF_CONDUCT.md +0 -0
  19. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/LICENSE +0 -0
  20. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/README.md +0 -0
  21. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/docs/api.rst +0 -0
  22. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/docs/conf.py +0 -0
  23. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/docs/index.rst +0 -0
  24. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/__init__.py +0 -0
  25. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/__main__.py +0 -0
  26. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/_backend.py +0 -0
  27. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/_backend.pyi +0 -0
  28. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/_util.py +0 -0
  29. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/src/pyproject_api/py.typed +0 -0
  30. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/_build_sdist.py +0 -0
  31. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/demo_pkg_inline/build.py +0 -0
  32. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/demo_pkg_inline/pyproject.toml +0 -0
  33. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/test_backend.py +0 -0
  34. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/test_main.py +0 -0
  35. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/test_util.py +0 -0
  36. {pyproject_api-1.11.2 → pyproject_api-1.11.4}/tests/test_version.py +0 -0
@@ -34,7 +34,7 @@ jobs:
34
34
  - dev
35
35
  - pkg_meta
36
36
  os:
37
- - ubuntu-latest
37
+ - ubuntu-26.04
38
38
  - windows-latest
39
39
  - macos-latest
40
40
  exclude:
@@ -47,7 +47,7 @@ jobs:
47
47
  fetch-depth: 0
48
48
  persist-credentials: false
49
49
  - name: Install the latest version of uv
50
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
50
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
51
51
  with:
52
52
  enable-cache: false
53
53
  cache-dependency-glob: "pyproject.toml"
@@ -11,14 +11,14 @@ env:
11
11
 
12
12
  jobs:
13
13
  build:
14
- runs-on: ubuntu-latest
14
+ runs-on: ubuntu-26.04
15
15
  steps:
16
16
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
17
17
  with:
18
18
  fetch-depth: 0
19
19
  persist-credentials: false
20
20
  - name: Install the latest version of uv
21
- uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
21
+ uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
22
22
  with:
23
23
  enable-cache: false
24
24
  cache-dependency-glob: "pyproject.toml"
@@ -26,7 +26,7 @@ jobs:
26
26
  - name: Build package
27
27
  run: uv build --python 3.14 --python-preference only-managed --sdist --wheel . --out-dir dist
28
28
  - name: Store the distribution packages
29
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
29
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
30
30
  with:
31
31
  name: ${{ env.dists-artifact-name }}
32
32
  path: dist/*
@@ -34,7 +34,7 @@ jobs:
34
34
  release:
35
35
  needs:
36
36
  - build
37
- runs-on: ubuntu-latest
37
+ runs-on: ubuntu-26.04
38
38
  environment:
39
39
  name: release
40
40
  url: https://pypi.org/project/pyproject-api/${{ github.ref_name }}
@@ -42,7 +42,7 @@ jobs:
42
42
  id-token: write
43
43
  steps:
44
44
  - name: Download all the dists
45
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
45
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
46
46
  with:
47
47
  name: ${{ env.dists-artifact-name }}
48
48
  path: dist/
@@ -5,7 +5,7 @@ repos:
5
5
  - id: end-of-file-fixer
6
6
  - id: trailing-whitespace
7
7
  - repo: https://github.com/python-jsonschema/check-jsonschema
8
- rev: 0.38.0
8
+ rev: 0.38.2
9
9
  hooks:
10
10
  - id: check-github-workflows
11
11
  args: [ "--verbose" ]
@@ -13,17 +13,17 @@ repos:
13
13
  rev: v2.4.3
14
14
  hooks:
15
15
  - id: codespell
16
- additional_dependencies: ["tomli>=2.3"]
16
+ additional_dependencies: ["tomli>=2.4.1"]
17
17
  - repo: https://github.com/tox-dev/tox-toml-fmt
18
- rev: "v1.10.3"
18
+ rev: "v1.11.0"
19
19
  hooks:
20
20
  - id: tox-toml-fmt
21
21
  - repo: https://github.com/tox-dev/pyproject-fmt
22
- rev: "v2.29.4"
22
+ rev: "v2.30.1"
23
23
  hooks:
24
24
  - id: pyproject-fmt
25
25
  - repo: https://github.com/astral-sh/ruff-pre-commit
26
- rev: "v0.16.7"
26
+ rev: "v0.16.10"
27
27
  hooks:
28
28
  - id: ruff-format
29
29
  - id: ruff
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pyproject-api
3
- Version: 1.11.2
3
+ Version: 1.11.4
4
4
  Summary: API to interact with the python pyproject.toml based projects
5
5
  Project-URL: Changelog, https://github.com/tox-dev/pyproject-api/releases
6
6
  Project-URL: Homepage, https://pyproject-api.readthedocs.io
@@ -29,8 +29,8 @@ Classifier: Topic :: Software Development :: Libraries
29
29
  Classifier: Topic :: Software Development :: Testing
30
30
  Classifier: Topic :: Utilities
31
31
  Requires-Python: >=3.10
32
- Requires-Dist: packaging>=25
33
- Requires-Dist: tomli>=2.3; python_version < '3.11'
32
+ Requires-Dist: packaging>=26.3
33
+ Requires-Dist: tomli>=2.4.1; python_version < '3.11'
34
34
  Description-Content-Type: text/markdown
35
35
 
36
36
  # [`pyproject-api`][RTD]
@@ -2,7 +2,7 @@
2
2
  build-backend = "hatchling.build"
3
3
  requires = [
4
4
  "hatch-vcs>=0.5",
5
- "hatchling>=1.28",
5
+ "hatchling>=1.32.4",
6
6
  ]
7
7
 
8
8
  [project]
@@ -47,8 +47,8 @@ dynamic = [
47
47
  "version",
48
48
  ]
49
49
  dependencies = [
50
- "packaging>=25",
51
- "tomli>=2.3; python_version<'3.11'",
50
+ "packaging>=26.3",
51
+ "tomli>=2.4.1; python_version<'3.11'",
52
52
  ]
53
53
  urls.Changelog = "https://github.com/tox-dev/pyproject-api/releases"
54
54
  urls.Homepage = "https://pyproject-api.readthedocs.io"
@@ -65,28 +65,28 @@ dev = [
65
65
  ]
66
66
  test = [
67
67
  "covdefaults>=2.3",
68
- "pytest>=8.4.2",
69
- "pytest-cov>=7",
70
- "pytest-mock>=3.15.1",
71
- "setuptools>=80.9",
68
+ "pytest>=9.1.1",
69
+ "pytest-cov>=7.1",
70
+ "pytest-mock>=3.16",
71
+ "setuptools>=84",
72
72
  ]
73
73
  type = [
74
- "ty>=0.0.19,<0.0.25",
74
+ "ty<0.0.25,>=0.0.24",
75
75
  { include-group = "docs" },
76
76
  { include-group = "test" },
77
77
  ]
78
78
  docs = [
79
- "furo>=2025.9.25",
79
+ "furo>=2025.12.19",
80
80
  "sphinx-autodoc-typehints>=3.5.1",
81
- "sphinx-llm>=0.4.1",
81
+ "sphinx-llm>=1.1",
82
82
  ]
83
83
  lint = [
84
- "pre-commit-uv>=4.2",
84
+ "pre-commit-uv>=4.3",
85
85
  ]
86
86
  pkg-meta = [
87
87
  "check-wheel-contents>=0.6.3",
88
- "twine>=6.2",
89
- "uv>=0.10.2",
88
+ "twine>=7",
89
+ "uv>=0.12.22",
90
90
  ]
91
91
 
92
92
  [tool.hatch]
@@ -17,7 +17,7 @@ from packaging.requirements import Requirement
17
17
  from pyproject_api._util import ensure_empty_dir
18
18
 
19
19
  if TYPE_CHECKING:
20
- from collections.abc import Iterator
20
+ from collections.abc import Generator, Iterator
21
21
 
22
22
  if sys.version_info >= (3, 11): # pragma: no cover (py311+)
23
23
  import tomllib
@@ -55,11 +55,11 @@ class CmdStatus(ABC):
55
55
  class RequiresBuildSdistResult(NamedTuple):
56
56
  """Information collected while acquiring the source distribution build dependencies."""
57
57
 
58
- #: wheel build dependencies
58
+ #: source distribution build dependencies
59
59
  requires: tuple[Requirement, ...]
60
60
  #: backend standard output while acquiring the source distribution build dependencies
61
61
  out: str
62
- #: backend standard output while acquiring the source distribution build dependencies
62
+ #: backend standard error while acquiring the source distribution build dependencies
63
63
  err: str
64
64
 
65
65
 
@@ -75,7 +75,7 @@ class RequiresBuildWheelResult(NamedTuple):
75
75
 
76
76
 
77
77
  class RequiresBuildEditableResult(NamedTuple):
78
- """Information collected while acquiring the wheel build dependencies."""
78
+ """Information collected while acquiring the editable wheel build dependencies."""
79
79
 
80
80
  #: editable wheel build dependencies
81
81
  requires: tuple[Requirement, ...]
@@ -92,7 +92,7 @@ class MetadataForBuildWheelResult(NamedTuple):
92
92
  metadata: Path
93
93
  #: backend standard output while generating the wheel metadata
94
94
  out: str
95
- #: backend standard output while generating the wheel metadata
95
+ #: backend standard error while generating the wheel metadata
96
96
  err: str
97
97
 
98
98
 
@@ -103,7 +103,7 @@ class MetadataForBuildEditableResult(NamedTuple):
103
103
  metadata: Path
104
104
  #: backend standard output while generating the editable wheel metadata
105
105
  out: str
106
- #: backend standard output while generating the editable wheel metadata
106
+ #: backend standard error while generating the editable wheel metadata
107
107
  err: str
108
108
 
109
109
 
@@ -114,7 +114,7 @@ class SdistResult(NamedTuple):
114
114
  sdist: Path
115
115
  #: backend standard output while building the source distribution
116
116
  out: str
117
- #: backend standard output while building the source distribution
117
+ #: backend standard error while building the source distribution
118
118
  err: str
119
119
 
120
120
 
@@ -211,10 +211,11 @@ class Frontend(ABC):
211
211
  folder: Path,
212
212
  ) -> tuple[Path, tuple[Path, ...], str, str | None, tuple[Requirement, ...], bool]:
213
213
  """
214
- Frontend creation arguments from a python project folder (thould have a ``pypyproject.toml`` file per PEP-518).
214
+ Frontend creation arguments from a python project folder (should have a ``pyproject.toml`` file per PEP-518).
215
215
 
216
216
  :param folder: the python project folder
217
217
  :return: the frontend creation args
218
+ :raises ValueError: if ``build-system`` is not a table, or one of its keys has a type PEP-518 rules out
218
219
 
219
220
  E.g., to create a frontend from a python project folder:
220
221
 
@@ -226,16 +227,20 @@ class Frontend(ABC):
226
227
  if py_project_toml.exists():
227
228
  with py_project_toml.open("rb") as file_handler:
228
229
  py_project = tomllib.load(file_handler)
229
- build_system = py_project.get("build-system", {})
230
+ if not isinstance(build_system := py_project.get("build-system", {}), dict):
231
+ msg = f"build-system must be a table, got {build_system!r}"
232
+ raise ValueError(msg)
230
233
  if "backend-path" in build_system:
231
- backend_paths: tuple[Path, ...] = tuple(folder / p for p in build_system["backend-path"])
234
+ backend_paths: tuple[Path, ...] = tuple(folder / p for p in _str_list(build_system, "backend-path"))
232
235
  else:
233
236
  backend_paths = ()
234
237
  if "requires" in build_system:
235
- requires: tuple[Requirement, ...] = tuple(Requirement(r) for r in build_system.get("requires"))
238
+ requires: tuple[Requirement, ...] = tuple(Requirement(r) for r in _str_list(build_system, "requires"))
236
239
  else:
237
240
  requires = cls.LEGACY_REQUIRES
238
- build_backend = build_system.get("build-backend", cls.LEGACY_BUILD_BACKEND)
241
+ if not isinstance(build_backend := build_system.get("build-backend", cls.LEGACY_BUILD_BACKEND), str):
242
+ msg = f"build-system.build-backend must be a string, got {build_backend!r}"
243
+ raise ValueError(msg)
239
244
  else:
240
245
  backend_paths = ()
241
246
  requires = cls.LEGACY_REQUIRES
@@ -490,7 +495,7 @@ class Frontend(ABC):
490
495
  return metadata_directory / basename, out, err
491
496
 
492
497
  @contextmanager
493
- def _wheel_directory(self) -> Iterator[Path]: # ruff:ignore[no-self-use]
498
+ def _wheel_directory(self) -> Generator[Path, None, None]: # ruff:ignore[no-self-use]
494
499
  with TemporaryDirectory() as wheel_directory:
495
500
  yield Path(wheel_directory)
496
501
 
@@ -528,3 +533,10 @@ class Frontend(ABC):
528
533
  @contextmanager
529
534
  def _send_msg(self, cmd: str, result_file: Path, msg: str) -> Iterator[CmdStatus]:
530
535
  raise NotImplementedError
536
+
537
+
538
+ def _str_list(build_system: dict[str, Any], key: str) -> list[str]:
539
+ if not isinstance(value := build_system[key], list) or not all(isinstance(entry, str) for entry in value):
540
+ msg = f"build-system.{key} must be a list of strings, got {value!r}"
541
+ raise ValueError(msg)
542
+ return value
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '1.11.2'
22
- __version_tuple__ = version_tuple = (1, 11, 2)
21
+ __version__ = version = '1.11.4'
22
+ __version_tuple__ = version_tuple = (1, 11, 4)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -10,7 +10,7 @@ from typing import TYPE_CHECKING, Any, cast
10
10
  from ._frontend import CmdStatus, Frontend
11
11
 
12
12
  if TYPE_CHECKING:
13
- from collections.abc import Iterator
13
+ from collections.abc import Generator
14
14
  from pathlib import Path
15
15
 
16
16
  from packaging.requirements import Requirement
@@ -62,7 +62,7 @@ class SubprocessFrontend(Frontend):
62
62
  self.executable = sys.executable
63
63
 
64
64
  @contextmanager
65
- def _send_msg(self, cmd: str, result_file: Path, msg: str) -> Iterator[SubprocessCmdStatus]: # ruff:ignore[unused-method-argument]
65
+ def _send_msg(self, cmd: str, result_file: Path, msg: str) -> Generator[SubprocessCmdStatus, None, None]: # ruff:ignore[unused-method-argument]
66
66
  env = os.environ.copy()
67
67
  backend = os.pathsep.join(str(i) for i in self._backend_paths).strip()
68
68
  if backend:
@@ -73,6 +73,7 @@ class SubprocessFrontend(Frontend):
73
73
  stderr=PIPE,
74
74
  stdin=PIPE,
75
75
  universal_newlines=True,
76
+ errors="backslashreplace",
76
77
  cwd=self._root,
77
78
  env=env,
78
79
  )
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import re
3
4
  import subprocess # ruff:ignore[suspicious-subprocess-import]
4
5
  import sys
5
6
  from pathlib import Path
@@ -41,8 +42,45 @@ def test_missing_backend(local_builder: Callable[[str], Path]) -> None:
41
42
  assert "ModuleNotFoundError: No module named " in exc.err
42
43
 
43
44
 
44
- @pytest.mark.parametrize(("stream", "output"), [("stdout", "out"), ("stderr", "err")])
45
- def test_large_command_with_backend_output(stream: str, output: str, local_builder: Callable[[str], Path]) -> None:
45
+ @pytest.fixture
46
+ def isolated_build_wheel() -> Callable[[Path, str, int], tuple[str, str]]:
47
+ script = dedent("""
48
+ import sys
49
+ from pathlib import Path
50
+ from pyproject_api import SubprocessFrontend
51
+
52
+ root = Path(sys.argv[1])
53
+ frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(root)[:-1])
54
+ result = frontend.build_wheel(root / "dist", config_settings={"payload": "y" * int(sys.argv[3])})
55
+ print(result.wheel.name)
56
+ print(getattr(result, sys.argv[2]), end="")
57
+ """)
58
+
59
+ def _f(root: Path, output: str, payload_size: int) -> tuple[str, str]:
60
+ # Run the frontend in a child process so a hang fails on the timeout instead of stalling pytest.
61
+ result = subprocess.run(
62
+ [sys.executable, "-X", "utf8", "-c", script, str(root), output, str(payload_size)],
63
+ capture_output=True,
64
+ encoding="utf-8",
65
+ timeout=30,
66
+ check=True,
67
+ )
68
+ assert not result.stderr
69
+ wheel, captured = result.stdout.split("\n", 1)
70
+ return wheel, captured
71
+
72
+ return _f
73
+
74
+
75
+ @pytest.mark.parametrize(
76
+ ("stream", "output"), [pytest.param("stdout", "out", id="stdout"), pytest.param("stderr", "err", id="stderr")]
77
+ )
78
+ def test_large_command_with_backend_output(
79
+ stream: str,
80
+ output: str,
81
+ local_builder: Callable[[str], Path],
82
+ isolated_build_wheel: Callable[[Path, str, int], tuple[str, str]],
83
+ ) -> None:
46
84
  tmp_path = local_builder(f"""
47
85
  import sys
48
86
 
@@ -53,27 +91,30 @@ def test_large_command_with_backend_output(stream: str, output: str, local_build
53
91
  assert config_settings == {{"payload": "y" * 1048576}}
54
92
  return "demo-1.0-py3-none-any.whl"
55
93
  """)
56
- # Isolate the frontend so a pipe deadlock fails with a timeout instead of hanging pytest.
57
- script = dedent("""
94
+ wheel, captured = isolated_build_wheel(tmp_path, output, 1048576)
95
+ assert (wheel, "x" * 1048576 in captured) == ("demo-1.0-py3-none-any.whl", True)
96
+
97
+
98
+ @pytest.mark.parametrize(
99
+ ("stream", "output"), [pytest.param("stdout", "out", id="stdout"), pytest.param("stderr", "err", id="stderr")]
100
+ )
101
+ def test_backend_output_with_invalid_bytes(
102
+ stream: str,
103
+ output: str,
104
+ local_builder: Callable[[str], Path],
105
+ isolated_build_wheel: Callable[[Path, str, int], tuple[str, str]],
106
+ ) -> None:
107
+ tmp_path = local_builder(f"""
58
108
  import sys
59
- from pathlib import Path
60
- from pyproject_api import SubprocessFrontend
61
109
 
62
- root = Path(sys.argv[1])
63
- frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(root)[:-1])
64
- result = frontend.build_wheel(root / "dist", config_settings={"payload": "y" * 1048576})
65
- print(result.wheel.name)
66
- print("x" * 1048576 in getattr(result, sys.argv[2]))
110
+ sys.{stream}.buffer.write(b"before\\xffafter")
111
+ sys.{stream}.flush()
112
+
113
+ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
114
+ return "demo-1.0-py3-none-any.whl"
67
115
  """)
68
- result = subprocess.run(
69
- [sys.executable, "-c", script, str(tmp_path), output],
70
- capture_output=True,
71
- text=True,
72
- timeout=30,
73
- check=True,
74
- )
75
- assert result.stdout.splitlines() == ["demo-1.0-py3-none-any.whl", "True"]
76
- assert not result.stderr
116
+ wheel, captured = isolated_build_wheel(tmp_path, output, 0)
117
+ assert (wheel, "before\\xffafter" in captured) == ("demo-1.0-py3-none-any.whl", True)
77
118
 
78
119
 
79
120
  def test_large_command_with_failed_backend(local_builder: Callable[[str], Path]) -> None:
@@ -128,6 +169,38 @@ def test_backend_build_sdist_demo_pkg_inline(tmp_path: Path, demo_pkg_inline: Pa
128
169
  assert result.sdist == tmp_path / "demo_pkg_inline-1.0.0.tar.gz"
129
170
 
130
171
 
172
+ @pytest.mark.parametrize(
173
+ ("toml", "message"),
174
+ [
175
+ pytest.param(
176
+ '[build-system]\nrequires = "setuptools"',
177
+ "build-system.requires must be a list of strings, got 'setuptools'",
178
+ id="requires-string",
179
+ ),
180
+ pytest.param(
181
+ '[build-system]\nrequires = [{name = "setuptools"}]',
182
+ "build-system.requires must be a list of strings, got [{'name': 'setuptools'}]",
183
+ id="requires-table-entry",
184
+ ),
185
+ pytest.param(
186
+ '[build-system]\nbackend-path = "build"',
187
+ "build-system.backend-path must be a list of strings, got 'build'",
188
+ id="backend-path-string",
189
+ ),
190
+ pytest.param(
191
+ "[build-system]\nbuild-backend = 42",
192
+ "build-system.build-backend must be a string, got 42",
193
+ id="build-backend-int",
194
+ ),
195
+ pytest.param('build-system = "setuptools"', "build-system must be a table, got 'setuptools'", id="not-a-table"),
196
+ ],
197
+ )
198
+ def test_create_args_from_folder_rejects_bad_build_system_type(tmp_path: Path, toml: str, message: str) -> None:
199
+ (tmp_path / "pyproject.toml").write_text(toml)
200
+ with pytest.raises(ValueError, match=rf"^{re.escape(message)}$"):
201
+ SubprocessFrontend.create_args_from_folder(tmp_path)
202
+
203
+
131
204
  def test_backend_obj(tmp_path: Path) -> None:
132
205
  toml = """
133
206
  [build-system]
@@ -11,7 +11,7 @@ from pyproject_api._frontend import BackendFailed
11
11
  from pyproject_api._via_fresh_subprocess import SubprocessFrontend
12
12
 
13
13
  if TYPE_CHECKING:
14
- from collections.abc import Iterator
14
+ from collections.abc import Generator
15
15
  from pathlib import Path
16
16
 
17
17
  from _pytest.tmpdir import TempPathFactory
@@ -149,7 +149,7 @@ class _Result(NamedTuple):
149
149
 
150
150
  def test_result_missing(frontend_setuptools: SubprocessFrontend, tmp_path: Path, mocker: MockerFixture) -> None:
151
151
  @contextmanager
152
- def named_temporary_file(prefix: str) -> Iterator[_Result]:
152
+ def named_temporary_file(prefix: str) -> Generator[_Result, None, None]:
153
153
  write = S_IWUSR | S_IWGRP | S_IWOTH
154
154
  base = tmp_path / prefix
155
155
  result = base.with_suffix(".json")
@@ -1,4 +1,4 @@
1
- requires = [ "tox>=4.34.1", "tox-uv>=1.29" ]
1
+ requires = [ "tox>=4.64.7", "tox-uv>=1.36.1" ]
2
2
  env_list = [ "3.15", "3.14", "3.13", "3.12", "3.11", "3.10", "3.15t", "docs", "fix", "pkg_meta", "type" ]
3
3
  skip_missing_interpreters = true
4
4
 
File without changes
File without changes