22from __future__ import annotations
33
44import ctypes
5+ import os
56import re
7+ import sysconfig
68
79import requests
810from bs4 import BeautifulSoup , Tag
911
12+ from src .pointers .std_structs import STRUCT_MAP
13+
1014PAGES : dict [str , BeautifulSoup ] = {}
1115BASE_URL : str = "https://docs.python.org/3.11/c-api"
1216C_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
7683CT_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+
162228def _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
0 commit comments