diff --git a/scripts/converters.py b/scripts/converters.py index 5c44c02..ac551e0 100644 --- a/scripts/converters.py +++ b/scripts/converters.py @@ -19,42 +19,50 @@ docstring_replacements = ( ("NULL", "None"), ) +builtin_types = ( # automatically converted by cython + "short", + "ushort", + #"char", + "int", + "uint", + "double", + "Evas_Coord", + "Evas_Real" + ) + complex_types = ( "Eina_List" ) mapping_in = { # c_type: pyx_type - "Eina_Bool": "bint", - "char *": "", + "bool": "bint", + "char": "", "Evas_Object *": "_Eo_Base", "Eo *": "_Eo_Base", } conversions_in = { - # c_type: (conversion, c_param_convert) - "Eina_Bool": (None, None), - "char *": ( - "if isinstance({0}, unicode): {0} = PyUnicode_AsUTF8String({0})", - "<{1}>if {0} is not None else NULL" - ), - "Evas_Object *": (None, "{0}.obj"), - "Eo *": (None, "{0}.obj"), + # c_type: conversion + "bool": None, + "char": "if isinstance({0}, unicode): {0} = PyUnicode_AsUTF8String({0})", + "Evas_Object": None, + "Eo": None, } mapping_out = { # c_type: pyx_type "bool": "bint", - "char *": "unicode", - "Elm_Object_Item *": "_ObjectItem", - "Evas_Object *": "_Eo_Base", - "Eo *": "_Eo_Base", + "char": "unicode", + "Elm_Object_Item": "_ObjectItem", + "Evas_Object": "_Eo_Base", + "Eo": "_Eo_Base", } conversions_out = { # c_type: conversion "bool": "bool({0})", - "char *": '{0}.decode("utf-8")', + "char": '{0}.decode("utf-8")', "Elm_Object_Item *": 'object_item_to_python({0})', "Evas_Object *": 'object_from_instance({0})', "Eo": 'object_from_instance({0})', @@ -76,34 +84,43 @@ def remove_type_prefixes(ctype): def convert_in_param(tp, name, is_nonull=None): + c_type = tp.c_type if tp.type == eolian.TypeType.POINTER: - c_type = tp.c_type tp = tp.base_type - else: - c_type = tp.c_type + if tp.type == eolian.TypeType.CLASS: + return None, c_type, "{0}.obj".format(tp.name) key = tp.name if tp.name else c_type + conv_expr = conversions_in.get(key) + if conv_expr is not None: + conv_expr = conv_expr.format(name) - c_type = conversions_in.get(key, c_type) - - if tp.type == eolian.TypeType.CLASS: - key = "{0}.obj".format(key) + conv_t = mapping_in.get(key) + if conv_t is None: + if key not in builtin_types: + log.warn("Unknown in param %s (type=%s, c_type=%s)", tp.name, tp.type, c_type) + conv_t = "" if not is_nonull: key = "<{0}>if {1} is not None else NULL".format(c_type, name) - return conv_expr, c_type, key + return conv_expr, conv_t, key def convert_out_param(tp, name): if tp.type == eolian.TypeType.POINTER: tp = tp.base_type + if tp.type == eolian.TypeType.CLASS: + # TODO: set obj + return tp.c_type, name key = tp.name if tp.name else tp.c_type conv = conversions_out.get(key) if not conv: + if not key in builtin_types: + log.warn("Unknown out param %s (type=%s, c_type=%s)", tp.name, tp.type, tp.c_type) return tp.c_type, name name = conv.format(name) @@ -114,17 +131,21 @@ def conv_type_ret(tp): if not tp: return + if tp.type == eolian.TypeType.POINTER: + tp = tp.base_type + c_type = tp.c_type key = tp.name if tp.name else c_type #c_t = remove_type_prefixes(tp) py_t = mapping_out.get(key) if py_t is None: - log.warn("Unknown ret type: %s", key) + if key not in builtin_types: + log.warn("Unknown ret type: %s, %s", key, tp.type) return c_type, c_type py_ret_type = py_t[0] - c_ret_type = remove_type_prefixes(c_type) + #c_ret_type = remove_type_prefixes(c_type) - return py_ret_type, c_ret_type + return py_ret_type, c_type diff --git a/scripts/eolian_generate.py b/scripts/eolian_generate.py index 1a4c284..0cd155a 100755 --- a/scripts/eolian_generate.py +++ b/scripts/eolian_generate.py @@ -139,11 +139,15 @@ class PyxGenerator(Generator): self.dedent() def c_call_write(self, c_name, c_params, ret_type=None, returns=None): + # TODO: Eina_Bool ret value, useful? + self.write("_eo_do_start(self.obj, NULL, EINA_FALSE, eina_main_loop_is(), NULL, NULL, 0)") c_params = ", ".join(c_params) - c_call = "eo_do(self.obj, %s(%s))" % (c_name, c_params) + #c_call = "eo_do(self.obj, %s(%s))" % (c_name, c_params) + c_call = "%s(%s)" % (c_name, c_params) if ret_type: c_call = "cdef " + ret_type + " " + self.RET_PARAM + " = " + "<{0}>".format(ret_type) + c_call self.write(c_call) + self.write("_eo_do_end(self.obj)") class Function(object): @@ -155,25 +159,31 @@ class Function(object): self.prefix = prefix self.isget = isget - ftypes = eolian.FunctionType - if func.type == ftypes.PROP_SET or func.type == ftypes.PROPERTY and not isget: - ftype = eolian.FunctionType.PROP_SET - if func.type == ftypes.PROP_GET or func.type == ftypes.PROPERTY and isget: - ftype = eolian.FunctionType.PROP_GET - else: - ftype = eolian.FunctionType.METHOD - - ret_type = func.return_type_get(ftype) - if ret_type.type == eolian.TypeType.UNKNOWN: - self.c_ret_type = "void" - else: - self.c_ret_type = ret_type.c_type self.c_name = func.full_c_name_get(self.prefix) + + 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" + 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" + else: + ret_type = func.return_type_get(eolian.FunctionType.METHOD) + + self.c_ret_type = ret_type.c_type if ret_type.type != eolian.TypeType.UNKNOWN else "void" + self.c_params = [] for p in func.parameters: self.c_params.append((p.type.c_type, p.name)) - def cdefs_generate(self, gen): + def cdef_generate(self, gen): + func = self.func + ftypes = eolian.FunctionType + if (func.type == ftypes.PROP_SET or func.type == ftypes.PROPERTY) and self.isget: + return + if (func.type == ftypes.PROP_GET or func.type == ftypes.PROPERTY) and not self.isget: + return rtype = self.c_ret_type if not rtype: rtype = "void" @@ -193,6 +203,7 @@ class Function(object): c_call_params = [] return_params = [] conv_params = [] + conv_in_exps = [] out_cdefs = [] @@ -215,11 +226,12 @@ class Function(object): if di == eolian.ParameterDir.IN: conv_expr, c_type, name = convert_in_param(p.type, p.name, p.is_nonull) + conv_in_exps.append(conv_expr) header_params.append((c_type, p.name)) c_call_params.append(name) elif di == eolian.ParameterDir.OUT: - out_cdefs.append((c_type, "&"+name)) - c_type, name = convert_out_param(p.type, name) + out_cdefs.append((c_type, "&"+p.name)) + c_type, name = convert_out_param(p.type, p.name) return_params.append((p.type, name)) elif di == eolian.ParameterDir.INOUT: conv_expr, c_type, name = convert_in_param(p.type, p.name, p.is_nonull) @@ -243,7 +255,8 @@ class Function(object): conv_expr, c_type, name = convert_in_param(p.type, p.name, p.is_nonull) - conv_params.append(name) + conv_in_exps.append(conv_expr) + conv_params.append(p.name) c_call_params.append(name) assert conv_params, "params should not be empty for setter of: %s" % (func.name) @@ -281,6 +294,10 @@ class Function(object): conv_params = ", ".join(conv_params) gen.write("%s = value" % (conv_params)) + for e in conv_in_exps: + if e: + gen.write(e) + gen.out_cdefs_write(out_cdefs) if ret_type.type != eolian.TypeType.UNKNOWN: @@ -340,9 +357,9 @@ class Property(object): self.getter = Function(prop, prefix, isget=True) self.setter = Function(prop, prefix, isget=False) - def cdefs_generate(self, gen): - self.getter.cdefs_generate(gen) - self.setter.cdefs_generate(gen) + 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)) @@ -355,62 +372,71 @@ class Property(object): gen.dedent() -class EoConstructor(Function): +class Constructor(object): - 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 __init__(self, prefix): + def __init__(self, ctors, prefix): self.prefix = prefix + self.ctors = ctors + + def cdef_generate(self, gen): + pass def generate(self, gen): cls_get = self.prefix + "_class_get()" - gen.write("def __init__(self, parent=None):") + + header_params = [("_Base", "parent=None")] + c_ctors = [] + + for ctor in self.ctors: + func = ctor.function + + c_name = func.full_c_name_get(self.prefix) + + c_call_params = [] + + py_name = func.name + if keyword.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, 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)) + + gen.write("def __init__(self, %s):" % (", ".join([" ".join((t, n)).strip() for t, n in header_params]))) gen.indent() - gen.write( - "cdef Eo *obj = eo_add" - "(%s, parent.obj if parent is not None else NULL)" % ( - cls_get - ) - ) + + gen.write("cdef Eo *obj = eo_add(") + gen.indent() + gen.write("%s, 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(obj)") + gen.dedent() gen.write() - def cdefs_generate(self, gen): - pass - class Class(object): def __init__(self): self.docs = [] self.inherits = [] - self.ctors = [] + self.ctor = [] self.methods = [] self.props = [] self.default_ctor = None @@ -435,8 +461,6 @@ class Class(object): self.inherits = list(cls.inherits) - ctors = None - prefix = cls.eo_prefix if not prefix: @@ -445,29 +469,8 @@ class Class(object): self.prefix = prefix - if cls.type == eolian.ClassType.REGULAR: # or cls.type == eolian.ClassType.MIXIN: - ctors = cls.constructors - if ctors: - for ctor in ctors: - function_counter[ctor.full_name] += 1 - if not args.with_legacy_api: - try: - o = EoConstructor.parse(ctor, prefix) - except Exception as e: - log.warn( - "Skipping %s.%s because of an exception", - cls.name, ctor.full_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 - self.default_ctor = EoDefaultConstructor(prefix) + #if cls.type == eolian.ClassType.REGULAR: # or cls.type == eolian.ClassType.MIXIN: + self.ctor.append(Constructor(cls.constructors, prefix)) props = cls.functions_get(eolian.FunctionType.PROPERTY) for prop in props: @@ -480,7 +483,7 @@ class Class(object): function_counter["_".join((prefix, prop.name))] += 1 try: o = Property(prop, prefix) - except Exception as e: + except Exception: log.exception( "Skipping %s.%s because of an exception", cls.name, prop.name @@ -500,7 +503,7 @@ class Class(object): function_counter["_".join((prefix, method.name))] += 1 try: o = Function(method, prefix) - except Exception as e: + except Exception: log.exception( "Skipping %s.%s because of an exception" % (cls.name, method.name)) @@ -512,6 +515,14 @@ class Class(object): return self + def cdefs_generate(self, gen): + if self.ctor or self.methods or self.props: + for o in self.ctor + self.methods + self.props: + 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: @@ -526,19 +537,10 @@ class Class(object): if len(inherits) > 1: log.error( "Multiple inheritance is not supported in extension classes.\n" - "Class: %r" % (cls) + "Class: %r" % (self.c_name) ) return - 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.cdefs_generate(gen) - gen.dedent() - gen.write() - - if not inherits: inherits = ("object", ) inherits = ", ".join(inherits) @@ -546,18 +548,19 @@ class Class(object): gen.write("cdef class _%s(%s):" % (self.name, inherits)) gen.indent() - if not self.ctors and not self.props and not self.methods and \ - not self.default_ctor: + if not self.ctor and not self.props and not self.methods: gen.write("pass") else: for o in ( - [self.default_ctor] + - list(self.ctors) + + self.ctor + list(self.methods) + list(self.props) ): if o: - o.generate(gen) + try: + o.generate(gen) + except Exception: + log.exception("Error while generating %r", o) gen.dedent() gen.write() @@ -600,65 +603,118 @@ class Class(object): gen.write() -py_path = os.path.join(args.output, "__init__" + ".py") -if os.path.exists(py_path): - os.remove(py_path) +class File(object): + def __init__(self, filepath): + self.filepath = filepath + self.pyxgen = PyxGenerator() + self.pygen = Generator() + self.cls = eolian.Class.get_by_file(filepath) + if not self.cls: + raise RuntimeError("Could not get class") + self.gencls = Class.parse(self.cls) + + def cdefs_generate(self): + gen = self.pyxgen + gen.write('cdef extern from "Eina.h":') + gen.indent() + gen.write('Eina_Bool eina_main_loop_is(void)') + gen.dedent() + gen.write() + + gen.write('cdef extern from "Eo.h":') + gen.indent() + gen.write('Eina_Bool _eo_do_start(const Eo *obj, const Eo_Class *cur_klass, Eina_Bool is_super, Eina_Bool is_main_loop, const char *file, const char *func, int line)') + gen.write('void _eo_do_end(const Eo **ojb)') + gen.dedent() + gen.write() + + gen.write('cdef extern from "%s":' % (self.gencls.header_path)) + gen.indent() + + for i in eolian.type_aliases_get_by_file(self.filepath): + print(i) + for i in eolian.type_structs_get_by_file(self.filepath): + print(i) + for i in eolian.type_enums_get_by_file(self.filepath): + print(i) + + self.gencls.cdefs_generate(gen) + + gen.dedent() + gen.write() + + def pyx_generate(self): + self.gencls.pyx_generate(self.pyxgen) + + #f_base = os.path.splitext(py_path)[0] + + path = [args.output] + namespaces = [] + for ns in self.cls.namespaces: + namespaces.append(ns.lower()) + if not namespaces: + log.error("Class %s has no namespaces defined" % (self.cls.name)) + filename = self.cls.name.lower() + ".pxi" + namespace = ".".join(namespaces) + filename = ".".join((namespace, filename)) + path.append(filename) + pxi_path = os.path.join(*path) + + o = self.pyxgen.printout() + if o: + with open(pxi_path, "w") as f: + f.write(o.encode("utf-8")) + log.info(pxi_path + " written") + + def py_generate(self): + py_path = os.path.join(args.output, "__init__" + ".py") + if os.path.exists(py_path): + os.remove(py_path) + + with open(py_path, "a") as py_f: + self.gencls.py_generate(self.pygen) + + o = self.pygen.printout() + if o: + py_f.write(o.encode("utf-8")) + py_f.write("\n") + log.info(py_path + " appended") + + +class Alias(object): + def cdef_generate(self, gen): + pass + + +class Struct(object): + def cdef_generate(self, gen): + pass + + +class Enum(object): + def cdef_generate(self, gen): + pass + 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) - #eolian.all_eo_files_parse() - #eolian.all_eot_files_parse() + 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 in eolian.all_classes_get(): - 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(py_path)[0] - - path = [args.output] - namespaces = [] - for ns in cls.namespaces: - namespaces.append(ns.lower()) - filename = cls.name.lower() + ".pxi" - namespace = ".".join(namespaces) - filename = ".".join((namespace, filename)) - path.append(filename) - pxi_path = os.path.join(*path) - - 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") + eolf = File(filename) + eolf.cdefs_generate() + eolf.pyx_generate() def report():