pyproject-api 1.11.0__tar.gz → 1.11.2__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.0 → pyproject_api-1.11.2}/.github/dependabot.yaml +6 -1
  2. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.github/workflows/check.yaml +2 -2
  3. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.github/workflows/release.yaml +3 -3
  4. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.pre-commit-config.yaml +5 -5
  5. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/PKG-INFO +6 -4
  6. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/README.md +4 -2
  7. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/docs/conf.py +5 -0
  8. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/pyproject.toml +2 -1
  9. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/_util.py +1 -1
  10. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/_version.py +2 -2
  11. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/_via_fresh_subprocess.py +6 -6
  12. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/test_frontend.py +48 -0
  13. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/test_util.py +12 -0
  14. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.github/FUNDING.yaml +0 -0
  15. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.github/SECURITY.md +0 -0
  16. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.github/release.yaml +0 -0
  17. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.gitignore +0 -0
  18. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/.readthedocs.yml +0 -0
  19. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/CODE_OF_CONDUCT.md +0 -0
  20. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/LICENSE +0 -0
  21. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/docs/api.rst +0 -0
  22. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/docs/index.rst +0 -0
  23. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/__init__.py +0 -0
  24. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/__main__.py +0 -0
  25. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/_backend.py +0 -0
  26. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/_backend.pyi +0 -0
  27. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/_frontend.py +0 -0
  28. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/src/pyproject_api/py.typed +0 -0
  29. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/_build_sdist.py +0 -0
  30. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/demo_pkg_inline/build.py +0 -0
  31. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/demo_pkg_inline/pyproject.toml +0 -0
  32. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/test_backend.py +0 -0
  33. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/test_frontend_setuptools.py +0 -0
  34. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/test_main.py +0 -0
  35. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tests/test_version.py +0 -0
  36. {pyproject_api-1.11.0 → pyproject_api-1.11.2}/tox.toml +0 -0
@@ -3,6 +3,11 @@ updates:
3
3
  - package-ecosystem: "github-actions"
4
4
  directory: "/"
5
5
  schedule:
6
- interval: "daily"
6
+ interval: "weekly"
7
+ day: "tuesday"
8
+ groups:
9
+ github-actions:
10
+ patterns:
11
+ - "*"
7
12
  cooldown:
8
13
  default-days: 7
@@ -42,12 +42,12 @@ jobs:
42
42
  - { os: macos-latest, env: "dev" }
43
43
  - { os: macos-latest, env: "pkg_meta" }
44
44
  steps:
45
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
45
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
46
46
  with:
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@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
50
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
51
51
  with:
52
52
  enable-cache: false
53
53
  cache-dependency-glob: "pyproject.toml"
@@ -13,12 +13,12 @@ jobs:
13
13
  build:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
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@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
21
+ uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
22
22
  with:
23
23
  enable-cache: false
24
24
  cache-dependency-glob: "pyproject.toml"
@@ -47,6 +47,6 @@ jobs:
47
47
  name: ${{ env.dists-artifact-name }}
48
48
  path: dist/
49
49
  - name: Publish to PyPI
50
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
50
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
51
51
  with:
52
52
  attestations: true
@@ -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.37.4
8
+ rev: 0.38.0
9
9
  hooks:
10
10
  - id: check-github-workflows
11
11
  args: [ "--verbose" ]
@@ -15,21 +15,21 @@ repos:
15
15
  - id: codespell
16
16
  additional_dependencies: ["tomli>=2.3"]
17
17
  - repo: https://github.com/tox-dev/tox-toml-fmt
18
- rev: "v1.9.3"
18
+ rev: "v1.10.3"
19
19
  hooks:
20
20
  - id: tox-toml-fmt
21
21
  - repo: https://github.com/tox-dev/pyproject-fmt
22
- rev: "v2.25.3"
22
+ rev: "v2.29.4"
23
23
  hooks:
24
24
  - id: pyproject-fmt
25
25
  - repo: https://github.com/astral-sh/ruff-pre-commit
26
- rev: "v0.15.22"
26
+ rev: "v0.16.7"
27
27
  hooks:
28
28
  - id: ruff-format
29
29
  - id: ruff
30
30
  args: ["--fix", "--unsafe-fixes", "--exit-non-zero-on-fix"]
31
31
  - repo: https://github.com/zizmorcore/zizmor-pre-commit
32
- rev: v1.27.0
32
+ rev: v1.30.1
33
33
  hooks:
34
34
  - id: zizmor
35
35
  - repo: meta
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: pyproject-api
3
- Version: 1.11.0
3
+ Version: 1.11.2
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
@@ -33,11 +33,13 @@ Requires-Dist: packaging>=25
33
33
  Requires-Dist: tomli>=2.3; python_version < '3.11'
34
34
  Description-Content-Type: text/markdown
35
35
 
36
- # [`pyproject-api`](https://pyproject-api.readthedocs.io/en/latest/)
36
+ # [`pyproject-api`][RTD]
37
37
 
