Skip to content

Commit 433715a

Browse files
committed
refactor: use tomlkit instead of manual parsing
Signed-off-by: Henry Schreiner <[email protected]>
1 parent f255ea3 commit 433715a

File tree

6 files changed

+80
-66
lines changed

6 files changed

+80
-66
lines changed

CMakeLists.txt

+4-2
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,10 @@ if(PYBIND11_INSTALL)
327327
if(DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME STREQUAL "pybind11")
328328
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/empty")
329329
file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py")
330-
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/")
331-
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
330+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py"
331+
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/")
332+
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py"
333+
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/")
332334
endif()
333335

334336
# Uninstall target

noxfile.py

+4-14
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import argparse
1010
import contextlib
1111
import os
12-
import re
1312
from pathlib import Path
1413
from typing import TYPE_CHECKING
1514

@@ -21,10 +20,6 @@
2120
nox.needs_version = ">=2025.2.9"
2221
nox.options.default_venv_backend = "uv|virtualenv"
2322

24-
MARKER_PATTERN = re.compile(
25-
r"# not-in-global-start.*?# not-in-global-end\n?", re.DOTALL
26-
)
27-
2823

2924
@nox.session(reuse_venv=True)
3025
def lint(session: nox.Session) -> None:
@@ -137,22 +132,17 @@ def build_global(session: nox.Session) -> None:
137132
"""
138133

139134
installer = ["--installer=uv"] if session.venv_backend == "uv" else []
140-
session.install("build")
135+
session.install("build", "tomlkit")
141136
session.log("Building pybind11-global files")
142137
pyproject = Path("pyproject.toml")
143-
with preserve_file(pyproject) as txt:
144-
new_txt = txt.replace('name = "pybind11"', 'name = "pybind11-global"')
145-
assert txt != new_txt
146-
newer_txt = MARKER_PATTERN.sub("", new_txt)
147-
assert new_txt != newer_txt
148-
138+
with preserve_file(pyproject):
139+
newer_txt = session.run("python", "tools/make_global.py", silent=True)
140+
assert isinstance(newer_txt, str)
149141
pyproject.write_text(newer_txt, encoding="utf-8")
150142
session.run(
151143
"python",
152144
"-m",
153145
"build",
154146
*installer,
155-
"-Cskbuild.wheel.install-dir=/data",
156-
"-Cskbuild.experimental=true",
157147
*session.posargs,
158148
)

pyproject.toml

+20-25
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ Discussions = "https://github.com/pybind/pybind11/discussions"
4242
Changelog = "https://pybind11.readthedocs.io/en/latest/changelog.html"
4343
Chat = "https://gitter.im/pybind/Lobby"
4444

45-
# not-in-global-start
4645
[project.optional-dependencies]
4746
global = ["pybind11-global"] # TODO: pin
47+
4848
[project.scripts]
4949
pybind11-config = "pybind11.__main__:main"
5050

@@ -54,24 +54,6 @@ pybind11 = "pybind11.__main__:main"
5454
[project.entry-points.pkg_config]
5555
pybind11 = "pybind11.share.pkgconfig"
5656

57-
[[tool.scikit-build.generate]]
58-
path = "pybind11/_version.py"
59-
template = '''
60-
from __future__ import annotations
61-
62-
63-
def _to_int(s: str) -> int | str:
64-
try:
65-
return int(s)
66-
except ValueError:
67-
return s
68-
69-
70-
__version__ = "$version"
71-
version_info = tuple(_to_int(s) for s in __version__.split("."))
72-
'''
73-
# not-in-global-end
74-
7557

7658
[tool.scikit-build]
7759
minimum-version = "build-system.requires"
@@ -81,11 +63,6 @@ sdist.exclude = [
8163
]
8264
wheel.install-dir = "pybind11"
8365
wheel.platlib = false
84-
wheel.packages = [
85-
# not-in-global-start
86-
"pybind11",
87-
# not-in-global-end
88-
]
8966

9067
[tool.scikit-build.cmake.define]
9168
BUILD_TESTING = false
@@ -106,6 +83,24 @@ result = "{major}.{minor}.{patch}"
10683
[tool.uv]
10784
index-strategy = "unsafe-best-match"
10885

86+
[[tool.scikit-build.generate]]
87+
path = "pybind11/_version.py"
88+
template = '''
89+
from __future__ import annotations
90+
91+
92+
def _to_int(s: str) -> int | str:
93+
try:
94+
return int(s)
95+
except ValueError:
96+
return s
97+
98+
99+
__version__ = "$version"
100+
version_info = tuple(_to_int(s) for s in __version__.split("."))
101+
'''
102+
103+
109104
[tool.mypy]
110105
files = ["pybind11"]
111106
python_version = "3.8"
@@ -114,7 +109,7 @@ enable_error_code = ["ignore-without-code", "redundant-expr", "truthy-bool"]
114109
warn_unreachable = true
115110

116111
[[tool.mypy.overrides]]
117-
module = ["ghapi.*"]
112+
module = ["ghapi.*", "tomlkit"] # tomlkit has types, but not very helpful
118113
ignore_missing_imports = true
119114

120115

tests/extra_python_package/test_files.py

+18-25
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,23 @@
33
import contextlib
44
import os
55
import shutil
6-
import string
7-
import re
86
import subprocess
97
import sys
108
import tarfile
119
import zipfile
1210
from pathlib import Path
1311
from typing import Generator
14-
from pprint import pprint
1512

1613
# These tests must be run explicitly
1714

1815
DIR = Path(__file__).parent.resolve()
1916
MAIN_DIR = DIR.parent.parent
2017

21-
MARKER_PATTERN = re.compile(
22-
r"# not-in-global-start.*?# not-in-global-end\n?", re.DOTALL
23-
)
18+
19+
# Newer pytest has global path setting, but keeping old pytest for now
20+
sys.path.append(str(MAIN_DIR / "tools"))
21+
22+
from make_global import get_global # noqa: E402
2423

2524
HAS_UV = shutil.which("uv") is not None
2625
UV_ARGS = ["--installer=uv"] if HAS_UV else []
@@ -158,12 +157,8 @@ def build_global() -> Generator[None, None, None]:
158157
"""
159158

