Skip to content

Commit 8a13bc3

Browse files
committed
just about done
1 parent b0f1530 commit 8a13bc3

File tree

10 files changed

+2194
-4768
lines changed

10 files changed

+2194
-4768
lines changed

gen.py

Lines changed: 119 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@
22
from __future__ import annotations
33

44
import ctypes
5+
import os
56
import re
7+
import sysconfig
68

79
import requests
810
from bs4 import BeautifulSoup, Tag
911

12+
from src.pointers.std_structs import STRUCT_MAP
13+
1014
PAGES: dict[str, BeautifulSoup] = {}
1115
BASE_URL: str = "https://docs.python.org/3.11/c-api"
1216
C_FUNC = re.compile(
13-
r"(((.+) )?(\w+(\**)*)) (\w+)\(((((.+ \w+(\[\])?,?)*(, ?\.\.\.)?))|void)\)"
17+
r"^(((.+) )?(\w+(\**)*)) (\w+)\(((((.+ \w+(\[\])?,?)*(, ?\.\.\.)?))|void)\)+$"
1418
)
1519

1620

@@ -71,6 +75,9 @@ def ctp(data: str) -> str:
7175
"PyOS_sighandler_t": VOID_P,
7276
"PyGILState_STATE": VOID_P,
7377
"PyModuleDef": "PyModuleDef",
78+
"struct PyModuleDef": "PyModuleDef",
79+
"PyCodeObject": "PyCodeObject",
80+
"PyCapsule_Destructor": VOID_P,
7481
}
7582

