Skip to content

Commit 583dd9a

Browse files
committed
Initial support for generating .pyi files
Signed-off-by: Matthew Ballance <[email protected]>
1 parent def34e6 commit 583dd9a

File tree

4 files changed

+174
-2
lines changed

4 files changed

+174
-2
lines changed

src/astbuilder/pyext_accessor_gen.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from astbuilder.pyext_type_name_gen import PyExtTypeNameGen
2+
from astbuilder.pyext_type_name_gen_pyi import PyExtTypeNameGenPyi
23
from astbuilder.type_pointer import PointerKind
34
from astbuilder.type_scalar import TypeKind
45
from astbuilder.visitor import Visitor
@@ -22,13 +23,14 @@ class PyExtAccessorGen(Visitor):
2223
#
2324
#
2425

25-
def __init__(self, name, clsname, decl_pxd, pxd, pyx):
26+
def __init__(self, name, clsname, decl_pxd, pxd, pyx, pyi):
2627
super().__init__()
2728
self.name = name
2829
self.clsname = clsname
2930
self.pxd = pxd
3031
self.decl_pxd = decl_pxd
3132
self.pyx = pyx
33+
self.pyi = pyi
3234
self.field = None
3335
self.decl_pxd_ptr_tgen = PyExtTypeNameGen(
3436
ns=self.name,
@@ -44,6 +46,7 @@ def __init__(self, name, clsname, decl_pxd, pxd, pyx):
4446
is_pytype=False,
4547
is_ref=True,
4648
is_const=True)
49+
self.pyi_tgen = PyExtTypeNameGenPyi(ns=self.name)
4750
self.list_accessor_gen = PyExtListAccessorGen(
4851
name, clsname, decl_pxd, pxd, pyx)
4952

@@ -59,6 +62,8 @@ def visitTypeList(self, t):
5962
t,
6063
is_pydecl=False,
6164
is_pytype=False) + " get" + name + "();")
65+
self.pyi.println("def get%s(self) -> %s: ..." % (name, self.pyi_tgen.gen(t)))
66+
self.pyi.println()
6267

6368

6469
def visitTypeMap(self, t):
@@ -93,6 +98,9 @@ def visitTypePointer(self, t):
9398
else:
9499
raise Exception("Accessor generation not supported for " + str(self.pt))
95100

101+
self.pyi.println("def get%s(self) -> %s: ..." % (name, tname))
102+
self.pyi.println()
103+
96104
def visitAstClass(self, c):
97105
self.gen_class_accessors(c)
98106

@@ -178,6 +186,11 @@ def gen_enum_accessors(self, t):
178186
# Generate a non-const accessor
179187
self.decl_pxd.println("void set" + name + "(" +
180188
self.decl_pxd_ptr_tgen.gen(t) + " v)")
189+
190+
self.pyi.println("def set%s(self, v : %s): ..." % (
191+
name,
192+
self.pyi_tgen.gen(t)))
193+
self.pyi.println()
181194

182195
print("<-- gen_enum_accessors %s" % self.field.name)
183196

@@ -261,6 +274,9 @@ def gen_string_accessors(self, t):
261274
(self.name, self.clsname, name))
262275
self.pyx.dec_indent()
263276

277+
self.pyi.println("def get%s(self) -> str: ..." % name)
278+
self.pyi.println()
279+
264280
# Generate a setter
265281
self.decl_pxd.println("void set%s(%s v)" % (
266282
name,
@@ -278,6 +294,9 @@ def gen_string_accessors(self, t):
278294
self.name, self.clsname, name))
279295
self.pyx.dec_indent()
280296

297+
self.pyi.println("def set%s(self, v : str): ..." % name)
298+
self.pyi.println()
299+
281300
def const_ref_ret(self, t, is_pydecl=False, is_pytype=False):
282301
return PyExtTypeNameGen(ns=self.name,compressed=True,is_pydecl=is_pydecl,
283302
is_pytype=is_pytype, is_ret=True,is_const=True).gen(t)

