generate type stubs which are sufficiently self-consistent to run mypy on all the examples

This commit is contained in:
Richard Smith 2024-11-16 20:12:17 +00:00 committed by Richard Smith
parent a33f4fcc9a
commit e6f2c188db
4 changed files with 54 additions and 19 deletions

View file

@ -45,8 +45,8 @@ def process(filename):
strval = str(e['value']).strip() strval = str(e['value']).strip()
if strval.startswith("__"): if strval.startswith("__"):
continue continue
if strval in known_enum: # if strval in known_enum:
print(e['name'] + " = raylib." + strval) # print(e['name'] + " = raylib." + strval)
elif strval in known_define: elif strval in known_define:
print(e['name'] + " = " + strval) print(e['name'] + " = " + strval)
else: else:

View file

@ -29,6 +29,12 @@ for filename in (Path("raylib.json"), Path("raymath.json"), Path("rlgl.json"), P
for st in js["structs"]: for st in js["structs"]:
if st["name"] not in known_structs: if st["name"] not in known_structs:
known_structs[st["name"]] = st known_structs[st["name"]] = st
for e in js['enums']:
if e['name'] and e['values']:
print ("class "+e['name']+"(int):")
for value in e['values']:
print(" "+value['name']+" = "+str(value['value']))
print("")
def ctype_to_python_type(t): def ctype_to_python_type(t):
@ -42,29 +48,39 @@ def ctype_to_python_type(t):
return "int" return "int"
elif t == "uint64_t": elif t == "uint64_t":
return "int" return "int"
elif t == "short":
return "int"
elif t == "unsigned short":
return "int"
elif t == "double": elif t == "double":
return "float" return "float"
elif "char * *" in t: elif "char * *" in t:
return "list[str]" return "list[str]"
elif "char *" in t: elif "char *" in t:
return "str" return "str"
elif "char" in t: elif t == "char":
return "str" # not sure about this one return "str" # not sure about this one
elif t == "unsigned char":
return "int"
elif "*" in t: elif "*" in t:
return "Any" return "Any"
elif "[" in t:
return "list" # TODO FIXME type of items in the list
elif t.startswith("struct"): elif t.startswith("struct"):
return t.replace("struct ", "") return t.replace("struct ", "")
elif t.startswith("unsigned"): elif t.startswith("unsigned"):
return t.replace("unsigned ", "") return t.replace("unsigned ", "")
elif t.startswith("enum"):
return t.replace("enum ", "")
else: else:
return t return t
print("""from typing import Any print("""from typing import Any
import _cffi_backend # type: ignore
def pointer(struct): ffi: _cffi_backend.FFI
...
""") """)
# These words can be used for c arg names, but not in python # These words can be used for c arg names, but not in python
@ -90,6 +106,8 @@ for name, attr in getmembers(rl):
if param_name in reserved_words: if param_name in reserved_words:
param_name = param_name + "_" + str(i) param_name = param_name + "_" + str(i)
param_type = ctype_to_python_type(arg.cname) param_type = ctype_to_python_type(arg.cname)
if "struct" in arg.cname:
param_type += "|list|tuple"
sig += f"{param_name}: {param_type}," sig += f"{param_name}: {param_type},"
return_type = ffi.typeof(attr).result.cname return_type = ffi.typeof(attr).result.cname
@ -128,11 +146,14 @@ for struct in ffi.list_types()[0]:
print(f' """ struct """') print(f' """ struct """')
sig = "" sig = ""
for arg in ffi.typeof(struct).fields: for arg in ffi.typeof(struct).fields:
sig += ", " + arg[0] ptype = ctype_to_python_type(arg[1].type.cname)
if arg[1].type.kind == "struct":
ptype += "|list|tuple"
sig += f", {arg[0]}: {ptype}|None = None"
print(f" def __init__(self{sig}):") print(f" def __init__(self{sig}):")
for arg in ffi.typeof(struct).fields: for arg in ffi.typeof(struct).fields:
print(f" self.{arg[0]}={arg[0]}") print(f" self.{arg[0]}:{ctype_to_python_type(arg[1].type.cname)} = {arg[0]} # type: ignore")
# elif ffi.typeof(struct).kind == "enum": # elif ffi.typeof(struct).kind == "enum":
# print(f"{struct}: int") # print(f"{struct}: int")

View file

@ -42,27 +42,35 @@ def ctype_to_python_type(t):
return "int" return "int"
elif t == "uint64_t": elif t == "uint64_t":
return "int" return "int"
elif t == "short":
return "int"
elif t == "unsigned short":
return "int"
elif t == "double": elif t == "double":
return "float" return "float"
elif "char * *" in t: elif "char * *" in t:
return "list[str]" return "list[bytes]"
elif "char *" in t: elif "char *" in t:
return "str" return "bytes"
elif "char" in t: elif "char" in t:
return "str" # not sure about this one return "bytes" # not sure about this one
elif "*" in t: elif "*" in t:
return "Any" return "Any"
elif "[" in t:
return "list" # TODO FIXME type of items in the list
elif t.startswith("struct"): elif t.startswith("struct"):
return t.replace("struct ", "") return t.replace("struct ", "")
elif t.startswith("unsigned"): elif t.startswith("unsigned"):
return t.replace("unsigned ", "") return t.replace("unsigned ", "")
elif t.startswith("enum"):
return t.replace("enum ", "")
else: else:
return t return t
print("""from typing import Any print("""from typing import Any
import _cffi_backend import _cffi_backend # type: ignore
ffi: _cffi_backend.FFI ffi: _cffi_backend.FFI
rl: _cffi_backend.Lib rl: _cffi_backend.Lib
@ -96,6 +104,8 @@ for name, attr in getmembers(rl):
if param_name in reserved_words: if param_name in reserved_words:
param_name = param_name + "_" + str(i) param_name = param_name + "_" + str(i)
param_type = ctype_to_python_type(arg.cname) param_type = ctype_to_python_type(arg.cname)
if "struct" in arg.cname:
param_type += "|list|tuple"
sig += f"{param_name}: {param_type}," sig += f"{param_name}: {param_type},"
return_type = ffi.typeof(attr).result.cname return_type = ffi.typeof(attr).result.cname
@ -121,17 +131,22 @@ for struct in ffi.list_types()[0]:
# if ffi.typeof(struct).fields is None: # if ffi.typeof(struct).fields is None:
# print("weird empty struct, skipping", file=sys.stderr) # print("weird empty struct, skipping", file=sys.stderr)
# continue # continue
print(f"{struct}: struct") print(f"class {struct}:")
# sig = "" # sig = ""
# for arg in ffi.typeof(struct).fields: fields = ffi.typeof(struct).fields
# sig += ", " + arg[0] if fields is not None:
#print(ffi.typeof(struct).fields)
#print(f" {arg}: {arg}")
# print(f" def __init__(self{sig}):") # print(f" def __init__(self{sig}):")
# #
# for arg in ffi.typeof(struct).fields: for arg in ffi.typeof(struct).fields:
print(f" {arg[0]}: {ctype_to_python_type(arg[1].type.cname)}")
else:
print(" ...")
# print(f" self.{arg[0]}={arg[0]}") # print(f" self.{arg[0]}={arg[0]}")
elif ffi.typeof(struct).kind == "enum": elif ffi.typeof(struct).kind == "enum":
print(f"{struct}: int") print(f"{struct} = int")
else: else:
print("ERROR UNKNOWN TYPE", ffi.typeof(struct), file=sys.stderr) print("ERROR UNKNOWN TYPE", ffi.typeof(struct), file=sys.stderr)

View file

@ -42,14 +42,13 @@ python3 create_enums.py > dynamic/raylib/enums.py
echo "creating defines.py" echo "creating defines.py"
python3 create_define_consts.py > raylib/defines.py python3 create_define_consts.py | awk '!seen[$0]++' > raylib/defines.py
python3 create_define_consts.py > dynamic/raylib/defines.py python3 create_define_consts.py | awk '!seen[$0]++' > dynamic/raylib/defines.py
echo "creating pyi files" echo "creating pyi files"
python3 create_stub_pyray.py > pyray/__init__.pyi python3 create_stub_pyray.py > pyray/__init__.pyi
python3 create_enums.py >> pyray/__init__.pyi
python3 create_stub_static.py >raylib/__init__.pyi python3 create_stub_static.py >raylib/__init__.pyi