948 lines
31 KiB
Python
Executable File
948 lines
31 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import os
|
|
import textwrap
|
|
import keyword
|
|
from traceback import format_exc
|
|
|
|
from collections import Counter
|
|
|
|
from argparse import ArgumentParser
|
|
parser = ArgumentParser(description="Python generator for eolian")
|
|
parser.add_argument('-v', '--verbose', action="count")
|
|
parser.add_argument('-o', '--output', default="/tmp", help="max is -vvv")
|
|
parser.add_argument('paths', nargs="+")
|
|
args = parser.parse_args()
|
|
|
|
import logging
|
|
handler = logging.StreamHandler()
|
|
formatter = logging.Formatter("%(name)s %(levelname)s: %(message)s")
|
|
handler.setFormatter(formatter)
|
|
log = logging.getLogger("efl.eolian")
|
|
log.addHandler(handler)
|
|
|
|
level = logging.ERROR
|
|
if args.verbose:
|
|
level -= 10 * args.verbose
|
|
log.setLevel(level)
|
|
|
|
from efl import eolian
|
|
eolian.init()
|
|
|
|
DOCSTRINGS_ENABLED = True
|
|
|
|
import re
|
|
|
|
enum_re = re.compile(r"typedef\s+enum\s*{([^}]*)}([^;]+);")
|
|
memb_re = re.compile(
|
|
r"\n +(?P<value>\w+)[(), =<\d\-+]*([/*<]* (?P<comment>[^/]+)? ?\*/)?")
|
|
|
|
docstring_replacements = (
|
|
("@brief ", ""),
|
|
(re.compile(r"@ingroup .+", re.S), r""),
|
|
(re.compile(r"@see (.+)(?!@)"), r":see: \1"),
|
|
("@return ", ":return: "),
|
|
(re.compile(r"@(note|warning) "), r".. \1:: "),
|
|
(re.compile(r"@(p|c) (\w+)"), r"``\2``"),
|
|
(re.compile(r"@(b) (\w+)"), r"**\2**"),
|
|
(re.compile(r"@(i) (\w+)"), r"*\2*"),
|
|
("EINA_TRUE", "True"),
|
|
("EINA_FALSE", "False"),
|
|
("NULL", "None"),
|
|
)
|
|
|
|
complex_types = (
|
|
"Eina_List"
|
|
)
|
|
|
|
param_type_mapping = {
|
|
# c_type: (py_type, conversion, c_param_convert)
|
|
"int": (None, None, None),
|
|
"short": (None, None, None),
|
|
"double": (None, None, None),
|
|
"Evas_Coord": ("int", None, None),
|
|
"Eina_Bool": ("bint", None, None),
|
|
"char *": (
|
|
"",
|
|
"if isinstance({0}, unicode): {0} = PyUnicode_AsUTF8String({0})",
|
|
"<{1}>if {0} is not None else NULL"
|
|
),
|
|
"Evas_Object *": ("_Eo_Base", None, "{0}.obj"),
|
|
"Eo *": ("_Eo_Base", None, "{0}.obj"),
|
|
}
|
|
|
|
return_type_mapping = {
|
|
# c_type: (py_type, conversion)
|
|
"int": (None, None),
|
|
"short": (None, None, None),
|
|
"double": (None, None),
|
|
"Evas_Coord": ("int", None),
|
|
"Eina_Bool": ("bint", None),
|
|
"char *": ("unicode", '{0}.decode("utf-8")'),
|
|
"Elm_Object_Item *": (
|
|
"_ObjectItem", 'object_item_to_python({0})'
|
|
),
|
|
"Evas_Object *": ("_Eo_Base", 'object_from_instance({0})'),
|
|
"Eo *": ("_Eo_Base", 'object_from_instance({0})'),
|
|
}
|
|
|
|
anon_enums = [] # (filename, enum_members), ...
|
|
enums = {} # enum_name: (filename, enum_members), ...
|
|
|
|
|
|
def conv_cls_name(name):
|
|
s = name.split("_")
|
|
if len(s) > 1:
|
|
return s[0], "_".join(s[1:])
|
|
else:
|
|
return name, name
|
|
|
|
|
|
def remove_type_prefixes(ctype):
|
|
for t in "const ", "unsigned ", "short ":
|
|
ctype = ctype.replace(t, "")
|
|
return ctype
|
|
|
|
|
|
class Generator(object):
|
|
|
|
tab = " "
|
|
|
|
def __init__(self):
|
|
self.result = []
|
|
self.level = 0
|
|
|
|
def indent(self):
|
|
self.level += 1
|
|
|
|
def dedent(self):
|
|
self.level -= 1
|
|
|
|
def write(self, text=None, wrapped=False, i_ind=None, s_ind=None):
|
|
if not i_ind:
|
|
i_ind = self.level * self.tab
|
|
if not s_ind:
|
|
s_ind = self.level * self.tab
|
|
if text:
|
|
if wrapped:
|
|
self.result.append(textwrap.fill(
|
|
text,
|
|
initial_indent=i_ind,
|
|
subsequent_indent=s_ind,
|
|
width=79
|
|
))
|
|
else:
|
|
self.result.append(i_ind + text)
|
|
else:
|
|
self.result.append("")
|
|
|
|
def docstring_write(self, docstrings):
|
|
if not DOCSTRINGS_ENABLED or not docstrings:
|
|
return
|
|
elif len(docstrings) == 1:
|
|
self.write('"""' + docstrings[0] + '"""')
|
|
else:
|
|
self.write('"""')
|
|
for docs in docstrings:
|
|
if docs.startswith("- ") or \
|
|
docs.startswith(".. ") or \
|
|
docs.startswith(":"):
|
|
self.write(
|
|
docs, wrapped=True,
|
|
s_ind=self.level * self.tab + " "
|
|
)
|
|
elif docs.startswith(" - "):
|
|
self.write(
|
|
docs, wrapped=True,
|
|
s_ind=self.level * self.tab + " "
|
|
)
|
|
else:
|
|
self.write(docs, wrapped=True)
|
|
self.write('"""')
|
|
|
|
def printout(self):
|
|
return "\n".join(self.result)
|
|
|
|
def write_enum(self, name):
|
|
filename, members = enums[name]
|
|
self.write("from efl.utils.enum import IntEnum")
|
|
self.write("# File: " + filename)
|
|
self.write("class %s(IntEnum):" % (name))
|
|
self.indent()
|
|
for value, comment in members:
|
|
self.write("%s # %s" % (value, comment))
|
|
self.dedent()
|
|
self.write()
|
|
|
|
|
|
class PyxGenerator(Generator):
|
|
def method_header_write(self, name, params, clsm=False):
|
|
params2 = []
|
|
if clsm:
|
|
self.write("@classmethod")
|
|
params2.append("cls")
|
|
else:
|
|
params2.append("self")
|
|
|
|
for t, n in params:
|
|
if t in param_type_mapping:
|
|
c_t = t.replace("const ", "").replace("unsigned ", "")
|
|
py_t = param_type_mapping[c_t][0]
|
|
if py_t is not None:
|
|
t = py_t
|
|
params2.append(" ".join((t, n)).strip())
|
|
|
|
params2 = ", ".join(params2)
|
|
define = "def %s(%s):" % (name, params2)
|
|
self.write(define)
|
|
|
|
def cdefs_write(self, cdefs):
|
|
self.write("cdef:")
|
|
self.indent()
|
|
types = {}
|
|
for t, n in cdefs:
|
|
types.setdefault(t, []).append(n)
|
|
for t, names in types.items():
|
|
if "*" in t:
|
|
# complex, write one per line
|
|
for n in names:
|
|
self.write("%s %s" % (t, n))
|
|
else:
|
|
# simple, all on one line
|
|
self.write("%s %s" % (t, ", ".join(names)))
|
|
self.dedent()
|
|
|
|
def c_call_write(self, name, params, ret_type, ret_param, legacy=False):
|
|
if legacy:
|
|
c_call = "%s(%s)" % (name, ", ".join(("self.obj", params)))
|
|
else:
|
|
c_call = "eo_do(self.obj, %s(%s))" % (name, params)
|
|
if ret_type:
|
|
r_type = ret_type
|
|
if r_type in return_type_mapping:
|
|
conv = return_type_mapping[r_type][1]
|
|
if conv:
|
|
c_call = conv.format(c_call)
|
|
c_call = ret_param + " = " + c_call
|
|
self.write(c_call)
|
|
|
|
|
|
class Method(object):
|
|
|
|
def __init__(self, eo_prefix, legacy_prefix):
|
|
self.params = []
|
|
self.py_params = []
|
|
self.c_params = []
|
|
self.cdefs = []
|
|
self.returns = []
|
|
self.docs = []
|
|
self.enum_types = []
|
|
self.ret_type = None
|
|
self.py_name = None
|
|
self.eo_prefix = eo_prefix
|
|
self.legacy_prefix = legacy_prefix
|
|
|
|
@classmethod
|
|
def parse(cls, func, eo_prefix, legacy_prefix):
|
|
self = cls.__new__(cls)
|
|
self.__init__(eo_prefix, legacy_prefix)
|
|
|
|
self.py_name = func.name
|
|
if keyword.iskeyword(self.py_name):
|
|
self.py_name += "_"
|
|
|
|
if self.eo_prefix:
|
|
self.c_name = func.full_c_name_get(self.eo_prefix)
|
|
elif self.legacy_prefix:
|
|
self.c_name = func.full_c_name_get(self.legacy_prefix)
|
|
else:
|
|
raise ValueError("No prefix found for function %r" % (func))
|
|
|
|
func_desc = func.description_get("comment")
|
|
if func_desc:
|
|
func_desc = func_desc.split("\n\n")
|
|
for desc in func_desc:
|
|
for pat, repl in docstring_replacements:
|
|
if isinstance(pat, basestring):
|
|
desc = desc.replace(pat, repl)
|
|
else:
|
|
desc = pat.sub(repl, desc)
|
|
if desc:
|
|
self.docs.append(desc)
|
|
self.docs.append("")
|
|
|
|
for p in func.parameters_list:
|
|
pdir, ptype, name, desc = p.information
|
|
ptype2 = remove_type_prefixes(ptype)
|
|
if not ptype2 in param_type_mapping:
|
|
if not ptype2 in enums:
|
|
raise TypeError("Unknown param type: %s" % (ptype2))
|
|
else:
|
|
self.enum_types.append(ptype2)
|
|
enums_counter[ptype2] += 1
|
|
self.params.append((ptype, name))
|
|
if pdir == eolian.ParameterDir.IN:
|
|
self.py_params.append((ptype, name))
|
|
self.c_params.append((ptype, name))
|
|
self.docs.append(":param %s: %s" % (name, desc))
|
|
self.docs.append(":type %s: %s" % (name, ptype))
|
|
elif pdir == eolian.ParameterDir.OUT:
|
|
self.cdefs.append((ptype, name))
|
|
self.c_params.append((ptype, "&%s" % (name)))
|
|
self.returns.append((ptype, name))
|
|
else:
|
|
self.py_params.append((ptype, name))
|
|
self.c_params.append((ptype, name))
|
|
self.returns.append((ptype, name))
|
|
|
|
ret_type = func.return_type_get(func.type)
|
|
if ret_type:
|
|
ret_type2 = remove_type_prefixes(ret_type)
|
|
if not ret_type2 in return_type_mapping:
|
|
if not ret_type2 in enums:
|
|
raise TypeError("Unknown return type: %s" % (ret_type2))
|
|
else:
|
|
self.enum_types.append(ret_type2)
|
|
enums_counter[ret_type2] += 1
|
|
self.ret_type = ret_type
|
|
ret_desc = func.return_comment_get(func.type)
|
|
|
|
if self.returns:
|
|
self.docs.append(
|
|
":return: " + ", ".join([r[1] for r in self.returns]))
|
|
self.docs.append(
|
|
":rtype: " + ", ".join([r[0] for r in self.returns]))
|
|
elif self.ret_type:
|
|
if ret_desc:
|
|
self.docs.append(":return: " + ret_desc)
|
|
self.docs.append(":rtype: " + self.ret_type)
|
|
|
|
return self
|
|
|
|
def pyx_generate(self, gen, param_conv=None):
|
|
gen.method_header_write(self.py_name, self.py_params)
|
|
gen.indent()
|
|
|
|
RET_PARAM = "py_efl_ret"
|
|
|
|
if self.docs:
|
|
gen.docstring_write(self.docs)
|
|
|
|
if self.ret_type:
|
|
t = self.ret_type
|
|
if t in return_type_mapping:
|
|
c_t = remove_type_prefixes(t)
|
|
py_t = return_type_mapping[c_t][0]
|
|
if py_t is not None:
|
|
t = py_t
|
|
self.cdefs.append((t, RET_PARAM))
|
|
|
|
if self.cdefs:
|
|
gen.cdefs_write(self.cdefs)
|
|
|
|
conv = getattr(self, "params_conv", None)
|
|
if conv:
|
|
gen.write(conv)
|
|
|
|
for t, n in self.py_params:
|
|
t = remove_type_prefixes(t)
|
|
if t in param_type_mapping:
|
|
conv = param_type_mapping[t][1]
|
|
if conv:
|
|
gen.write(conv.format(n, t))
|
|
|
|
for i, (t, n) in enumerate(self.c_params):
|
|
t2 = remove_type_prefixes(t)
|
|
if t2 in param_type_mapping:
|
|
conv = param_type_mapping[t2][2]
|
|
if conv:
|
|
self.c_params[i] = (t, conv.format(n, t))
|
|
|
|
c_params = ", ".join([c[1] for c in self.c_params])
|
|
if self.eo_prefix:
|
|
gen.c_call_write(
|
|
self.c_name, c_params, self.ret_type, RET_PARAM, legacy=False
|
|
)
|
|
elif self.legacy_prefix:
|
|
gen.c_call_write(
|
|
self.c_name, c_params, self.ret_type, RET_PARAM, legacy=True
|
|
)
|
|
else:
|
|
raise ValueError("no prefix found for %r" % (self.name))
|
|
|
|
if self.returns:
|
|
ret = self.returns[:]
|
|
for i, (t, n) in enumerate(self.returns):
|
|
t = remove_type_prefixes(t)
|
|
if t in return_type_mapping:
|
|
conv = return_type_mapping[t][1]
|
|
if conv:
|
|
gen.write("conv_%s = %s" % (n, conv.format(n)))
|
|
ret[i] = t, "conv_%s" % (n)
|
|
gen.write("return %s" % (", ".join([r[1] for r in ret])))
|
|
elif self.ret_type:
|
|
ret = RET_PARAM
|
|
r_type = self.ret_type
|
|
if r_type in return_type_mapping:
|
|
conv = return_type_mapping[r_type][1]
|
|
if conv:
|
|
ret = conv.format(ret)
|
|
gen.write("return %s" % (ret))
|
|
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
def cdef_generate(self, gen):
|
|
rtype = self.ret_type
|
|
if not rtype:
|
|
rtype = "void"
|
|
line = " ".join((rtype, self.c_name))
|
|
line += "("
|
|
line += ", ".join([" ".join((t, n)) for t, n in self.params])
|
|
line += ")"
|
|
|
|
gen.write(line)
|
|
|
|
|
|
class Property(object):
|
|
def __init__(self, eo_prefix, legacy_prefix):
|
|
super(type(self), self).__init__()
|
|
self.docs = []
|
|
self.eo_prefix = eo_prefix
|
|
self.enum_types = []
|
|
|
|
@classmethod
|
|
def parse(cls, func, eo_prefix, legacy_prefix):
|
|
self = cls.__new__(cls)
|
|
self.__init__(eo_prefix, legacy_prefix)
|
|
|
|
self.py_name = func.name
|
|
if keyword.iskeyword(self.py_name):
|
|
self.py_name += "_"
|
|
|
|
getter_desc = func.description_get("comment_get")
|
|
setter_desc = func.description_get("comment_set")
|
|
for func_desc in getter_desc, setter_desc:
|
|
if func_desc:
|
|
func_desc = func_desc.split("\n\n")
|
|
for desc in func_desc:
|
|
for pat, repl in docstring_replacements:
|
|
if isinstance(pat, basestring):
|
|
desc = desc.replace(pat, repl)
|
|
else:
|
|
desc = pat.sub(repl, desc)
|
|
if desc:
|
|
self.docs.append(desc)
|
|
self.docs.append("")
|
|
|
|
self.getter = None
|
|
self.setter = None
|
|
|
|
for param in func.property_values_list:
|
|
self.docs.append(":type %s: %s" % (param.name, param.type))
|
|
|
|
if func.type == eolian.FunctionType.PROP_SET or \
|
|
func.type == eolian.FunctionType.PROPERTY:
|
|
# Create setter
|
|
m = self.setter = Method(eo_prefix, legacy_prefix)
|
|
m.py_name = "__set__"
|
|
if eo_prefix:
|
|
fname = func.full_c_name_get(eo_prefix)
|
|
m.c_name = "_".join((fname, "set"))
|
|
elif legacy_prefix:
|
|
fname = func.full_c_name_get(legacy_prefix)
|
|
m.c_name = "_".join((fname, "set"))
|
|
else:
|
|
raise ValueError("No prefix found for %r" % (func))
|
|
py_params = []
|
|
for p in func.property_values_list:
|
|
pdir, ptype, name, desc = p.information
|
|
assert pdir == eolian.ParameterDir.IN, "prop has other than IN"
|
|
ptype2 = remove_type_prefixes(ptype)
|
|
if not ptype2 in param_type_mapping:
|
|
if not ptype2 in enums:
|
|
raise TypeError("Unknown param type: %s" % (ptype2))
|
|
else:
|
|
self.enum_types.append(ptype2)
|
|
enums_counter[ptype2] += 1
|
|
m.params.append((ptype, name))
|
|
py_params.append(name)
|
|
m.c_params.append((ptype, name))
|
|
|
|
assert py_params, (
|
|
"params should not be empty for setter of: %s" % (func.name)
|
|
)
|
|
py_params = ", ".join(py_params)
|
|
m.params_conv = "%s = value" % (py_params)
|
|
|
|
m.py_params = (("", "value"),)
|
|
|
|
m.ret_type = func.return_type_get(eolian.FunctionType.PROP_SET)
|
|
if m.ret_type:
|
|
ret_type2 = remove_type_prefixes(m.ret_type)
|
|
if not ret_type2 in return_type_mapping:
|
|
if not ret_type2 in enums:
|
|
raise TypeError(
|
|
"Unknown return type: %s" % (ret_type2))
|
|
else:
|
|
self.enum_types.append(ret_type2)
|
|
enums_counter[ret_type2] += 1
|
|
|
|
if func.type == eolian.FunctionType.PROP_GET or \
|
|
func.type == eolian.FunctionType.PROPERTY:
|
|
# Create getter
|
|
m = self.getter = Method(eo_prefix, legacy_prefix)
|
|
m.py_name = "__get__"
|
|
if eo_prefix:
|
|
fname = func.full_c_name_get(eo_prefix)
|
|
m.c_name = "_".join((fname, "get"))
|
|
elif legacy_prefix:
|
|
fname = func.full_c_name_get(legacy_prefix)
|
|
m.c_name = "_".join((fname, "get"))
|
|
else:
|
|
raise ValueError("No prefix found for %r" % (func))
|
|
for p in func.property_values_list:
|
|
pdir, ptype, name, desc = p.information
|
|
assert pdir == eolian.ParameterDir.IN, "prop has other than IN"
|
|
ptype2 = remove_type_prefixes(ptype)
|
|
if not ptype2 in param_type_mapping:
|
|
if not ptype2 in enums:
|
|
raise TypeError("Unknown param type: %s" % (ptype2))
|
|
else:
|
|
self.enum_types.append(ptype2)
|
|
enums_counter[ptype2] += 1
|
|
m.params.append((ptype, name))
|
|
m.cdefs.append((ptype, name))
|
|
m.c_params.append((ptype, "&%s" % (name)))
|
|
m.returns.append((ptype, name))
|
|
|
|
m.ret_type = func.return_type_get(eolian.FunctionType.PROP_GET)
|
|
if m.ret_type:
|
|
ret_type2 = remove_type_prefixes(m.ret_type)
|
|
if not ret_type2 in return_type_mapping:
|
|
if not ret_type2 in enums:
|
|
raise TypeError(
|
|
"Unknown return type: %s" % (ret_type2))
|
|
else:
|
|
self.enum_types.append(ret_type2)
|
|
enums_counter[ret_type2] += 1
|
|
|
|
return self
|
|
|
|
def pyx_generate(self, gen):
|
|
if self.enum_types:
|
|
for t in set(self.enum_types):
|
|
gen.write_enum(t)
|
|
|
|
gen.write("property %s:" % (self.py_name))
|
|
|
|
gen.indent()
|
|
gen.docstring_write(self.docs)
|
|
|
|
if self.setter:
|
|
self.setter.pyx_generate(gen)
|
|
if self.getter:
|
|
self.getter.pyx_generate(gen)
|
|
|
|
gen.dedent()
|
|
|
|
def cdef_generate(self, gen):
|
|
if self.setter:
|
|
self.setter.cdef_generate(gen)
|
|
if self.getter:
|
|
self.getter.cdef_generate(gen)
|
|
|
|
|
|
class EoConstructor(Method):
|
|
|
|
def pyx_generate(self, gen):
|
|
cls_get = self.eo_prefix + "_class_get()"
|
|
|
|
gen.method_header_write(
|
|
self.py_name, [("", "parent")] + self.py_params, clsm=True)
|
|
|
|
gen.indent()
|
|
|
|
gen.docstring_write(self.docs)
|
|
|
|
c_params = ", ".join([c[1] for c in self.c_params])
|
|
gen.write("self = cls.__new__(cls)")
|
|
gen.write(
|
|
"cdef Eo *obj = eo_add_custom"
|
|
"(%s, parent.obj if parent is not None else NULL, %s(%s))" % (
|
|
cls_get, self.c_name, c_params
|
|
)
|
|
)
|
|
gen.write("self._set_obj(obj)")
|
|
gen.write("return self")
|
|
gen.dedent()
|
|
|
|
gen.write()
|
|
|
|
|
|
class EoDefaultConstructor(EoConstructor):
|
|
|
|
def pyx_generate(self, gen):
|
|
cls_get = self.eo_prefix + "_class_get()"
|
|
gen.write("def __init__(self, parent=None):")
|
|
gen.indent()
|
|
gen.write(
|
|
"cdef Eo *obj = eo_add"
|
|
"(%s, parent.obj if parent is not None else NULL)" % (
|
|
cls_get
|
|
)
|
|
)
|
|
gen.write("self._set_obj(obj)")
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
def cdef_generate(self, gen):
|
|
pass
|
|
|
|
|
|
class LegacyDefaultConstructor(Method):
|
|
|
|
def pyx_generate(self, gen):
|
|
call = self.legacy_prefix + "_add"
|
|
gen.write("def __init__(self, parent=None):")
|
|
gen.indent()
|
|
gen.write(
|
|
"cdef Eo *obj = %s"
|
|
"(parent.obj if parent is not None else NULL)" % (
|
|
call
|
|
)
|
|
)
|
|
gen.write("self._set_obj(obj)")
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
|
|
class Class(object):
|
|
def __init__(self):
|
|
self.docs = []
|
|
self.inherits = []
|
|
|
|
self.ctors = []
|
|
self.methods = []
|
|
self.props = []
|
|
self.default_ctor = None
|
|
self.header_path = None
|
|
|
|
@classmethod
|
|
def parse(klass, cls):
|
|
self = klass.__new__(klass)
|
|
self.__init__()
|
|
|
|
class_counter[cls.name] += 1
|
|
|
|
self.c_name = cls.name
|
|
self.lib_name, self.name = conv_cls_name(cls.name)
|
|
|
|
header_path = cls.filename
|
|
if not header_path:
|
|
log.error("Class %s has no filename" % (cls.name))
|
|
return
|
|
header_path = os.path.basename(header_path + ".h")
|
|
self.header_path = header_path
|
|
|
|
self.inherits = cls.inherits_list
|
|
|
|
desc = cls.description
|
|
if desc:
|
|
descs = desc.split("\n\n")
|
|
for desc in descs:
|
|
for pat, repl in docstring_replacements:
|
|
if isinstance(pat, basestring):
|
|
desc = desc.replace(pat, repl)
|
|
else:
|
|
desc = pat.sub(repl, desc)
|
|
if desc:
|
|
self.docs.append(desc)
|
|
self.docs.append("")
|
|
|
|
for k, v in (
|
|
type(cls).__dict__.items()
|
|
):
|
|
if k.startswith("__") or hasattr(v, "__call__") or \
|
|
k == "description":
|
|
continue
|
|
|
|
if k == "events" or k == "implements" or k == "inherits_list":
|
|
objs = v.__get__(cls)
|
|
if not objs:
|
|
continue
|
|
self.docs.append(
|
|
"- %s: " % (k)
|
|
)
|
|
for obj in objs:
|
|
self.docs.append(
|
|
" - %s" % (obj)
|
|
)
|
|
else:
|
|
self.docs.append(
|
|
"- %s: %s" % (k, v.__get__(cls))
|
|
)
|
|
|
|
ctors = None
|
|
eo_prefix = self.eo_prefix = cls.eo_prefix
|
|
legacy_prefix = self.legacy_prefix = cls.legacy_prefix
|
|
|
|
if eo_prefix and eo_prefix != "null":
|
|
prefix = eo_prefix
|
|
elif legacy_prefix and legacy_prefix != "null":
|
|
log.warn("Class %s has legacy prefix" % (cls.name))
|
|
prefix = legacy_prefix
|
|
else:
|
|
log.warn("Class %s has no prefix!" % (cls.name))
|
|
nspaces = cls.namespaces_list
|
|
nspaces.append(cls.name.lower())
|
|
prefix = eo_prefix = self.eo_prefix = "_".join(nspaces)
|
|
|
|
if cls.type == eolian.ClassType.REGULAR: # \
|
|
#or cls.type == eolian.ClassType.MIXIN:
|
|
ctors = cls.functions_list_get(eolian.FunctionType.CTOR)
|
|
if ctors:
|
|
for ctor in ctors:
|
|
function_counter["_".join((prefix, ctor.name))] += 1
|
|
if eo_prefix:
|
|
try:
|
|
o = EoConstructor.parse(ctor, eo_prefix, None)
|
|
except Exception as e:
|
|
log.warn(
|
|
"Skipping %s.%s because of an exception"
|
|
% (cls.name, ctor.name))
|
|
log.warn(e)
|
|
log.debug(format_exc())
|
|
continue
|
|
else:
|
|
log.error("legacy custom constructors not supported")
|
|
continue
|
|
self.ctors.append(o)
|
|
generated_function_counter[
|
|
"_".join((prefix, ctor.name))] += 1
|
|
if eo_prefix:
|
|
self.default_ctor = EoDefaultConstructor(eo_prefix, None)
|
|
else:
|
|
self.default_ctor = LegacyDefaultConstructor(
|
|
None, legacy_prefix)
|
|
|
|
props = cls.functions_list_get(eolian.FunctionType.PROPERTY)
|
|
for prop in props:
|
|
function_counter["_".join((prefix, prop.name))] += 1
|
|
try:
|
|
o = Property.parse(prop, eo_prefix, legacy_prefix)
|
|
except Exception as e:
|
|
log.warn(
|
|
"Skipping %s.%s because of an exception"
|
|
% (cls.name, prop.name))
|
|
log.warn(e)
|
|
log.debug(format_exc())
|
|
continue
|
|
self.props.append(o)
|
|
generated_function_counter["_".join((prefix, prop.name))] += 1
|
|
|
|
methods = cls.functions_list_get(eolian.FunctionType.METHOD)
|
|
for method in methods:
|
|
function_counter["_".join((prefix, method.name))] += 1
|
|
try:
|
|
o = Method.parse(method, eo_prefix, legacy_prefix)
|
|
except Exception as e:
|
|
log.warn(
|
|
"Skipping %s.%s because of an exception"
|
|
% (cls.name, method.name))
|
|
log.warn(e)
|
|
log.debug(format_exc())
|
|
continue
|
|
self.methods.append(o)
|
|
generated_function_counter["_".join((prefix, method.name))] += 1
|
|
|
|
generated_class_counter[cls.name] += 1
|
|
|
|
return self
|
|
|
|
def pyx_generate(self, gen):
|
|
if self.ctors or self.methods or self.props:
|
|
gen.write('cdef extern from "%s":' % (self.header_path))
|
|
gen.indent()
|
|
for o in self.ctors + self.methods + self.props:
|
|
o.cdef_generate(gen)
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
inherits = []
|
|
for i in self.inherits:
|
|
i_cls = eolian.class_find_by_name(i)
|
|
if i_cls:
|
|
if i_cls.type == eolian.ClassType.INTERFACE or \
|
|
i_cls.type == eolian.ClassType.MIXIN:
|
|
continue
|
|
else:
|
|
log.warn("Class %s is unknown" % (i))
|
|
inherits.append("_" + i)
|
|
assert len(inherits) < 2, (
|
|
"Multiple inheritance is not supported in extension classes.\n"
|
|
"Class: %r, inherits: %r" % (cls, inherits))
|
|
|
|
if not inherits:
|
|
inherits = ("object", )
|
|
inherits = ", ".join(inherits)
|
|
|
|
gen.write("cdef class _%s(%s):" % (self.name, inherits))
|
|
gen.indent()
|
|
|
|
gen.docstring_write(self.docs)
|
|
|
|
if not self.ctors and not self.props and not self.methods and \
|
|
not self.default_ctor:
|
|
gen.write("pass")
|
|
else:
|
|
for o in (
|
|
[self.default_ctor] +
|
|
self.ctors +
|
|
self.methods +
|
|
self.props
|
|
):
|
|
if o:
|
|
o.pyx_generate(gen)
|
|
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
def py_generate(self, gen):
|
|
inherits = ["_" + self.name]
|
|
imports = [(self.lib_name, "_" + self.name)]
|
|
for i in self.inherits:
|
|
i_cls = eolian.class_find_by_name(i)
|
|
if i_cls:
|
|
if i_cls.type == eolian.ClassType.REGULAR or \
|
|
i_cls.type == eolian.ClassType.ABSTRACT:
|
|
continue
|
|
else:
|
|
log.warn("Class %s is unknown" % (i))
|
|
l, n = conv_cls_name(i)
|
|
inherits.append("_" + n)
|
|
imports.append((l, "_" + n))
|
|
|
|
for l, n in imports:
|
|
l = l.lower()
|
|
gen.write("from %s import %s" % (l, n))
|
|
|
|
gen.write()
|
|
gen.write()
|
|
|
|
gen.write("class %s(%s):" % (self.name, ", ".join(inherits)))
|
|
gen.indent()
|
|
|
|
gen.write("def __init__(self, parent=None, *args, **kwargs):")
|
|
gen.indent()
|
|
gen.write(
|
|
"_%s.__init__(self, parent=None, *args, **kwargs)" % (
|
|
self.name
|
|
)
|
|
)
|
|
|
|
gen.dedent()
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
|
|
py_path = os.path.join(args.output, "__init__" + ".py")
|
|
if os.path.exists(py_path):
|
|
os.remove(py_path)
|
|
|
|
|
|
for path in args.paths:
|
|
for dirpath, dirnames, filenames in os.walk(path):
|
|
for filename in filenames:
|
|
if filename.endswith(".h"):
|
|
with open(os.path.join(dirpath, filename), "r") as h:
|
|
s = h.read()
|
|
matches = re.finditer(enum_re, s)
|
|
for m in matches:
|
|
name = m.group(2)
|
|
members = m.group(1)
|
|
log.debug("==== %s ====" % (name))
|
|
log.debug("--- RAW ---")
|
|
log.debug(members)
|
|
members = re.finditer(memb_re, members)
|
|
members2 = []
|
|
log.debug("--- REGEXED ---")
|
|
for match in members:
|
|
value = match.group("value")
|
|
comment = match.group("comment")
|
|
if comment:
|
|
comment2 = []
|
|
for line in comment.splitlines():
|
|
comment2.append(line.strip())
|
|
comment = " ".join(comment2)
|
|
log.debug(value, comment)
|
|
members2.append((value, comment))
|
|
if name:
|
|
name = name.strip("_ ")
|
|
enums[name] = (filename, members2)
|
|
else:
|
|
anon_enums.append((filename, members2))
|
|
|
|
enums_counter = Counter(enums.keys())
|
|
class_counter = Counter()
|
|
generated_class_counter = Counter()
|
|
function_counter = Counter()
|
|
generated_function_counter = Counter()
|
|
|
|
for path in args.paths:
|
|
for dirpath, dirnames, filenames in os.walk(path):
|
|
eolian.directory_scan(dirpath)
|
|
for filename in filenames:
|
|
if filename.endswith(".eo"):
|
|
f = os.path.join(dirpath, filename)
|
|
if not eolian.eo_file_parse(f):
|
|
log.warn("Errors in parsing %s" % (f))
|
|
|
|
|
|
with open(py_path, "a") as py_f:
|
|
for cls_name in eolian.class_names_list_get():
|
|
cls = eolian.class_find_by_name(cls_name)
|
|
try:
|
|
gencls = Class.parse(cls)
|
|
except Exception:
|
|
log.exception(
|
|
"Skipping %s because of an exception" % (cls.name))
|
|
continue
|
|
|
|
pyxgen = PyxGenerator()
|
|
gencls.pyx_generate(pyxgen)
|
|
|
|
f_base = os.path.splitext(filename)[0]
|
|
pxi_path = os.path.join(args.output, cls_name.lower() + ".pxi")
|
|
o = pyxgen.printout()
|
|
if o:
|
|
with open(pxi_path, "w") as f:
|
|
f.write(o.encode("utf-8"))
|
|
log.info(pxi_path + " written")
|
|
|
|
pygen = Generator()
|
|
gencls.py_generate(pygen)
|
|
|
|
o = pygen.printout()
|
|
if o:
|
|
py_f.write(o.encode("utf-8"))
|
|
py_f.write("\n")
|
|
log.info(py_path + " appended")
|
|
|
|
print("===============================================")
|
|
print("Number of classes: %d" % (len(class_counter)))
|
|
print("Number of classes generated: %d" % (len(generated_class_counter)))
|
|
print("Percentage of classes generated: %f" % (
|
|
float(len(generated_class_counter)) / float(len(class_counter)) * 100.0
|
|
))
|
|
print("Number of functions: %d" % (len(function_counter)))
|
|
print("Number of functions generated: %d" % (
|
|
len(generated_function_counter)))
|
|
print("Percentage of functions generated: %f" % (
|
|
float(len(generated_function_counter)) / float(len(function_counter))
|
|
* 100.0
|
|
))
|
|
|
|
|
|
eolian.shutdown()
|