src/astbuilder/pyext_gen_params.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
@author: mballance
55
'''
66
from astbuilder.pyext_type_name_gen import PyExtTypeNameGen
7+
from astbuilder.pyext_type_name_gen_pyi import PyExtTypeNameGenPyi
78
from astbuilder.type_userdef import TypeUserDef
89
from astbuilder.type_pointer import TypePointer
910
from astbuilder.ast_enum import AstEnum
@@ -60,6 +61,36 @@ def gen_ctor_params(cls,
6061

6162
return ret
6263

64+
@classmethod
65+
def gen_ctor_params_pyi(cls,
66+
ns,
67+
c,
68+
out):
69+
"""Returns True if this level (or a previous level) added content"""
70+
ret = False
71+
72+
if c.super is not None:
73+
# Recurse first
74+
ret |= cls.gen_ctor_params_pyi(ns, c.super.target, out)
75+
else:
76+
out.write("self")
77+
78+
ret = True
79+
80+
out.inc_indent()
81+
params = list(filter(lambda d : d.is_ctor, c.data))
82+
for i,p in enumerate(params):
83+
if i == 0:
84+
out.write(",\n")
85+
out.write(out.ind)
86+
out.write(p.name + " : ")
87+
out.write(PyExtTypeNameGenPyi(ns=ns).gen(p.t))
88+
out.write((",\n" if i+1 < len(params) else ""))
89+
ret = True
90+
out.dec_indent()
91+
92+
return ret
93+
6394
@classmethod
6495
def gen_ctor_pvals(cls, name, c, out):
6596
ret = False

src/astbuilder/pyext_gen_pyx.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ def gen(self, ast):
6363
self.pyx.println("cimport %s_decl" % self.name)
6464

6565
self.pyx.println("from enum import IntEnum")
66+
self.pyi.println("from enum import IntEnum, auto")
67+
self.pyi.println("from typing import Dict, List, Tuple")
6668

6769
for e in ast.enums:
6870
if self.namespace is not None:
@@ -77,6 +79,9 @@ def gen(self, ast):
7779

7880
self.pyx.println("class %s(IntEnum):" % e.name)
7981
self.pyx.inc_indent()
82+
83+
self.pyi.println("class %s(IntEnum):" % e.name)
84+
self.pyi.inc_indent()
8085

8186
for i,v in enumerate(e.values):
8287
if self.namespace is not None:
@@ -87,10 +92,13 @@ def gen(self, ast):
8792
e.name, v[0], e.name + "::" + v[0]))
8893
self.pyx.println("%s = %s_decl.%s.%s_%s" % (
8994
v[0], self.name, e.name, e.name, v[0]))
95+
self.pyi.println("%s = auto()" % v[0])
96+
self.pyi.println()
9097

9198
self.decl_pxd.dec_indent()
9299
self.decl_pxd.dec_indent()
93100
self.pyx.dec_indent()
101+
self.pyi.dec_indent()
94102

95103
for e in ast.structs:
96104
if self.namespace is not None:
@@ -124,6 +132,9 @@ def gen(self, ast):
124132

125133
self.pyx.println("class %s(IntEnum):" % e.name)
126134
self.pyx.inc_indent()
135+
136+
self.pyi.println("class %s(IntEnum):" % e.name)
137+
self.pyi.inc_indent()
127138

128139
for i,v in enumerate(e.values):
129140
if self.namespace is not None:
@@ -134,10 +145,13 @@ def gen(self, ast):
134145
e.name, v, e.name + "::" + v))
135146
self.pyx.println("%s = %s_decl.%s.%s_%s" % (
136147
v, self.name, e.name, e.name, v))
148+
self.pyi.println("%s = auto()" % v)
149+
self.pyi.println()
137150

138151
self.decl_pxd.dec_indent()
139152
self.decl_pxd.dec_indent()
140153
self.pyx.dec_indent()
154+
self.pyi.dec_indent()
141155

142156
self.gen_factory_decl()
143157
self.gen_factory()
@@ -211,8 +225,10 @@ def gen_factory(self):
211225
self.pyx.println("cdef Factory _inst = None")
212226
self.pxd.println("cdef class Factory(object):")
213227
self.pyx.println("cdef class Factory(object):")
228+
self.pyi.println("class Factory(object):")
214229
self.pxd.inc_indent()
215230
self.pyx.inc_indent()
231+
self.pyi.inc_indent()
216232
self.pxd.println("cdef %s_decl.IFactory *_hndl" % (self.name,))
217233

218234
for c in self.ast.classes:
@@ -221,6 +237,9 @@ def gen_factory(self):
221237
PyExtGenParams.gen_ctor_params(
222238
self.name, c, self.pxd, is_pydecl=True, is_pytype=True, ins_self=True)
223239
self.pxd.write(")\n")
240+
self.pyi.write("%sdef mk%s(" % (self.pyi.ind, name))
241+
PyExtGenParams.gen_ctor_params_pyi(self.name, c, self.pyi)
242+
self.pyi.write(") -> '%s': ...\n" % c.name)
224243

225244
self.pyx.print("cpdef %s mk%s(" % (c.name, name))
226245
self.pyx.inc_indent(2)
@@ -287,9 +306,14 @@ def gen_factory(self):
287306

288307
self.pyx.println("return _inst")
289308
self.pyx.dec_indent()
309+
310+
self.pyi.println("@staticmethod")
311+
self.pyi.println("def inst() -> 'Factory': ...")
312+
self.pyi.println()
290313

291314
self.pxd.dec_indent()
292315
self.pyx.dec_indent()
316+
self.pyi.dec_indent()
293317

294318
def visitAstClass(self, c : AstClass):
295319

@@ -317,20 +341,27 @@ def visitAstClass(self, c : AstClass):
317341
c.name, PyExtTypeNameGen(ns=self.name,is_pytype=True).gen(c.super)))
318342
self.pxd.println("cdef class %s(%s):" % (
319343
c.name, PyExtTypeNameGen(self.name, is_pytype=True).gen(c.super)))
344+
self.pyi.println("class %s(%s):" % (
345+
c.name, PyExtTypeNameGen(self.name, is_pytype=True).gen(c.super)))
320346
else:
321347
self.pyx.println("cdef class %s(object):" % c.name)
322348
self.pxd.println("cdef class %s(object):" % c.name)
349+
self.pyi.println("class %s(object):" % c.name)
323350

324351

325352
self.pyx.inc_indent()
326353
self.pxd.inc_indent()
354+
self.pyi.inc_indent()
355+
356+
self.pyi.println("pass")
327357

328358
if c.super is None:
329359
self.pxd.println("cdef %s_decl.I%s *_hndl" % (self.name, c.name))
330360
self.pxd.println("cdef bool _owned")
331361

332362
self.pyx.println()
333363
self.pxd.println()
364+
self.pyi.println()
334365

335366
if c.super is None:
336367
self.pyx.println("def __dealloc__(self):")
@@ -406,7 +437,7 @@ def visitAstClass(self, c : AstClass):
406437
# self.pyx.println()
407438

408439
for d in c.data:
409-
PyExtAccessorGen(self.name, c.name, self.decl_pxd, self.pxd, self.pyx).gen(d)
440+
PyExtAccessorGen(self.name, c.name, self.decl_pxd, self.pxd, self.pyx, self.pyi).gen(d)
410441

411442
if len(c.data) == 0:
412443
self.decl_pxd.println("pass")
@@ -421,6 +452,7 @@ def visitAstClass(self, c : AstClass):
421452
self.decl_pxd.dec_indent()
422453
self.pyx.dec_indent()
423454
self.pxd.dec_indent()
455+
self.pyi.dec_indent()
424456

425457
self.decl_pxd.println()
426458
self.pyx.println()
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
2+
from astbuilder.type_pointer import PointerKind
3+
from astbuilder.type_scalar import TypeScalar, TypeKind
4+
5+
from .type_pointer import TypePointer
6+
from .visitor import Visitor
7+
from astbuilder.ast_enum import AstEnum
8+
from astbuilder.ast_flags import AstFlags
9+
from astbuilder.ast_struct import AstStruct
10+
11+
12+
class PyExtTypeNameGenPyi(Visitor):
13+
14+
def __init__(self, ns, is_ret=False):
15+
self.out = ""
16+
self.ns = ns
17+
self.is_ret = is_ret
18+
self.depth = 0
19+
20+
def gen(self, t):
21+
self.out = ""
22+
t.accept(self)
23+
return self.out
24+
25+
def visitAstEnum(self, e : AstEnum):
26+
# print("is_pydecl=%s is_pytype=%s" % (self.is_pydecl, self.is_pytype))
27+
self.out += e.name
28+
29+
def visitAstFlags(self, f : AstFlags):
30+
self.out += f.name
31+
32+
def visitTypeList(self, t):
33+
if self.depth == 0:
34+
self.depth += 1
35+
36+
self.out += "List["
37+
self.out += PyExtTypeNameGenPyi(ns=self.ns).gen(t.t)
38+
self.out += "]"
39+
40+
self.depth -= 1
41+
else:
42+
self.out += PyExtTypeNameGenPyi(ns=self.ns).gen(t)
43+
44+
def visitTypeMap(self, t):
45+
if self.depth == 0:
46+
self.depth += 1
47+
48+
self.out += "Dict["
49+
self.out += PyExtTypeNameGenPyi(ns=self.ns).gen(t.kt)
50+
self.out += ","
51+
self.out += PyExtTypeNameGenPyi(ns=self.ns).gen(t.vt)
52+
self.out += "]"
53+
54+
self.depth -= 1
55+
else:
56+
self.out += PyExtTypeNameGenPyi(ns=self.ns).gen(t)
57+
58+
def visitTypePointer(self, t : TypePointer):
59+
if self.depth == 0:
60+
self.depth += 1
61+
Visitor.visitTypePointer(self, t)
62+
self.depth -= 1
63+
else:
64+
self.out += PyExtTypeNameGenPyi(ns=self.ns).gen(t)
65+
66+
def visitTypeScalar(self, t : TypeScalar):
67+
vmap = {
68+
TypeKind.String : "str",
69+
TypeKind.Bool : "bool",
70+
TypeKind.Int8: "int",
71+
TypeKind.Uint8: "int",
72+
TypeKind.Int16: "int",
73+
TypeKind.Uint16: "int",
74+
TypeKind.Int32: "int",
75+
TypeKind.Uint32: "int",
76+
TypeKind.Int64: "int",
77+
TypeKind.Uint64: "int",
78+
}
79+
self.out += vmap[t.t]
80+
81+
def visitAstStruct(self, s : AstStruct):
82+
self.out += s.name
83+
84+
def visitTypeUserDef(self, t):
85+
if isinstance(t.target, (AstEnum,AstFlags)):
86+
t.target.accept(self)
87+
elif isinstance(t.target, (AstStruct,)):
88+
self.out += "%s" % t.name
89+
else:
90+
self.out += "%s" % t.name

0 commit comments

Comments
 (0)