160159
pyproject = MAIN_DIR / "pyproject.toml"
161-
with preserve_file(pyproject) as txt:
162-
new_txt = txt.replace('name = "pybind11"', 'name = "pybind11-global"')
163-
assert txt != new_txt
164-
newer_txt = MARKER_PATTERN.sub("", new_txt)
165-
assert new_txt != newer_txt
166-
160+
with preserve_file(pyproject):
161+
newer_txt = get_global()
167162
pyproject.write_text(newer_txt, encoding="utf-8")
168163
yield
169164

@@ -191,32 +186,35 @@ def test_build_sdist(monkeypatch, tmpdir):
191186
(sdist,) = tmpdir.visit("*.tar.gz")
192187

193188
with tarfile.open(str(sdist), "r:gz") as tar:
194-
start = tar.getnames()[0] + "/"
195-
version = start[9:-1]
196189
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
197190

198191
pyproject_toml = read_tz_file(tar, "pyproject.toml")
199192

200193
files = headers | sdist_files
201194
assert files <= simpler
202195

203-
simple_version = ".".join(version.split(".")[:3])
204196
assert b'name = "pybind11"' in pyproject_toml
205197

206198

207199
def test_build_global_dist(monkeypatch, tmpdir):
208200
monkeypatch.chdir(MAIN_DIR)
209201
with build_global():
210202
subprocess.run(
211-
[sys.executable, "-m", "build", "--sdist", "--outdir", str(tmpdir), *UV_ARGS],
203+
[
204+
sys.executable,
205+
"-m",
206+
"build",
207+
"--sdist",
208+
"--outdir",
209+
str(tmpdir),
210+
*UV_ARGS,
211+
],
212212
check=True,
213213
)
214214

215215
(sdist,) = tmpdir.visit("*.tar.gz")
216216

217217
with tarfile.open(str(sdist), "r:gz") as tar:
218-
start = tar.getnames()[0] + "/"
219-
version = start[16:-1]
220218
simpler = {n.split("/", 1)[-1] for n in tar.getnames()[1:]}
221219

222220
pyproject_toml = read_tz_file(tar, "pyproject.toml")
@@ -258,12 +256,8 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
258256
monkeypatch.chdir(MAIN_DIR)
259257
with build_global():
260258
subprocess.run(
261-
[sys.executable, "-m", "pip", "wheel", ".",
262-
"-Cskbuild.wheel.install-dir=/data",
263-
"-Cskbuild.experimental=true",
264-
"-w",
265-
str(tmpdir),
266-
*UV_ARGS], check=True
259+
[sys.executable, "-m", "pip", "wheel", ".", "-w", str(tmpdir), *UV_ARGS],
260+
check=True,
267261
)
268262

269263
(wheel,) = tmpdir.visit("*.whl")
@@ -282,7 +276,6 @@ def tests_build_global_wheel(monkeypatch, tmpdir):
282276
names = z.namelist()
283277

284278
beginning = names[0].split("/", 1)[0].rsplit(".", 1)[0]
285-
pprint(names)
286279
trimmed = {n[len(beginning) + 1 :] for n in names}
287280

288281
assert files == trimmed

tests/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ scipy~=1.5.4; platform_python_implementation=="CPython" and python_version<"3.10
1515
scipy~=1.8.0; platform_python_implementation=="CPython" and python_version=="3.10" and sys_platform!="win32"
1616
scipy~=1.11.1; platform_python_implementation=="CPython" and python_version>="3.11" and python_version<"3.13" and sys_platform!="win32"
1717
scipy~=1.15.2; platform_python_implementation=="CPython" and python_version=="3.13" and sys_platform!="win32"
18+
tomlkit

tools/make_global.py

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env -S uv run -q
2+
3+
# /// script
4+
# dependencies = ["tomlkit"]
5+
# ///
6+
from __future__ import annotations
7+
8+
from pathlib import Path
9+
10+
import tomlkit
11+
12+
DIR = Path(__file__).parent.resolve()
13+
PYPROJECT = DIR.parent / "pyproject.toml"
14+
15+
16+
def get_global() -> str:
17+
pyproject = tomlkit.parse(PYPROJECT.read_text())
18+
del pyproject["tool"]["scikit-build"]["generate"]
19+
del pyproject["project"]["optional-dependencies"]
20+
del pyproject["project"]["entry-points"]
21+
del pyproject["project"]["scripts"]
22+
pyproject["project"]["name"] = "pybind11-global"
23+
pyproject["tool"]["scikit-build"]["experimental"] = True
24+
pyproject["tool"]["scikit-build"]["wheel"]["install-dir"] = "/data"
25+
pyproject["tool"]["scikit-build"]["wheel"]["packages"] = []
26+
27+
result = tomlkit.dumps(pyproject)
28+
assert isinstance(result, str)
29+
return result
30+
31+
32+
if __name__ == "__main__":
33+
print(get_global())

0 commit comments

Comments
 (0)