7683
CT_TYPES = {
@@ -159,10 +166,69 @@ def _get_type(ctype: str, *, add_pointer: bool = False) -> str | None:
159166
return None
160167

161168

169+
def _gen_str(
170+
name: str,
171+
signature: str,
172+
params: dict[str, list[str]],
173+
minver: str | None,
174+
) -> str | None:
175+
signature = signature.replace(" *", "* ").replace("* *", "** ")
176+
177+
for i in {"#", "//", "typedef", "static", "/*"}:
178+
if signature.startswith(i):
179+
return None
180+
match = C_FUNC.match(signature)
181+
182+
if match and (name not in params):
183+
params[name] = []
184+
group = match.group(1)
185+
ret = _get_type(group)
186+
187+
if not ret:
188+
not_found(group, name)
189+
return None
190+
191+
if match.group(12):
192+
argtypes = ""
193+
else:
194+
args = match.group(7)
195+
if not args:
196+
args = "void"
197+
argtypes = ", ("
198+
199+
if args != "void":
200+
for arg in args.split(", "):
201+
arg_split = arg.split(" ")
202+
argname = arg_split.pop(-1)
203+
add_pointer: bool = False
204+
205+
if argname.endswith("[]"):
206+
argname = argname[:-2]
207+
add_pointer = True
208+
209+
params[name].append(argname if argname != "def" else "df")
210+
211+
join = " ".join(arg_split).replace(
212+
"const ", ""
213+
) # we dont care about consts
214+
typ = _get_type(join, add_pointer=add_pointer)
215+
216+
if not typ:
217+
not_found(join, name)
218+
continue
219+
220+
argtypes += typ + ","
221+
222+
argtypes += ")"
223+
224+
return f"# {signature}\n_register('{name}', {ret}{argtypes}{f', minver={DOUBLE_QUOTE}{minver}{DOUBLE_QUOTE},' if minver else ''})\n"
225+
return None # to make mypy happy
226+
227+
162228
def _gen_ct_bindings() -> dict[str, list[str]]:
163229
params: dict[str, list[str]] = {}
164230

165-
out: str = ""
231+
out: str = "\n\n"
166232
resp = requests.get(
167233
f"{BASE_URL}/stable.html#stable-application-binary-interface",
168234
)
@@ -220,55 +286,50 @@ def _gen_ct_bindings() -> dict[str, list[str]]:
220286
minver = minver_soup.get_text()[:-1].split(" ")[-1]
221287
# this is super janky
222288

223-
signature = signature.replace(" *", "* ").replace("* *", "** ")
224-
match = C_FUNC.match(signature)
289+
result = _gen_str(
290+
name,
291+
signature,
292+
params,
293+
minver,
294+
)
225295

226-
if match:
227-
params[name] = []
228-
group = match.group(1)
229-
ret = _get_type(group)
296+
if result:
297+
out += result
230298

231-
if not ret:
232-
not_found(group, name)
233-
continue
299+
include = sysconfig.get_path("include")
234300

235-
if match.group(12):
236-
argtypes = ""
237-
else:
238-
args = match.group(7)
239-
if not args:
240-
args = "void"
241-
argtypes = ", ("
301+
print(f"Reading signatures from {include}")
302+
for root, _, files in os.walk(include):
303+
for i in files:
304+
path = os.path.join(root, i)
305+
if os.path.isdir(path):
306+
continue
242307

243-
if args != "void":
244-
for arg in args.split(", "):
245-
arg_split = arg.split(" ")
246-
argname = arg_split.pop(-1)
247-
add_pointer: bool = False
308+
with open(path) as f:
309+
lines = f.read().split("\n")
248310

249-
if argname.endswith("[]"):
250-
argname = argname[:-2]
251-
add_pointer = True
311+
for i in lines:
312+
i = i.replace(";", "")
252313

253-
params[name].append(argname if argname != "def" else "df")
314+
if i.startswith("PyAPI_FUNC"):
315+
idx = i.index(")")
316+
typ = i[10:idx]
317+
i = i[10:idx] + typ
254318

255-
join = " ".join(arg_split).replace(
256-
"const ", ""
257-
) # we dont care about consts
258-
typ = _get_type(join, add_pointer=add_pointer)
319+
match = C_FUNC.match(i)
259320

260-
if not typ:
261-
not_found(join, name)
262-
continue
321+
if not match:
322+
continue
263323

264-
argtypes += typ + ","
265-
266-
argtypes += ")"
324+
result = _gen_str(
325+
match.group(6),
326+
i,
327+
params,
328+
None,
329+
)
267330

268-
out += f"# {signature}\n_register('{name}', {ret}{argtypes}{f', minver={DOUBLE_QUOTE}{minver}{DOUBLE_QUOTE},' if minver else ''})\n"
269-
else:
270-
...
271-
# print(f"No match... {signature}")
331+
if result:
332+
out += result
272333

273334
_write_autogen("_pyapi.py", out)
274335
return params
@@ -280,6 +341,13 @@ def map_type(typ: type["ctypes._CData"] | None) -> str:
280341
name = typ.__name__
281342

282343
if name.startswith("LP_"):
344+
actual_name = name[3:]
345+
346+
for k, v in STRUCT_MAP.items():
347+
s_name: str = k.__name__
348+
if s_name == actual_name:
349+
return f"StructPointer[{v.__name__}]"
350+
283351
return "PointerLike"
284352

285353
return CT_TYPES[name[2:] if name != "py_object" else name]
@@ -313,7 +381,7 @@ def main():
313381
return
314382
break
315383

316-
out: str = """raise Exception('autogenerating some things is too complicated or not possible! please manually go through and update the rest. you may delete this error when finished')\n"""
384+
out: str = ""
317385
from src.pointers._pyapi import API_FUNCS
318386

319387
funcs: dict[str, list[str]] = {}
@@ -326,7 +394,8 @@ def main():
326394

327395
zip_params = (params[k], func.argtypes)
328396

329-
if not func.argtypes:
397+
if func.argtypes is None:
398+
print("No argtypes...", func.__name__)
330399
continue
331400

332401
fparams = [f"{param}: {map_type(typ)}" for param, typ in zip(*zip_params)]
@@ -376,6 +445,13 @@ def {name}({', '.join(fparams)}) -> {map_type(restype)}:
376445
{NEWLINE.join(v)}
377446
"""
378447

448+
all_str = "__all__ = ("
449+
450+
for i in funcs:
451+
all_str += f'"{i}",'
452+
453+
out = all_str + ")\n\n" + out
454+
379455
_write_autogen("api_bindings.py", out)
380456
print("success!")
381457

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,4 @@ classifiers = [
2323
dependencies = [
2424
"typing_extensions",
2525
]
26-
version = "2.2.0-a3"
26+
version = "2.2.0-beta"

src/mod.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#include <stdio.h>
77
#include <frameobject.h> // needed to get members of PyFrameObject
88
#define GETOBJ PyObject* obj; if (!PyArg_ParseTuple(args, "O", &obj)) return NULL
9-
#define INIT_HANDLER(handle, msg) if (signal(SIGSEGV, handle) == SIG_ERR) { \
9+
#define INIT_HANDLER(sig, handle, msg) if (signal(sig, handle) == SIG_ERR) { \
1010
PyErr_SetString(PyExc_ImportError, msg); \
1111
return NULL; \
1212
}
@@ -56,7 +56,7 @@ static void sigiot_handler(int signum) {
5656
static PyObject* handle(PyObject* self, PyObject* args) {
5757
PyObject* func;
5858
PyObject* params = NULL;
59-
PyObject* kwargs = NULL;;
59+
PyObject* kwargs = NULL;
6060

6161
if (!PyArg_ParseTuple(
6262
args,
@@ -101,7 +101,6 @@ static PyObject* handle(PyObject* self, PyObject* args) {
101101

102102
PyObject* result = PyObject_Call(func, params, kwargs);
103103
if (!result) return NULL;
104-
105104
return result;
106105
}
107106

@@ -124,10 +123,12 @@ static struct PyModuleDef module = {
124123

125124
PyMODINIT_FUNC PyInit__pointers(void) {
126125
INIT_HANDLER(
126+
SIGIOT,
127127
sigiot_handler,
128128
"cant load _pointers: failed to setup SIGIOT handler"
129129
);
130130
INIT_HANDLER(
131+
SIGSEGV,
131132
sigsegv_handler,
132133
"cant load _pointers: failed to setup SIGSEGV handler"
133134
);

src/pointers/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,5 @@
2828
from .std_structs import DivT, Lconv, LDivT, Tm
2929
from .structure import Struct, StructPointer
3030

31-
__version__ = "2.2.0-a4"
31+
__version__ = "2.2.0-beta"
32+
__license__ = "MIT"

0 commit comments

Comments
 (0)