990 lines
32 KiB
Python
Executable File
990 lines
32 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
import os
|
|
import textwrap
|
|
from keyword import iskeyword
|
|
|
|
# from collections import Counter
|
|
|
|
import logging
|
|
handler = logging.StreamHandler()
|
|
formatter = logging.Formatter("%(name)s %(levelname)s: %(message)s")
|
|
handler.setFormatter(formatter)
|
|
log = logging.getLogger("eolian")
|
|
log.addHandler(handler)
|
|
log_level = logging.WARN
|
|
log.setLevel(log_level)
|
|
|
|
import eolian
|
|
|
|
from converters import convert_in_param, convert_out_param, conv_type_ret, \
|
|
conv_cls_name, EolianTypeError, event_conversion_get
|
|
|
|
# XXX: C namespaces don't include this top level name
|
|
PY_EFL_TOP_LEVEL_PACKAGE = "efl"
|
|
generated_pxis = {}
|
|
|
|
|
|
def init():
|
|
eolian.init()
|
|
|
|
|
|
def shutdown():
|
|
eolian.shutdown()
|
|
|
|
|
|
def system_directory_scan():
|
|
eolian.system_directory_scan()
|
|
|
|
|
|
def all_eo_files_parse():
|
|
eolian.all_eo_files_parse()
|
|
|
|
|
|
def all_eot_files_parse():
|
|
eolian.all_eot_files_parse()
|
|
|
|
|
|
class EolianSkip(Exception):
|
|
pass
|
|
|
|
|
|
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 args.enable_docstrings 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):
|
|
result = "\n".join(self.result)
|
|
return result
|
|
|
|
def clear(self):
|
|
self.result = []
|
|
self.level = 0
|
|
|
|
|
|
class PyxGenerator(Generator):
|
|
|
|
RET_PARAM = "py_efl_ret"
|
|
|
|
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:
|
|
params2.append(" ".join((t, n)).strip())
|
|
|
|
params2 = ", ".join(params2)
|
|
define = "def %s(%s):" % (name, params2)
|
|
self.write(define)
|
|
|
|
def out_cdefs_write(self, out_cdefs):
|
|
if not out_cdefs:
|
|
return
|
|
self.write("cdef:")
|
|
self.indent()
|
|
types = {}
|
|
for t, n in out_cdefs:
|
|
types.setdefault(t, []).append(n)
|
|
for t, names in types.items():
|
|
#t = t.c_type
|
|
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, c_name, c_params, ret_type=None):
|
|
# TODO: Eina_Bool ret value from do_start, useful?
|
|
self.write("_eo_do_start(self.obj, NULL, EINA_FALSE, CFILE, CFUNC, CLINE)")
|
|
|
|
c_call = ""
|
|
if ret_type:
|
|
c_call += "cdef " + ret_type + " " + self.RET_PARAM + " = " + "<{0}>".format(ret_type)
|
|
|
|
c_call += c_name + "("
|
|
|
|
if not c_params:
|
|
c_call += ")"
|
|
self.write(c_call)
|
|
else:
|
|
self.write(c_call)
|
|
self.indent()
|
|
for p in c_params:
|
|
self.write(p + ",")
|
|
self.write(")")
|
|
self.dedent()
|
|
|
|
self.write("_eo_do_end(&self.obj)")
|
|
|
|
|
|
class Function(object):
|
|
|
|
RET_PARAM = "py_efl_ret"
|
|
|
|
def __init__(self, func, prefix, isget=None):
|
|
self.name = func.name
|
|
|
|
self.func = func
|
|
self.prefix = prefix
|
|
self.isget = isget
|
|
|
|
self.c_name = func.full_c_name
|
|
|
|
self.c_params = []
|
|
|
|
ret_type = None
|
|
|
|
ftypes = eolian.FunctionType
|
|
|
|
if (func.type == ftypes.PROP_SET or func.type == ftypes.PROPERTY) and not isget:
|
|
ret_type = func.return_type_get(eolian.FunctionType.PROP_SET)
|
|
self.c_name += "_set"
|
|
for p in func.parameters:
|
|
name = p.name
|
|
if iskeyword(name):
|
|
name += "_"
|
|
self.c_params.append((p.type.c_type, name))
|
|
if (func.type == ftypes.PROP_GET or func.type == ftypes.PROPERTY) and isget:
|
|
ret_type = func.return_type_get(eolian.FunctionType.PROP_GET)
|
|
self.c_name += "_get"
|
|
func_parameters = list(func.parameters)
|
|
if len(func_parameters) == 1 and not ret_type:
|
|
ret_type = func_parameters[0].type
|
|
else:
|
|
for p in func_parameters:
|
|
name = p.name
|
|
if iskeyword(name):
|
|
name += "_"
|
|
self.c_params.append((p.type.c_type + "*", name))
|
|
if func.type == ftypes.METHOD:
|
|
ret_type = func.return_type_get(eolian.FunctionType.METHOD)
|
|
for p in func.parameters:
|
|
name = p.name
|
|
if iskeyword(name):
|
|
name += "_"
|
|
c_type = p.type.c_type
|
|
if p.direction == eolian.ParameterDir.OUT:
|
|
c_type += "*"
|
|
self.c_params.append((c_type, name))
|
|
|
|
self.c_ret_type = ret_type.c_type if ret_type else "void"
|
|
|
|
def cdef_generate(self, gen):
|
|
func = self.func
|
|
ftypes = eolian.FunctionType
|
|
if func.type == ftypes.PROP_SET and self.isget:
|
|
return
|
|
if func.type == ftypes.PROP_GET and not self.isget:
|
|
return
|
|
rtype = self.c_ret_type
|
|
if not rtype:
|
|
rtype = "void"
|
|
line = " ".join((rtype, self.c_name))
|
|
line += "("
|
|
line += ", ".join([" ".join((t, n)) for t, n in self.c_params])
|
|
line += ")"
|
|
|
|
gen.write(line)
|
|
|
|
def generate(self, gen):
|
|
func = self.func
|
|
py_name = None
|
|
c_name = self.c_name
|
|
|
|
header_params = []
|
|
c_call_params = []
|
|
return_params = []
|
|
expand_params = []
|
|
conv_in_py_exps = []
|
|
|
|
out_cdefs = []
|
|
|
|
ret_type = None
|
|
|
|
ftypes = eolian.FunctionType
|
|
|
|
if func.type == ftypes.METHOD:
|
|
|
|
py_name = func.name
|
|
if iskeyword(py_name): # Check if name is python reserved
|
|
py_name += "_"
|
|
|
|
func_params = func.parameters
|
|
|
|
for p in func_params:
|
|
|
|
di = p.direction
|
|
c_type = p.type.c_type
|
|
|
|
p_name = p.name
|
|
if iskeyword(p_name):
|
|
p_name += "_"
|
|
|
|
if di == eolian.ParameterDir.IN:
|
|
conv_expr_py, c_type, name = convert_in_param(p.type, p_name, p.is_nonull)
|
|
conv_in_py_exps.append(conv_expr_py)
|
|
header_params.append((c_type, p_name))
|
|
c_call_params.append(name)
|
|
elif di == eolian.ParameterDir.OUT:
|
|
out_cdefs.append((c_type, p.name))
|
|
c_type, name = convert_out_param(p.type, p_name)
|
|
c_call_params.append("&" + p.name)
|
|
return_params.append((p.type, name))
|
|
elif di == eolian.ParameterDir.INOUT:
|
|
conv_expr_py, c_type, name = convert_in_param(p.type, p_name, p.is_nonull)
|
|
header_params.append((c_type, p_name))
|
|
c_call_params.append(name)
|
|
c_type, name = convert_out_param(p.type, p_name)
|
|
return_params.append((p.type, p_name))
|
|
|
|
ret_type = func.return_type_get(eolian.FunctionType.METHOD)
|
|
|
|
if (func.type == ftypes.PROP_SET or func.type == ftypes.PROPERTY) and not self.isget:
|
|
py_name = "__set__"
|
|
expand_params = []
|
|
|
|
if list(func.property_keys):
|
|
gen.method_header_write(py_name, (("", "tmp_value"),))
|
|
gen.indent()
|
|
gen.write("pass")
|
|
gen.dedent()
|
|
raise RuntimeError("Property keys not supported")
|
|
|
|
for p in list(func.property_values):
|
|
|
|
p_name = p.name
|
|
if iskeyword(p_name):
|
|
p_name += "_"
|
|
|
|
try:
|
|
conv_expr_py, c_type, name = convert_in_param(p.type, p_name, p.is_nonull)
|
|
except Exception:
|
|
gen.method_header_write(py_name, (("", "tmp_value"),))
|
|
gen.indent()
|
|
gen.write("pass")
|
|
gen.dedent()
|
|
raise
|
|
|
|
conv_in_py_exps.append(conv_expr_py)
|
|
expand_params.append((c_type, p_name))
|
|
c_call_params.append(name)
|
|
|
|
header_params = (("", "tmp_value"),)
|
|
#ret_type = func.return_type_get(eolian.FunctionType.PROP_SET)
|
|
|
|
if (func.type == ftypes.PROP_GET or func.type == ftypes.PROPERTY) and self.isget:
|
|
py_name = "__get__"
|
|
|
|
if list(func.property_keys):
|
|
gen.method_header_write(py_name, header_params)
|
|
gen.indent()
|
|
gen.write("pass")
|
|
gen.dedent()
|
|
raise RuntimeError("Property keys not supported")
|
|
|
|
ret_type = func.return_type_get(eolian.FunctionType.PROP_GET)
|
|
|
|
property_values = list(func.property_values)
|
|
if len(property_values) == 1 and not ret_type:
|
|
ret_type = property_values[0].type
|
|
else:
|
|
for p in list(func.property_values):
|
|
|
|
c_type = p.type.c_type
|
|
name = p.name
|
|
if iskeyword(name):
|
|
name += "_"
|
|
|
|
out_cdefs.append((c_type, name))
|
|
c_call_params.append("&%s" % (name))
|
|
|
|
try:
|
|
c_type, name = convert_out_param(p.type, name)
|
|
except Exception:
|
|
gen.method_header_write(py_name, header_params)
|
|
gen.indent()
|
|
gen.write("pass")
|
|
gen.dedent()
|
|
raise
|
|
|
|
return_params.append((p.type, name))
|
|
|
|
if not py_name:
|
|
return
|
|
|
|
gen.method_header_write(py_name, header_params)
|
|
gen.indent()
|
|
|
|
if expand_params:
|
|
tmp_prms = ", ".join(["tmp_prm%d" % (i) for i in range(len(expand_params))])
|
|
gen.write("%s = tmp_value" % (tmp_prms))
|
|
gen.write("cdef:")
|
|
gen.indent()
|
|
for i, (t, n) in enumerate(expand_params):
|
|
gen.write("%s = tmp_prm%d" % (" ".join((t, n)).strip(), i))
|
|
|
|
gen.dedent()
|
|
|
|
for e in conv_in_py_exps:
|
|
if e:
|
|
gen.write(e)
|
|
|
|
gen.out_cdefs_write(out_cdefs)
|
|
|
|
if ret_type:
|
|
ret_type2 = ret_type.c_type
|
|
else:
|
|
ret_type2 = None
|
|
|
|
gen.c_call_write(c_name, c_call_params, ret_type2)
|
|
|
|
if return_params:
|
|
rparams = []
|
|
for t, n in return_params:
|
|
name = n
|
|
if iskeyword(name):
|
|
name += "_"
|
|
try:
|
|
py_ret_type, c_ret_type = conv_type_ret(t)
|
|
except EolianTypeError:
|
|
gen.write("# FIXME: Unknown return type %s" % t.c_type)
|
|
gen.dedent()
|
|
raise
|
|
rparams.append((c_ret_type, name))
|
|
gen.write("return %s" % (", ".join([r[1] for r in rparams])))
|
|
elif ret_type:
|
|
ret = self.RET_PARAM
|
|
try:
|
|
ret_type, ret = convert_out_param(ret_type, ret)
|
|
except EolianTypeError:
|
|
gen.write("# FIXME: Unknown return type %s" % ret_type.c_type)
|
|
gen.dedent()
|
|
raise
|
|
gen.write("return %s" % (ret))
|
|
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
def doc_generate(self, gen):
|
|
func_desc = func.description_get(eolian.FunctionType.METHOD)
|
|
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.docs.append(":param %s: %s" % (name, desc))
|
|
self.docs.append(":type %s: %s" % (name, ptype))
|
|
|
|
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)
|
|
|
|
|
|
class Property(object):
|
|
def __init__(self, prop, prefix):
|
|
self.name = prop.name
|
|
|
|
self.prop = prop
|
|
self.prefix = prefix
|
|
self.py_name = prop.name
|
|
self.getter = Function(prop, prefix, isget=True)
|
|
self.setter = Function(prop, prefix, isget=False)
|
|
|
|
def cdef_generate(self, gen):
|
|
self.getter.cdef_generate(gen)
|
|
self.setter.cdef_generate(gen)
|
|
|
|
def generate(self, gen):
|
|
gen.write("property %s:" % (self.py_name))
|
|
|
|
gen.indent()
|
|
|
|
try:
|
|
self.getter.generate(gen)
|
|
self.setter.generate(gen)
|
|
finally:
|
|
gen.dedent()
|
|
|
|
|
|
class Constructor(object):
|
|
|
|
def __init__(self, ctors, prefix, clsgetter):
|
|
self.prefix = prefix
|
|
self.ctors = ctors
|
|
self.clsgetter = clsgetter
|
|
|
|
def cdef_generate(self, gen):
|
|
pass
|
|
|
|
def generate(self, gen):
|
|
cls_get = self.clsgetter
|
|
|
|
header_params = []
|
|
c_ctors = []
|
|
|
|
for ctor in self.ctors:
|
|
func = ctor.function
|
|
|
|
c_name = func.full_c_name
|
|
|
|
c_call_params = []
|
|
|
|
py_name = func.name
|
|
if iskeyword(py_name): # Check if name is python reserved
|
|
py_name += "_"
|
|
|
|
ftypes = eolian.FunctionType
|
|
if (func.type == ftypes.PROP_SET or func.type == ftypes.PROPERTY):
|
|
c_name = c_name + "_set"
|
|
|
|
for p in func.parameters:
|
|
di = p.direction
|
|
assert di == eolian.ParameterDir.IN, "other than IN param for constructor"
|
|
|
|
c_type = p.type.c_type
|
|
|
|
conv_expr_py, c_type, name = convert_in_param(p.type, p.name, p.is_nonull)
|
|
header_params.append((c_type, p.name))
|
|
c_call_params.append(name)
|
|
|
|
c_call_params = ", ".join(c_call_params)
|
|
c_ctors.append((c_name, c_call_params))
|
|
|
|
header_params.append(("_Eo", "parent=None"))
|
|
|
|
gen.write("def __init__(self, %s):" % (", ".join([" ".join((t, n)).strip() for t, n in header_params])))
|
|
gen.indent()
|
|
|
|
gen.write("cdef Eo *added_obj = eo_add_ref(")
|
|
gen.indent()
|
|
gen.write("%s(), <Eo *>parent.obj if parent is not None else NULL," % (cls_get))
|
|
for ctor in c_ctors:
|
|
gen.write("%s(%s)" % (ctor[0], ctor[1]))
|
|
gen.write(")")
|
|
gen.dedent()
|
|
gen.write("self._set_obj(added_obj)")
|
|
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
|
|
class Class(object):
|
|
def __init__(self):
|
|
self.docs = []
|
|
self.inherits = []
|
|
|
|
self.ctor = []
|
|
self.methods = []
|
|
self.props = []
|
|
self.events = []
|
|
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.full_c_name = cls.full_name
|
|
self.lib_name, self.name = conv_cls_name(cls)
|
|
self.cls_getter = cls.c_get_function_name
|
|
|
|
#self.inherits = list(cls.inherits)
|
|
# XXX: Hack for T1721
|
|
self.inherits = [x for x in cls.inherits if not x.startswith("Elm_Interface_Atspi")]
|
|
|
|
prefix = cls.eo_prefix
|
|
|
|
if not prefix:
|
|
log.warn("Class %s has no prefix!" % (cls.name))
|
|
prefix = cls.name.lower()
|
|
|
|
self.prefix = prefix
|
|
|
|
if cls.type == eolian.ClassType.REGULAR: # or cls.type == eolian.ClassType.MIXIN:
|
|
self.ctor.append(Constructor(cls.constructors, prefix, cls.c_get_function_name))
|
|
|
|
props = cls.functions_get(eolian.FunctionType.PROPERTY)
|
|
for prop in props:
|
|
if not prop.scope == eolian.ObjectScope.PUBLIC:
|
|
log.debug(
|
|
"Skipping non-public property %s.%s",
|
|
cls.name, prop.name
|
|
)
|
|
continue
|
|
# function_counter["_".join((prefix, prop.name))] += 1
|
|
try:
|
|
o = Property(prop, prefix)
|
|
except Exception:
|
|
log.exception(
|
|
"Skipping %s.%s because of an exception",
|
|
cls.name, prop.name
|
|
)
|
|
continue
|
|
self.props.append(o)
|
|
|
|
methods = cls.functions_get(eolian.FunctionType.METHOD)
|
|
for method in methods:
|
|
if not method.scope == eolian.ObjectScope.PUBLIC:
|
|
log.debug(
|
|
"Skipping non-public method %s.%s",
|
|
cls.name, method.name
|
|
)
|
|
continue
|
|
# function_counter["_".join((prefix, method.name))] += 1
|
|
try:
|
|
o = Function(method, prefix)
|
|
except Exception:
|
|
log.exception(
|
|
"Skipping %s.%s because of an exception"
|
|
% (cls.name, method.name))
|
|
continue
|
|
self.methods.append(o)
|
|
|
|
# generated_class_counter[cls.name] += 1
|
|
|
|
for event in cls.events:
|
|
self.events.append(Event(event))
|
|
|
|
return self
|
|
|
|
def cdefs_generate(self, gen):
|
|
gen.write("const Eo_Class *%s()" % (self.cls_getter))
|
|
# gen.write("ctypedef Eo %s" % (self.full_c_name.replace(".", "_")))
|
|
if self.ctor or self.methods or self.props or self.events:
|
|
for o in self.ctor + self.methods + self.props + self.events:
|
|
try:
|
|
o.cdef_generate(gen)
|
|
except Exception:
|
|
log.exception("Error generating cdefs for %r", o)
|
|
|
|
def pyx_generate(self, gen):
|
|
inherits = []
|
|
for i in self.inherits:
|
|
i_cls = eolian.Class.get_by_name(i)
|
|
if i_cls:
|
|
if i_cls.type == eolian.ClassType.INTERFACE or \
|
|
i_cls.type == eolian.ClassType.MIXIN:
|
|
continue
|
|
if i_cls.full_name == "Eo.Base":
|
|
inherits.append("_Eo")
|
|
continue
|
|
lib_name, name = conv_cls_name(i_cls)
|
|
inherits.append("_" + name)
|
|
else:
|
|
log.warn("Inherited class %s is unknown" % (i))
|
|
i = i.rpartition(".")[2]
|
|
inherits.append("_" + i)
|
|
if len(inherits) > 1:
|
|
log.error(
|
|
"Multiple inheritance is not supported in extension classes.\n"
|
|
"Class: %r" % (self.c_name)
|
|
)
|
|
return
|
|
|
|
if not inherits:
|
|
inherits = ("_Eo", )
|
|
inherits = ", ".join(inherits)
|
|
|
|
gen.write("cdef class _%s(%s):" % (self.name, inherits))
|
|
gen.indent()
|
|
gen.write()
|
|
|
|
# gen.write("cdef %s *ext_cls_obj" % self.full_c_name.replace(".", "_"))
|
|
# gen.write()
|
|
|
|
if self.events:
|
|
gen.write("def __cinit__(self):")
|
|
gen.indent()
|
|
for o in self.events[:]:
|
|
try:
|
|
o.generate_name_mapping(gen)
|
|
o.generate_conversion_mapping(gen)
|
|
except EolianTypeError:
|
|
log.info("Skipping %r because of unknown type", o)
|
|
except Exception:
|
|
log.exception("Error while generating %r", o)
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
for o in self.events[:]:
|
|
try:
|
|
o.generate_conversion_function(gen)
|
|
except EolianTypeError:
|
|
log.info("Skipping %r because of unknown type", o)
|
|
except Exception:
|
|
log.exception("Error while generating %r", o)
|
|
|
|
if not self.ctor and not self.props and not self.methods:
|
|
gen.write("pass")
|
|
else:
|
|
for o in (
|
|
self.ctor #+
|
|
#list(self.events)
|
|
):
|
|
try:
|
|
o.generate(gen)
|
|
except EolianTypeError:
|
|
raise
|
|
#log.error("Skipping %s constructor because of unknown type", self.full_c_name)
|
|
except Exception:
|
|
log.exception("Error while generating %s constructor", self.full_c_name)
|
|
|
|
for o in (
|
|
list(self.methods) +
|
|
list(self.props)
|
|
):
|
|
try:
|
|
o.generate(gen)
|
|
except EolianTypeError:
|
|
log.info("Skipping %r because of unknown type", o)
|
|
except Exception:
|
|
log.exception("Error while generating %r", o)
|
|
# else:
|
|
# generated_function_counter["_".join((o.prefix, o.name))] += 1
|
|
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
# --- Py class
|
|
inherits = ["_" + self.name]
|
|
#imports = [(self.lib_name, "_" + self.name)]
|
|
imports = []
|
|
for i in self.inherits:
|
|
i_cls = eolian.Class.get_by_name(i)
|
|
if i_cls:
|
|
if i_cls.type == eolian.ClassType.REGULAR or \
|
|
i_cls.type == eolian.ClassType.ABSTRACT:
|
|
continue
|
|
lib_name, name = conv_cls_name(i_cls)
|
|
else:
|
|
log.warn("Class %s is unknown" % (i))
|
|
i = i.rpartition(".")
|
|
lib_name, name = i[0], i[2]
|
|
|
|
inherits.append(lib_name.replace(".", "_") + "_" + name)
|
|
imports.append((lib_name, "_" + name))
|
|
|
|
for l, n in imports:
|
|
#l2 = PY_EFL_TOP_LEVEL_PACKAGE + "." + l.lower()
|
|
l2 = l.lower()
|
|
gen.write("from %s import %s as %s" % (l2, n, l.replace(".", "_") + n))
|
|
|
|
gen.write()
|
|
gen.write()
|
|
|
|
gen.write("class %s(%s):" % (self.name, ", ".join(inherits)))
|
|
gen.indent()
|
|
|
|
gen.write("def __init__(self, *args, **kwargs):")
|
|
gen.indent()
|
|
gen.write(
|
|
"_%s.__init__(self, *args, **kwargs)" % (
|
|
self.name
|
|
)
|
|
)
|
|
|
|
gen.dedent()
|
|
gen.dedent()
|
|
gen.write()
|
|
gen.write('_object_mapping_register("%s", %s)' % (self.full_c_name.replace(".", "_"), self.name))
|
|
gen.write()
|
|
|
|
|
|
class Event(object):
|
|
def __init__(self, event):
|
|
self.event = event
|
|
|
|
def cdef_generate(self, gen):
|
|
gen.write("enum: %s" % (self.event.c_name))
|
|
|
|
def generate_name_mapping(self, gen):
|
|
gen.write(
|
|
'eina_hash_add(self.event_desc_mapping, "%s", <const void *>%s)' % (
|
|
self.event.name, self.event.c_name
|
|
)
|
|
)
|
|
|
|
def generate_conversion_mapping(self, gen):
|
|
conv = event_conversion_get(self.event.type)
|
|
if conv:
|
|
gen.write(
|
|
'eina_hash_add(self.event_conv_mapping, %s, <const void *>self._event_conv_%s_to_python)' % (
|
|
self.event.c_name, self.event.name.replace(",", "_")
|
|
)
|
|
)
|
|
else:
|
|
pass #print("{0} - {1}".format(self.event.name, self.event.type.c_type))
|
|
|
|
def generate_conversion_function(self, gen):
|
|
conv = event_conversion_get(self.event.type)
|
|
if conv:
|
|
gen.write(
|
|
'cdef object _event_conv_%s_to_python(self, void *evi):' % (
|
|
self.event.name.replace(",", "_")
|
|
)
|
|
)
|
|
gen.indent()
|
|
gen.write('cdef {0} tmp = <{0}>evi'.format(self.event.type.c_type))
|
|
gen.write('return ' + conv)
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
|
|
class File(object):
|
|
def __init__(self, filename, header_name):
|
|
self.filename = filename
|
|
self.header_name = header_name
|
|
|
|
self.pyxgen = PyxGenerator()
|
|
self.pygen = Generator()
|
|
# if not eolian.eo_file_parse(filename):
|
|
# log.warn("Errors in parsing %s" % (filename))
|
|
|
|
self.cls = eolian.Class.get_by_file(filename)
|
|
if not self.cls:
|
|
raise RuntimeError("Could not get class from %s" % (filename))
|
|
if self.cls.full_name.startswith("Eo."):
|
|
raise EolianSkip("Skipping Eo class")
|
|
self.gencls = Class.parse(self.cls)
|
|
|
|
def cdefs_generate(self):
|
|
gen = self.pyxgen
|
|
|
|
gen.write('cdef extern from "%s":' % (self.header_name + ".h"))
|
|
gen.indent()
|
|
|
|
for i in eolian.type_aliases_get_by_file(self.filename):
|
|
base = i.base_type
|
|
if base.type == eolian.TypeType.REGULAR:
|
|
gen.write("ctypedef %s %s" % (base.name, i.name))
|
|
elif base.type == eolian.TypeType.FUNCTION:
|
|
ret_type = base.return_type
|
|
if ret_type:
|
|
ret = ret_type.c_type
|
|
else:
|
|
ret = "void"
|
|
gen.write("ctypedef %s (*%s)(%s)" % (
|
|
ret,
|
|
i.name,
|
|
", ".join([i.c_type for i in base.arguments])))
|
|
elif base.type == eolian.TypeType.STRUCT:
|
|
gen.write("ctypedef struct %s:" % (i.name))
|
|
gen.indent()
|
|
for f in base.struct_fields:
|
|
gen.write("%s %s" % (f.type.c_type, f.name))
|
|
gen.dedent()
|
|
else:
|
|
log.error("Unhandled alias! %s %s %s %s", i.name, i.full_name, i.filename, i.description)
|
|
for i in eolian.type_structs_get_by_file(self.filename):
|
|
log.error("Type struct not handled! %s %s %s %s", i.name, i.full_name, i.filename, i.description)
|
|
for i in eolian.type_enums_get_by_file(self.filename):
|
|
log.error("Type enum not handled! %s %s %s %s", i.name, i.full_name, i.filename, i.description)
|
|
for i in eolian.variable_constants_get_by_file(self.filename):
|
|
log.error("Variable constant not handled! %s", i)
|
|
for i in eolian.variable_globals_get_by_file(self.filename):
|
|
log.error("Variable global not handled! %s", i)
|
|
|
|
self.gencls.cdefs_generate(gen)
|
|
|
|
gen.dedent()
|
|
gen.write()
|
|
|
|
def pyx_generate(self):
|
|
self.gencls.pyx_generate(self.pyxgen)
|
|
|
|
def printout(self):
|
|
return self.pyxgen.printout()
|
|
|
|
|
|
# class_counter = Counter()
|
|
# generated_class_counter = Counter()
|
|
# function_counter = Counter()
|
|
# generated_function_counter = Counter()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
init()
|
|
from argparse import ArgumentParser
|
|
parser = ArgumentParser(description="Python bindings generator for Eolian")
|
|
parser.add_argument(
|
|
'-v', '--verbose', action="count", help="max is -vvv")
|
|
# parser.add_argument(
|
|
# '--enable-docstrings', action="store_true")
|
|
# parser.add_argument(
|
|
# '--with-legacy-api', action="store_true")
|
|
parser.add_argument(
|
|
'-o', '--output', default="/tmp", help="output directory, defaults to /tmp")
|
|
parser.add_argument(
|
|
'-I', help="include directory")
|
|
parser.add_argument('paths', nargs="+")
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
log_level -= 10 * args.verbose
|
|
|
|
log.setLevel(log_level)
|
|
|
|
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))
|
|
|
|
try:
|
|
eolf = File(filename)
|
|
except EolianSkip:
|
|
continue
|
|
except Exception:
|
|
log.exception("Exception while creating %s" % (filename))
|
|
continue
|
|
|
|
try:
|
|
eolf.cdefs_generate()
|
|
except Exception:
|
|
log.exception("Exception while generating cdefs for %s" % (filename))
|
|
continue
|
|
|
|
try:
|
|
eolf.pyx_generate()
|
|
except Exception:
|
|
log.exception("Exception while generating pyx for %s" % (filename))
|
|
continue
|
|
|
|
# try:
|
|
# eolf.py_generate()
|
|
# except Exception:
|
|
# log.exception("Exception while generating py for %s" % (filename))
|
|
# continue
|
|
|
|
|
|
path = []
|
|
cls_name = self.cls.name.lower()
|
|
filename = "generated_" + cls_name + ".pxi"
|
|
for ns in self.cls.namespaces:
|
|
path.append(ns.lower())
|
|
if not path:
|
|
log.warning("Class %s has no namespaces defined" % (self.cls.name))
|
|
nstmp = cls_name.partition("_")
|
|
path.append(nstmp[0])
|
|
filename = "generated_" + nstmp[2] + ".pxi"
|
|
path = os.path.join(*path)
|
|
pxi_path = os.path.join(args.output, path, filename)
|
|
|
|
self.pyxgen.printout(filepath=pxi_path)
|
|
|
|
generated_pxis.setdefault(os.path.join(path), []).append((path, filename, self.cls))
|
|
|
|
for pxis in generated_pxis.values():
|
|
ns, pxi_path, cls = pxis[0]
|
|
filename = os.path.join(args.output, ns, "generated_classes.pxi")
|
|
if os.path.exists(filename):
|
|
log.warn("File %s exists, removing" % (filename))
|
|
os.remove(filename)
|
|
with open(filename, "a") as f:
|
|
# for ns, pxi_path, cls in pxis:
|
|
# line = "ctypedef Eo %s\n" % (cls.full_name.replace(".", "_"))
|
|
# f.write(line)
|
|
for ns, pxi_path, cls in pxis:
|
|
line = 'include "%s"\n' % (pxi_path)
|
|
f.write(line)
|
|
|
|
|
|
# def report():
|
|
# print("===============================================")
|
|
# print("Number of classes: %d" % (len(class_counter)))
|
|
# print("Number of classes generated: %d" % (len(generated_class_counter)))
|
|
# if len(class_counter) > 0:
|
|
# 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)))
|
|
# if len(function_counter) > 0:
|
|
# print("Percentage of functions generated: %f" % (
|
|
# float(len(generated_function_counter)) / float(len(function_counter))
|
|
# * 100.0
|
|
# ))
|
|
|
|
# report()
|