38
38
  [![PyPI](https://img.shields.io/pypi/v/pyproject-api?style=flat-square)](https://pypi.org/project/pyproject-api/)
39
39
  [![Supported Python
40
40
  versions](https://img.shields.io/pypi/pyversions/pyproject-api.svg)](https://pypi.org/project/pyproject-api/)
41
41
  [![Downloads](https://static.pepy.tech/badge/pyproject-api/month)](https://pepy.tech/project/pyproject-api)
42
42
  [![check](https://github.com/tox-dev/pyproject-api/actions/workflows/check.yaml/badge.svg)](https://github.com/tox-dev/pyproject-api/actions/workflows/check.yaml)
43
- [![Documentation Status](https://readthedocs.org/projects/pyproject-api/badge/?version=latest)](https://pyproject-api.readthedocs.io/en/latest/?badge=latest)
43
+ [![Documentation Status](https://readthedocs.org/projects/pyproject-api/badge/?version=latest)][RTD]
44
+
45
+ [RTD]: https://pyproject-api.rtfd.io
@@ -1,8 +1,10 @@
1
- # [`pyproject-api`](https://pyproject-api.readthedocs.io/en/latest/)
1
+ # [`pyproject-api`][RTD]
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/pyproject-api?style=flat-square)](https://pypi.org/project/pyproject-api/)
4
4
  [![Supported Python
5
5
  versions](https://img.shields.io/pypi/pyversions/pyproject-api.svg)](https://pypi.org/project/pyproject-api/)
6
6
  [![Downloads](https://static.pepy.tech/badge/pyproject-api/month)](https://pepy.tech/project/pyproject-api)
7
7
  [![check](https://github.com/tox-dev/pyproject-api/actions/workflows/check.yaml/badge.svg)](https://github.com/tox-dev/pyproject-api/actions/workflows/check.yaml)
8
- [![Documentation Status](https://readthedocs.org/projects/pyproject-api/badge/?version=latest)](https://pyproject-api.readthedocs.io/en/latest/?badge=latest)
8
+ [![Documentation Status](https://readthedocs.org/projects/pyproject-api/badge/?version=latest)][RTD]
9
+
10
+ [RTD]: https://pyproject-api.rtfd.io
@@ -1,6 +1,7 @@
1
1
  # ruff:ignore[undocumented-public-module]
2
2
  from __future__ import annotations
3
3
 
4
+ import os
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  from sphinx.domains.python import PythonDomain
@@ -19,6 +20,7 @@ project_copyright = f"{company}"
19
20
  version, release = __version__, __version__.split("+")[0]
20
21
 
21
22
  extensions = [
23
+ "sphinx_llm.txt",
22
24
  "sphinx.ext.autosectionlabel",
23
25
  "sphinx.ext.extlinks",
24
26
  "sphinx.ext.autodoc",
@@ -71,3 +73,6 @@ def setup(app: Sphinx) -> None: # ruff:ignore[undocumented-public-function]
71
73
  return super().resolve_xref(env, fromdocname, builder, type, target, node, contnode)
72
74
 
73
75
  app.add_domain(PatchedPythonDomain, override=True)
76
+
77
+
78
+ markdown_http_base = os.environ.get("READTHEDOCS_CANONICAL_URL", "")
@@ -78,6 +78,7 @@ type = [
78
78
  docs = [
79
79
  "furo>=2025.9.25",
80
80
  "sphinx-autodoc-typehints>=3.5.1",
81
+ "sphinx-llm>=0.4.1",
81
82
  ]
82
83
  lint = [
83
84
  "pre-commit-uv>=4.2",
@@ -152,10 +153,10 @@ environment.python-version = "3.14"
152
153
 
153
154
  [tool.coverage]
154
155
  run.parallel = true
155
- run.patch = [ "subprocess" ]
156
156
  run.plugins = [
157
157
  "covdefaults",
158
158
  ]
159
+ run.patch = [ "subprocess" ]
159
160
  paths.source = [
160
161
  "src",
161
162
  ".tox*/*/lib/python*/site-packages",
@@ -11,7 +11,7 @@ def ensure_empty_dir(path: Path) -> None:
11
11
  if path.exists():
12
12
  if path.is_dir():
13
13
  for sub_path in path.iterdir():
14
- if sub_path.is_dir():
14
+ if sub_path.is_dir() and not sub_path.is_symlink():
15
15
  rmtree(sub_path, ignore_errors=True)
16
16
  else:
17
17
  sub_path.unlink()
@@ -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.0'
22
- __version_tuple__ = version_tuple = (1, 11, 0)
21
+ __version__ = version = '1.11.2'
22
+ __version_tuple__ = version_tuple = (1, 11, 2)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -5,7 +5,7 @@ import sys
5
5
  from contextlib import contextmanager
6
6
  from subprocess import PIPE, Popen # ruff:ignore[suspicious-subprocess-import]
7
7
  from threading import Thread
8
- from typing import IO, TYPE_CHECKING, Any, cast
8
+ from typing import TYPE_CHECKING, Any, cast
9
9
 
10
10
  from ._frontend import CmdStatus, Frontend
11
11
 
@@ -17,14 +17,15 @@ if TYPE_CHECKING:
17
17
 
18
18
 
19
19
  class SubprocessCmdStatus(CmdStatus, Thread):
20
- def __init__(self, process: Popen[str]) -> None:
20
+ def __init__(self, process: Popen[str], msg: str | None = None) -> None:
21
21
  super().__init__()
22
22
  self.process = process
23
+ self._msg = msg
23
24
  self._out_err: tuple[str, str] | None = None
24
25
  self.start()
25
26
 
26
27
  def run(self) -> None:
27
- self._out_err = self.process.communicate()
28
+ self._out_err = self.process.communicate(input=self._msg)
28
29
 
29
30
  @property
30
31
  def done(self) -> bool:
@@ -66,7 +67,7 @@ class SubprocessFrontend(Frontend):
66
67
  backend = os.pathsep.join(str(i) for i in self._backend_paths).strip()
67
68
  if backend:
68
69
  env["PYTHONPATH"] = backend
69
- process = Popen(
70
+ process = Popen( # ruff:ignore[subprocess-without-shell-equals-true] # fixed argv, no shell
70
71
  args=[self.executable, *self.backend_args],
71
72
  stdout=PIPE,
72
73
  stderr=PIPE,
@@ -75,8 +76,7 @@ class SubprocessFrontend(Frontend):
75
76
  cwd=self._root,
76
77
  env=env,
77
78
  )
78
- cast("IO[str]", process.stdin).write(f"{os.linesep}{msg}{os.linesep}")
79
- yield SubprocessCmdStatus(process)
79
+ yield SubprocessCmdStatus(process, f"{os.linesep}{msg}{os.linesep}")
80
80
 
81
81
  def send_cmd(self, cmd: str, **kwargs: Any) -> tuple[Any, str, str]:
82
82
  """
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import subprocess # ruff:ignore[suspicious-subprocess-import]
4
+ import sys
3
5
  from pathlib import Path
4
6
  from textwrap import dedent
5
7
  from typing import TYPE_CHECKING, Literal
@@ -39,6 +41,52 @@ def test_missing_backend(local_builder: Callable[[str], Path]) -> None:
39
41
  assert "ModuleNotFoundError: No module named " in exc.err
40
42
 
41
43
 
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:
46
+ tmp_path = local_builder(f"""
47
+ import sys
48
+
49
+ sys.{stream}.write("x" * 1048576)
50
+ sys.{stream}.flush()
51
+
52
+ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
53
+ assert config_settings == {{"payload": "y" * 1048576}}
54
+ return "demo-1.0-py3-none-any.whl"
55
+ """)
56
+ # Isolate the frontend so a pipe deadlock fails with a timeout instead of hanging pytest.
57
+ script = dedent("""
58
+ import sys
59
+ from pathlib import Path
60
+ from pyproject_api import SubprocessFrontend
61
+
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]))
67
+ """)
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
77
+
78
+
79
+ def test_large_command_with_failed_backend(local_builder: Callable[[str], Path]) -> None:
80
+ tmp_path = local_builder('raise RuntimeError("backend import failed")')
81
+ frontend = SubprocessFrontend(*SubprocessFrontend.create_args_from_folder(tmp_path)[:-1])
82
+
83
+ with pytest.raises(BackendFailed) as context:
84
+ frontend.build_wheel(tmp_path / "dist", config_settings={"payload": "x" * 1048576})
85
+
86
+ assert "failed to start backend" in context.value.err
87
+ assert "RuntimeError: backend import failed" in context.value.err
88
+
89
+
42
90
  @pytest.mark.parametrize("cmd", ["build_wheel", "build_sdist"])
43
91
  def test_missing_required_cmd(cmd: str, local_builder: Callable[[str], Path]) -> None:
44
92
  tmp_path = local_builder("")
@@ -43,3 +43,15 @@ def test_ensure_empty_dir_on_path_folder(tmp_path: Path) -> None:
43
43
  (tmp_path / "d").write_text("")
44
44
  ensure_empty_dir(tmp_path)
45
45
  assert list(tmp_path.iterdir()) == []
46
+
47
+
48
+ def test_ensure_empty_dir_on_path_symlink_to_folder(tmp_path: Path) -> None:
49
+ target = tmp_path / "target"
50
+ target.mkdir()
51
+ (target / "c").write_text("")
52
+ path = tmp_path / "a"
53
+ path.mkdir()
54
+ (path / "b").symlink_to(target, target_is_directory=True)
55
+ ensure_empty_dir(path)
56
+ assert list(path.iterdir()) == []
57
+ assert list(target.iterdir()) == [target / "c"]
File without changes
File without changes