#!/usr/bin/env python3 # encoding: utf-8 """ Pyolian: ctypes bindings for the eolian library. This file (along with eolian_lib.py) implement full pythonic eolian bindings. Those bindings are designed to work at runtime without any prepare/compilation steps required. The only requirement is that we can find the eolian.so/dll somwhere built in the source tree, look at eolian_lib.py to see the searchs performed. You can directly use the python API provided here if you need direct access to eolian, or we suggest to look at the template-based generator.py if you just need to generate some sort of text files out of the eolian database. To use this library from outside this directory, you need to hack sys.path in a way that this folder will be available on PYTHON_PATH, fe: pyolian_path = os.path.join(EFL_ROOT_PATH, 'src', 'scripts') sys.path.insert(0, pyolian_path) from pyolian import eolian from pyolian.generator import Template """ from enum import IntEnum import atexit from ctypes import cast, byref, c_char_p, c_void_p, c_int import ctypes try: from .eolian_lib import lib except ImportError: from eolian_lib import lib _already_halted = False ### Eolian Enums ############################################################ class Eolian_Function_Type(IntEnum): UNRESOLVED = 0 PROPERTY = 1 PROP_SET = 2 PROP_GET = 3 METHOD = 4 FUNCTION_POINTER = 5 class Eolian_Parameter_Dir(IntEnum): UNKNOWN = 0 IN = 1 OUT = 2 INOUT = 3 class Eolian_Class_Type(IntEnum): UNKNOWN_TYPE = 0 REGULAR = 1 ABSTRACT = 2 MIXIN = 3 INTERFACE = 4 class Eolian_Object_Scope(IntEnum): UNKNOWN = 0 PUBLIC = 1 PRIVATE = 2 PROTECTED = 3 class Eolian_Typedecl_Type(IntEnum): UNKNOWN = 0 STRUCT = 1 STRUCT_OPAQUE = 2 ENUM = 3 ALIAS = 4 FUNCTION_POINTER = 5 class Eolian_Type_Type(IntEnum): UNKNOWN_TYPE = 0 VOID = 1 REGULAR = 2 CLASS = 3 UNDEFINED = 4 class Eolian_Type_Builtin_Type(IntEnum): INVALID = 0 BYTE = 1 UBYTE = 2 CHAR = 3 SHORT = 4 USHORT = 5 INT = 6 UINT = 7 LONG = 8 ULONG = 9 LLONG = 10 ULLONG = 11 INT8 = 12 UINT8 = 13 INT16 = 14 UINT16 = 15 INT32 = 16 UINT32 = 17 INT64 = 18 UINT64 = 19 INT128 = 20 UINT128 = 21 SIZE = 22 SSIZE = 23 INTPTR = 24 UINTPTR = 25 PTRDIFF = 26 TIME = 27 FLOAT = 28 DOUBLE = 29 BOOL = 30 VOID = 31 ACCESSOR = 32 ARRAY = 33 ITERATOR = 34 HASH = 35 LIST = 36 INARRAY = 37 INLIST = 38 FUTURE = 39 ANY_VALUE = 40 ANY_VALUE_PTR = 41 MSTRING = 42 STRING = 43 STRINGSHARE = 44 VOID_PTR = 45 FREE_CB = 46 class Eolian_C_Type_Type(IntEnum): DEFAULT = 0 PARAM = 1 RETURN = 2 class Eolian_Expression_Type(IntEnum): UNKNOWN = 0 INT = 1 UINT = 2 LONG = 3 ULONG = 4 LLONG = 5 ULLONG = 6 FLOAT = 7 DOUBLE = 8 STRING = 9 CHAR = 10 NULL = 11 BOOL = 12 NAME = 13 UNARY = 14 BINARY = 15 class Eolian_Expression_Mask(IntEnum): SINT = 1 << 0 UINT = 1 << 1 INT = SINT | UINT FLOAT = 1 << 2 BOOL = 1 << 3 STRING = 1 << 4 CHAR = 1 << 5 NULL = 1 << 6 SIGNED = SINT | FLOAT NUMBER = INT | FLOAT ALL = NUMBER | BOOL | STRING | CHAR | NULL class Eolian_Variable_Type(IntEnum): UNKNOWN = 0 CONSTANT = 1 GLOBAL = 2 class Eolian_Binary_Operator(IntEnum): INVALID = 0 ADD = 1 # + int, float SUB = 2 # - int, float MUL = 3 # * int, float DIV = 4 # / int, float MOD = 5 # % int EQ = 6 # == all types NQ = 7 # != all types GT = 8 # > int, float LT = 9 # < int, float GE = 10 # >= int, float LE = 11 # <= int, float AND = 12 # && all types OR = 13 # || all types BAND = 14 # & int BOR = 15 # | int BXOR = 16 # ^ int LSH = 17 # << int RSH = 18 # >> int class Eolian_Unary_Operator(IntEnum): INVALID = 0 UNM = 1 # - sint UNP = 2 # + sint NOT = 3 # ! int, float, bool BNOT = 4 # ~ int class Eolian_Declaration_Type(IntEnum): UNKNOWN = 0 CLASS = 1 ALIAS = 2 STRUCT = 3 ENUM = 4 VAR = 5 class Eolian_Doc_Token_Type(IntEnum): UNKNOWN = 0 TEXT = 1 REF = 2 MARK_NOTE = 3 MARK_WARNING = 4 MARK_REMARK = 5 MARK_TODO = 6 MARKUP_MONOSPACE = 7 class Eolian_Doc_Ref_Type(IntEnum): INVALID = 0 CLASS = 1 FUNC = 2 EVENT = 3 ALIAS = 4 STRUCT = 5 STRUCT_FIELD = 6 ENUM = 7 ENUM_FIELD = 8 VAR = 9 ### internal Classes ######################################################## class Iterator(object): """ Generic eina iterator wrapper """ def __init__(self, conv_func, iterator): self.next = self.__next__ # py2 compat self._conv = conv_func self._iter = c_void_p(iterator) self._tmp = c_void_p(0) def __iter__(self): return self def __next__(self): if not self._iter or not self._iter.value: raise StopIteration if not lib.eina_iterator_next(self._iter, byref(self._tmp)): lib.eina_iterator_free(self._iter) raise StopIteration return self._conv(self._tmp) def free(self): lib.eina_iterator_free(self._iter) class cached_property(object): """ Decorator to be used on properties that can be cached """ def __init__(self, fget, doc=None): self.fget = fget self.__doc__ = doc or fget.__doc__ self.__name__ = fget.__name__ self.__module__ = fget.__module__ def __get__(self, obj, cls): obj.__dict__[self.__name__] = result = self.fget(obj) return result class EolianBaseObject(object): __instances_cache = {} def __new__(cls, c_obj_pointer=None, *args, **kargs): # cannot cache without a pointer if c_obj_pointer is None: return super().__new__(cls) # cache based on the c pointer value (assume the eolian db is stabe) if isinstance(c_obj_pointer, c_void_p): key = c_obj_pointer.value elif isinstance(c_obj_pointer, int): key = c_obj_pointer else: raise TypeError('Wrong EolianBaseObject constructor pointer!!') # get instance from cache or create a new one if key in cls.__instances_cache: inst = cls.__instances_cache[key] else: inst = cls.__instances_cache[key] = super().__new__(cls) return inst def __init__(self, c_obj_pointer): if isinstance(c_obj_pointer, c_void_p): self._obj = c_void_p(c_obj_pointer.value) elif isinstance(c_obj_pointer, int): self._obj = c_void_p(c_obj_pointer) else: raise TypeError('Invalid constructor of type: %s for class: %s' % ( type(c_obj_pointer), self.__class__.__name__)) def __eq__(self, other): if isinstance(other, EolianBaseObject): return self._obj.value == other._obj.value elif isinstance(other, str): if hasattr(self, 'full_name'): return self.full_name == other elif hasattr(self, 'name'): return self.name == other return False def __hash__(self): return self._obj.value ### Main Eolian Unit ######################################################## class Eolian_Unit(EolianBaseObject): def class_get_by_name(self, class_name): c_cls = lib.eolian_class_get_by_name(self._obj, _str_to_bytes(class_name)) return Class(c_cls) if c_cls else None def class_get_by_file(self, file_name): c_cls = lib.eolian_class_get_by_file(self._obj, _str_to_bytes(file_name)) return Class(c_cls) if c_cls else None @property def all_classes(self): return Iterator(Class, lib.eolian_all_classes_get(self._obj)) @property def all_namespaces(self): # TODO find a better way to find namespaces (maybe inside eolian?) nspaces = set() for obj in self.all_classes: nspaces.add(Namespace(self, obj.namespace)) for obj in self.typedecl_all_aliases: nspaces.add(Namespace(self, obj.namespace)) for obj in self.typedecl_all_structs: nspaces.add(Namespace(self, obj.namespace)) for obj in self.typedecl_all_enums: nspaces.add(Namespace(self, obj.namespace)) return sorted(nspaces) def namespace_get_by_name(self, name): return Namespace(self, name) @property def typedecl_all_enums(self): return Iterator(Typedecl, lib.eolian_typedecl_all_enums_get(self._obj)) def typedecl_enum_get_by_name(self, name): c_tdecl = lib.eolian_typedecl_enum_get_by_name(self._obj, _str_to_bytes(name)) return Typedecl(c_tdecl) if c_tdecl else None def typedecl_enums_get_by_file(self, fname): return Iterator(Typedecl, lib.eolian_typedecl_enums_get_by_file(self._obj, _str_to_bytes(fname))) @property def typedecl_all_structs(self): return Iterator(Typedecl, lib.eolian_typedecl_all_structs_get(self._obj)) def typedecl_struct_get_by_name(self, name): c_tdecl = lib.eolian_typedecl_struct_get_by_name(self._obj, _str_to_bytes(name)) return Typedecl(c_tdecl) if c_tdecl else None def typedecl_structs_get_by_file(self, fname): return Iterator(Typedecl, lib.eolian_typedecl_structs_get_by_file(self._obj, _str_to_bytes(fname))) @property def typedecl_all_aliases(self): return Iterator(Typedecl, lib.eolian_typedecl_all_aliases_get(self._obj)) def typedecl_alias_get_by_name(self, name): c_tdecl = lib.eolian_typedecl_alias_get_by_name(self._obj, _str_to_bytes(name)) return Typedecl(c_tdecl) if c_tdecl else None def typedecl_aliases_get_by_file(self, fname): return Iterator(Typedecl, lib.eolian_typedecl_aliases_get_by_file(self._obj, _str_to_bytes(fname))) @property def variable_all_constants(self): return Iterator(Variable, lib.eolian_variable_all_constants_get(self._obj)) def variable_constant_get_by_name(self, name): c_var = lib.eolian_variable_constant_get_by_name(self._obj, _str_to_bytes(name)) return Variable(c_var) if c_var else None def variable_constants_get_by_file(self, fname): return Iterator(Variable, lib.eolian_variable_constants_get_by_file(self._obj, _str_to_bytes(fname))) @property def variable_all_globals(self): return Iterator(Variable, lib.eolian_variable_all_globals_get(self._obj)) def variable_global_get_by_name(self, name): c_var = lib.eolian_variable_global_get_by_name(self._obj, _str_to_bytes(name)) return Variable(c_var) if c_var else None def variable_globals_get_by_file(self, fname): return Iterator(Variable, lib.eolian_variable_globals_get_by_file(self._obj, _str_to_bytes(fname))) @property def all_declarations(self): return Iterator(Declaration, lib.eolian_all_declarations_get(self._obj)) def declaration_get_by_name(self, name): c_decl = lib.eolian_declaration_get_by_name(self._obj, _str_to_bytes(name)) return Declaration(c_decl) if c_decl else None def declarations_get_by_file(self, fname): return Iterator(Declaration, lib.eolian_declarations_get_by_file(self._obj, _str_to_bytes(fname))) class Eolian(Eolian_Unit): def __init__(self): self._obj = lib.eolian_new() # Eolian * def __del__(self): if not _already_halted: # do not free after eolian_shutdown lib.eolian_free(self._obj) def file_parse(self, filepath): c_unit = lib.eolian_file_parse(self._obj, _str_to_bytes(filepath)) return Eolian_Unit(c_unit) if c_unit else None @property def all_eo_file_paths(self): return Iterator(_str_to_py, lib.eolian_all_eo_file_paths_get(self._obj)) @property def all_eot_file_paths(self): return Iterator(_str_to_py, lib.eolian_all_eot_file_paths_get(self._obj)) @property def all_eo_files(self): return Iterator(_str_to_py, lib.eolian_all_eo_files_get(self._obj)) @property def all_eot_files(self): return Iterator(_str_to_py, lib.eolian_all_eot_files_get(self._obj)) def directory_scan(self, dir_path): return bool(lib.eolian_directory_scan(self._obj, _str_to_bytes(dir_path))) def system_directory_scan(self): return bool(lib.eolian_system_directory_scan(self._obj)) def all_eo_files_parse(self): return bool(lib.eolian_all_eo_files_parse(self._obj)) def all_eot_files_parse(self): return bool(lib.eolian_all_eot_files_parse(self._obj)) ### Namespace Utility Class ################################################# class Namespace(object): def __init__(self, unit, namespace_name): self._name = namespace_name self._unit = unit def __repr__(self): return "".format(self) def __eq__(self, other): return self.name == other.name def __lt__(self, other): return self.name < other.name def __gt__(self, other): return self.name > other.name def __hash__(self): return hash(self._name) @property def name(self): return self._name @property def namespaces(self): return self._name.split('.') @property def classes(self): return [ c for c in self._unit.all_classes if c.namespace == self._name ] @property def regulars(self): return [ c for c in self._unit.all_classes if c.type == Eolian_Class_Type.REGULAR and c.namespace == self._name] @property def mixins(self): return [ c for c in self._unit.all_classes if c.type == Eolian_Class_Type.MIXIN and c.namespace == self._name] @property def interfaces(self): return [ c for c in self._unit.all_classes if c.type == Eolian_Class_Type.INTERFACE and c.namespace == self._name] @property def aliases(self): return [ td for td in self._unit.typedecl_all_aliases if td.namespace == self._name] @property def structs(self): return [ td for td in self._unit.typedecl_all_structs if td.namespace == self._name] @property def enums(self): return [ td for td in self._unit.typedecl_all_enums if td.namespace == self._name] ### Eolian Classes ########################################################## class Class(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_class_name_get(self._obj)) @cached_property def full_name(self): return _str_to_py(lib.eolian_class_full_name_get(self._obj)) @cached_property def c_name(self): s = lib.eolian_class_c_name_get(self._obj) ret = _str_to_py(s) lib.eina_stringshare_del(c_void_p(s)) return ret @cached_property def c_get_function_name(self): s = lib.eolian_class_c_get_function_name_get(self._obj) ret = _str_to_py(s) lib.eina_stringshare_del(c_void_p(s)) return ret @cached_property def type(self): return Eolian_Class_Type(lib.eolian_class_type_get(self._obj)) @cached_property def data_type(self): return _str_to_py(lib.eolian_class_data_type_get(self._obj)) @cached_property def c_data_type(self): s = lib.eolian_class_c_data_type_get(self._obj) ret = _str_to_py(s) lib.eina_stringshare_del(c_void_p(s)) return ret @cached_property def legacy_prefix(self): return _str_to_py(lib.eolian_class_legacy_prefix_get(self._obj)) @cached_property def eo_prefix(self): return _str_to_py(lib.eolian_class_eo_prefix_get(self._obj)) @cached_property def event_prefix(self): return _str_to_py(lib.eolian_class_event_prefix_get(self._obj)) @cached_property def documentation(self): c_doc = lib.eolian_class_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None @property def constructors(self): return Iterator(Constructor, lib.eolian_class_constructors_get(self._obj)) @property def events(self): return Iterator(Event, lib.eolian_class_events_get(self._obj)) def event_get_by_name(self, event_name): c_event = lib.eolian_class_event_get_by_name(self._obj, _str_to_bytes(event_name)) return Event(c_event) if c_event else None @property def inherits(self): return Iterator(Class, lib.eolian_class_inherits_get(self._obj)) @cached_property def inherits_full(self): L = [] def do_class_recursive(cls): for other in cls.inherits: if not other in L: L.append(other) do_class_recursive(other) do_class_recursive(self) return L @cached_property def hierarchy(self): L = [] base = self.base_class while base: L.append(base) base = base.base_class return L @cached_property def base_class(self): inherits = list(self.inherits) if len(inherits) > 0: return inherits[0] @property def namespaces(self): return Iterator(_str_to_py, lib.eolian_class_namespaces_get(self._obj)) @cached_property def namespace(self): return '.'.join(self.namespaces) @cached_property def file(self): return _str_to_py(lib.eolian_class_file_get(self._obj)) @cached_property def ctor_enable(self): return bool(lib.eolian_class_ctor_enable_get(self._obj)) @cached_property def dtor_enable(self): return bool(lib.eolian_class_dtor_enable_get(self._obj)) def function_get_by_name(self, func_name, ftype=Eolian_Function_Type.UNRESOLVED): f = lib.eolian_class_function_get_by_name(self._obj, _str_to_bytes(func_name), ftype) return Function(f) if f else None def functions_get(self, ftype): return Iterator(Function, lib.eolian_class_functions_get(self._obj, ftype)) @property def methods(self): return self.functions_get(Eolian_Function_Type.METHOD) @property def properties(self): return self.functions_get(Eolian_Function_Type.PROPERTY) @property def implements(self): return Iterator(Implement, lib.eolian_class_implements_get(self._obj)) @property def parts(self): return Iterator(Part, lib.eolian_class_parts_get(self._obj)) class Part(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_part_name_get(self._obj)) @cached_property def class_(self): return Class(lib.eolian_part_class_get(self._obj)) @cached_property def documentation(self): c_doc = lib.eolian_part_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None class Constructor(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def full_name(self): return _str_to_py(lib.eolian_constructor_full_name_get(self._obj)) @cached_property def function(self): return Function(lib.eolian_constructor_function_get(self._obj)) @cached_property def is_optional(self): return bool(lib.eolian_constructor_is_optional(self._obj)) @cached_property def class_(self): return Class(lib.eolian_constructor_class_get(self._obj)) class Event(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_event_name_get(self._obj)) @cached_property def c_name(self): s = lib.eolian_event_c_name_get(self._obj) ret = _str_to_py(s) lib.eina_stringshare_del(c_void_p(s)) return ret @cached_property def type(self): c_type = lib.eolian_event_type_get(self._obj) return Type(c_type) if c_type else None @cached_property def documentation(self): c_doc = lib.eolian_event_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None @cached_property def scope(self): return Eolian_Object_Scope(lib.eolian_event_scope_get(self._obj)) @cached_property def is_beta(self): return bool(lib.eolian_event_is_beta(self._obj)) @cached_property def is_hot(self): return bool(lib.eolian_event_is_hot(self._obj)) @cached_property def is_restart(self): return bool(lib.eolian_event_is_restart(self._obj)) class Function(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_function_name_get(self._obj)) def full_c_name_get(self, ftype, use_legacy=False): s = lib.eolian_function_full_c_name_get(self._obj, ftype, use_legacy) ret = _str_to_py(s) lib.eina_stringshare_del(c_void_p(s)) return ret @cached_property def full_c_method_name(self): return self.full_c_name_get(Eolian_Function_Type.METHOD) @cached_property def full_c_getter_name(self): return self.full_c_name_get(Eolian_Function_Type.PROP_GET) @cached_property def full_c_setter_name(self): return self.full_c_name_get(Eolian_Function_Type.PROP_SET) @cached_property def full_c_method_name_legacy(self): return self.full_c_name_get(Eolian_Function_Type.METHOD, True) @cached_property def full_c_getter_name_legacy(self): return self.full_c_name_get(Eolian_Function_Type.PROP_GET, True) @cached_property def full_c_setter_name_legacy(self): return self.full_c_name_get(Eolian_Function_Type.PROP_SET, True) @cached_property def type(self): return Eolian_Function_Type(lib.eolian_function_type_get(self._obj)) def scope_get(self, ftype): return Eolian_Object_Scope(lib.eolian_function_scope_get(self._obj, ftype)) @cached_property def method_scope(self): return self.scope_get(Eolian_Function_Type.METHOD) @cached_property def getter_scope(self): return self.scope_get(Eolian_Function_Type.PROP_GET) @cached_property def setter_scope(self): return self.scope_get(Eolian_Function_Type.PROP_SET) def legacy_get(self, ftype): return _str_to_py(lib.eolian_function_legacy_get(self._obj, ftype)) def is_legacy_only(self, ftype): return bool(lib.eolian_function_is_legacy_only(self._obj, ftype)) @cached_property def is_class(self): return bool(lib.eolian_function_is_class(self._obj)) @cached_property def is_beta(self): return bool(lib.eolian_function_is_beta(self._obj)) @cached_property def object_is_const(self): return bool(lib.eolian_function_object_is_const(self._obj)) @cached_property def class_(self): c_cls = lib.eolian_function_class_get(self._obj) return Class(c_cls) if c_cls else None def is_constructor(self, klass): return bool(lib.eolian_function_is_constructor(self._obj, klass._obj)) # @cached_property # def is_function_pointer(self): # return bool(lib.eolian_function_is_function_pointer(self._obj)) @property def parameters(self): return Iterator(Function_Parameter, lib.eolian_function_parameters_get(self._obj)) def values_get(self, ftype): # TODO rename in property_values_get (or implement a proper Property class?) return Iterator(Function_Parameter, lib.eolian_property_values_get(self._obj, ftype)) @property def getter_values(self): # TODO rename ... return self.values_get(Eolian_Function_Type.PROP_GET) @property def setter_values(self): # TODO rename ... return self.values_get(Eolian_Function_Type.PROP_SET) def keys_get(self, ftype): # TODO rename in property_keys_get (or implement a proper Property class?) return Iterator(Function_Parameter, lib.eolian_property_keys_get(self._obj, ftype)) @property def getter_keys(self): # TODO rename ... return self.keys_get(Eolian_Function_Type.PROP_GET) @property def setter_keys(self): # TODO rename ... return self.keys_get(Eolian_Function_Type.PROP_SET) def return_type_get(self, ftype): c_type = lib.eolian_function_return_type_get(self._obj, ftype) return Type(c_type) if c_type else None def return_default_value(self, ftye): c_expr = lib.eolian_function_return_default_value_get(sel._obj, ftype) return Expression(c_expr) if c_expr else None def return_documentation(self, ftype): c_doc = lib.eolian_function_return_documentation_get(self._obj, ftype) return Documentation(c_doc) if c_doc else None def return_is_warn_unused(self, ftype): return bool(lib.eolian_function_return_is_warn_unused(self._obj, ftype)) @cached_property def method_return_type(self): return self.return_type_get(Eolian_Function_Type.METHOD) @cached_property def getter_return_type(self): return self.return_type_get(Eolian_Function_Type.PROP_GET) @cached_property def setter_return_type(self): return self.return_type_get(Eolian_Function_Type.PROP_SET) @cached_property def prop_readable(self): # TODO: maybe there is a better way to do this... ftype = Eolian_Function_Type.PROP_GET scope = lib.eolian_function_scope_get(self._obj, ftype) return True if scope != Eolian_Object_Scope.UNKNOWN else False @cached_property def prop_writable(self): # TODO: maybe there is a better way to do this... ftype = Eolian_Function_Type.PROP_SET scope = lib.eolian_function_scope_get(self._obj, ftype) return True if scope != Eolian_Object_Scope.UNKNOWN else False @cached_property def implement(self): c_impl = lib.eolian_function_implement_get(self._obj) return Implement(c_impl) if c_impl else None class Function_Parameter(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_parameter_name_get(self._obj)) @cached_property def direction(self): return Eolian_Parameter_Dir(lib.eolian_parameter_direction_get(self._obj)) @cached_property def documentation(self): c_doc = lib.eolian_parameter_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None @cached_property def is_nonull(self): return bool(lib.eolian_parameter_is_nonull(self._obj)) @cached_property def is_nullable(self): return bool(lib.eolian_parameter_is_nullable(self._obj)) @cached_property def is_optional(self): return bool(lib.eolian_parameter_is_optional(self._obj)) @cached_property def type(self): c_type = lib.eolian_parameter_type_get(self._obj) return Type(c_type) if c_type else None @cached_property def default_value(self): c_expr = lib.eolian_parameter_default_value_get(self._obj) return Expression(c_expr) if c_expr else None class Implement(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def full_name(self): return _str_to_py(lib.eolian_implement_full_name_get(self._obj)) @cached_property def class_(self): c_cls = lib.eolian_implement_class_get(self._obj) return Class(c_cls) if c_cls else None @cached_property def function(self): c_func = lib.eolian_implement_function_get(self._obj, None) return Function(c_func) if c_func else None def documentation_get(self, ftype=Eolian_Function_Type.METHOD): # something strange in this eolian api :/ (see 'documentation' property c_doc = lib.eolian_implement_documentation_get(self._obj, ftype) return Documentation(c_doc) if c_doc else None # TODO implement util properties for documentation_get def is_auto(self, ftype=Eolian_Function_Type.METHOD): return bool(lib.eolian_implement_is_auto(self._obj, ftype)) # TODO implement util properties for is_auto def is_empty(self, ftype=Eolian_Function_Type.METHOD): return bool(lib.eolian_implement_is_empty(self._obj, ftype)) # TODO implement util properties for is_empty def is_pure_virtual(self, ftype=Eolian_Function_Type.METHOD): return bool(lib.eolian_implement_is_pure_virtual(self._obj, ftype)) # TODO implement util properties for is_pure_virtual @cached_property def is_prop_set(self): return bool(lib.eolian_implement_is_prop_set(self._obj)) @cached_property def is_prop_get(self): return bool(lib.eolian_implement_is_prop_get(self._obj)) @property def is_property(self): return self.is_prop_get or self.is_prop_set @property def is_method(self): return not self.is_property class Type(EolianBaseObject): def __repr__(self): # return "".format(self) return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_type_name_get(self._obj)) @cached_property def full_name(self): return _str_to_py(lib.eolian_type_full_name_get(self._obj)) @property def namespaces(self): return Iterator(_str_to_py, lib.eolian_type_namespaces_get(self._obj)) @cached_property def namespace(self): return '.'.join(self.namespaces) @cached_property def free_func(self): return _str_to_py(lib.eolian_type_free_func_get(self._obj)) @cached_property def type(self): return Eolian_Type_Type(lib.eolian_type_type_get(self._obj)) @cached_property def builtin_type(self): return Eolian_Type_Builtin_Type(lib.eolian_type_builtin_type_get(self._obj)) @cached_property def c_type(self): return _str_to_py(lib.eolian_type_c_type_get(self._obj)) @cached_property def typedecl(self): c_tdecl = lib.eolian_type_typedecl_get(self._obj) return Typedecl(c_tdecl) if c_tdecl else None @cached_property def base_type(self): c_type = lib.eolian_type_base_type_get(self._obj) return Type(c_type) if c_type else None @cached_property def next_type(self): c_type = lib.eolian_type_next_type_get(self._obj) return Type(c_type) if c_type else None @cached_property def aliased_base(self): c_type = lib.eolian_type_aliased_base_get(self._obj) return Type(c_type) if c_type else None @cached_property def class_(self): c_cls = lib.eolian_type_class_get(self._obj) return Class(c_cls) if c_cls else None @cached_property def file(self): return _str_to_py(lib.eolian_type_file_get(self._obj)) @cached_property def is_owned(self): return bool(lib.eolian_type_is_owned(self._obj)) @cached_property def is_const(self): return bool(lib.eolian_type_is_const(self._obj)) @cached_property def is_ptr(self): return bool(lib.eolian_type_is_ptr(self._obj)) class Typedecl(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_typedecl_name_get(self._obj)) @cached_property def full_name(self): return _str_to_py(lib.eolian_typedecl_full_name_get(self._obj)) @cached_property def file(self): return _str_to_py(lib.eolian_typedecl_file_get(self._obj)) @cached_property def type(self): return Eolian_Typedecl_Type(lib.eolian_typedecl_type_get(self._obj)) @cached_property def c_type(self): return _str_to_py(lib.eolian_typedecl_c_type_get(self._obj)) @property def namespaces(self): return Iterator(_str_to_py, lib.eolian_typedecl_namespaces_get(self._obj)) @cached_property def namespace(self): return '.'.join(self.namespaces) @cached_property def free_func(self): return _str_to_py(lib.eolian_typedecl_free_func_get(self._obj)) @cached_property def is_extern(self): return bool(lib.eolian_typedecl_is_extern(self._obj)) @property def enum_fields(self): return Iterator(Enum_Type_Field, lib.eolian_typedecl_enum_fields_get(self._obj)) def enum_field_get(self, field): c_field = lib.eolian_typedecl_enum_field_get(self._obj, _str_to_bytes(field)) return Enum_Type_Field(c_field) if c_field else None @property def struct_fields(self): return Iterator(Struct_Type_Field, lib.eolian_typedecl_struct_fields_get(self._obj)) def struct_field_get(self, field): c_field = lib.eolian_typedecl_struct_field_get(self._obj, _str_to_bytes(field)) return Struct_Type_Field(c_field) if c_field else None @cached_property def base_type(self): c_type = lib.eolian_typedecl_base_type_get(self._obj) return Type(c_type) if c_type else None @cached_property def aliased_base(self): c_type = lib.eolian_typedecl_aliased_base_get(self._obj) return Type(c_type) if c_type else None @cached_property def documentation(self): c_doc = lib.eolian_typedecl_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None @cached_property def enum_legacy_prefix(self): return _str_to_py(lib.eolian_typedecl_enum_legacy_prefix_get(self._obj)) @cached_property def function_pointer(self): c_func = lib.eolian_typedecl_function_pointer_get(self._obj) return Function(c_func) if c_func else None class Enum_Type_Field(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_typedecl_enum_field_name_get(self._obj)) @cached_property def c_name(self): s = lib.eolian_typedecl_enum_field_c_name_get(self._obj) ret = _str_to_py(s) lib.eina_stringshare_del(c_void_p(s)) return ret @cached_property def value(self): c_expr = lib.eolian_typedecl_enum_field_value_get(self._obj, True) return Expression(c_expr) if c_expr else None @cached_property def documentation(self): c_doc = lib.eolian_typedecl_enum_field_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None class Struct_Type_Field(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_typedecl_struct_field_name_get(self._obj)) @cached_property def type(self): c_type = lib.eolian_typedecl_struct_field_type_get(self._obj) return Type(c_type) if c_type else None @cached_property def documentation(self): c_doc = lib.eolian_typedecl_struct_field_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None class Expression(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def type(self): return Eolian_Expression_Type(lib.eolian_expression_type_get(self._obj)) # TODO: EAPI Eolian_Value eolian_expression_value_get(const Eolian_Expression *expr); @cached_property def serialize(self): s = lib.eolian_expression_serialize(self._obj) ret = _str_to_py(s) lib.eina_stringshare_del(c_void_p(s)) return ret @cached_property def binary_operator(self): c_op = lib.eolian_expression_binary_operator_get(self._obj) return Eolian_Binary_Operator(c_op) if c_op is not None else None @cached_property def binary_lhs(self): c_expr = lib.eolian_expression_binary_lhs_get(self._obj) return Expression(c_expr) if c_expr else None @cached_property def binary_rhs(self): c_expr = lib.eolian_expression_binary_rhs_get(self._obj) return Expression(c_expr) if c_expr else None @cached_property def unary_operator(self): c_op = lib.eolian_expression_unary_operator_get(self._obj) return Eolian_Unary_Operator(c_op) if c_op is not None else None @cached_property def unary_expression(self): c_expr = lib.eolian_expression_unary_expression_get(self._obj) return Expression(c_expr) if c_expr is not None else None class Variable(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_variable_name_get(self._obj)) @cached_property def full_name(self): return _str_to_py(lib.eolian_variable_full_name_get(self._obj)) @property def namespaces(self): return Iterator(_str_to_py, lib.eolian_variable_namespaces_get(self._obj)) @cached_property def namespace(self): return '.'.join(self.namespaces) @cached_property def type(self): return Eolian_Variable_Type(lib.eolian_variable_type_get(self._obj)) @cached_property def value(self): c_expr = lib.eolian_variable_value_get(self._obj) return Expression(c_expr) if c_expr else None @cached_property def file(self): return _str_to_py(lib.eolian_variable_file_get(self._obj)) @cached_property def base_type(self): c_type = lib.eolian_variable_base_type_get(self._obj) return Type(c_type) if c_type else None @cached_property def is_extern(self): return bool(lib.eolian_variable_is_extern(self._obj)) @cached_property def documentation(self): c_doc = lib.eolian_variable_documentation_get(self._obj) return Documentation(c_doc) if c_doc else None class Declaration(EolianBaseObject): def __repr__(self): return "".format(self) @cached_property def name(self): return _str_to_py(lib.eolian_declaration_name_get(self._obj)) @cached_property def type(self): return Eolian_Declaration_Type(lib.eolian_declaration_type_get(self._obj)) @cached_property def class_(self): c_cls = lib.eolian_declaration_class_get(self._obj) return Class(c_cls) if c_cls else None @cached_property def data_type(self): c_typedec = lib.eolian_declaration_data_type_get(self._obj) return Typedecl(c_typedec) if c_typedec else None @cached_property def variable(self): c_var = lib.eolian_declaration_variable_get(self._obj) return Variable(c_var) if c_var else None class _Eolian_Doc_Token_Struct(ctypes.Structure): _fields_ = [("type", c_int), ("text", c_char_p), ("text_end", c_char_p)] class Documentation(EolianBaseObject): # OK (1 TODO Unit*) # def __repr__(self): # return "".format(self) # this is too much for py, just use string.split('\n\n') instead # def string_split(self, string): # c_list = lib.eolian_documentation_string_split @cached_property def summary(self): return _str_to_py(lib.eolian_documentation_summary_get(self._obj)) @cached_property def description(self): return _str_to_py(lib.eolian_documentation_description_get(self._obj)) @cached_property def since(self): return _str_to_py(lib.eolian_documentation_since_get(self._obj)) @cached_property def summary_tokens(self): """ return a list of paragraphs, each one is a list of tokens """ return self._tokenize(self.summary) @cached_property def description_tokens(self): """ return a list of paragraphs, each one is a list of tokens """ return self._tokenize(self.description) @classmethod def _tokenize(cls, full_text): paragraphs = [] if not full_text: return paragraphs tok = _Eolian_Doc_Token_Struct() for paragraph in full_text.split('\n\n'): tokens = [] c_paragraph = _str_to_bytes(paragraph) # keep c_paragraph alive ! lib.eolian_doc_token_init(byref(tok)) next_chunk = lib.eolian_documentation_tokenize(c_paragraph, byref(tok)) while next_chunk: typ = lib.eolian_doc_token_type_get(byref(tok)) txt = lib.eolian_doc_token_text_get(byref(tok)) # ref = # TODO ... Stupido parametro '*unit' :( tokens.append(Documentation_Token(typ, txt)) lib.free(c_void_p(txt)) next_chunk = lib.eolian_documentation_tokenize(c_char_p(next_chunk), byref(tok)) paragraphs.append(tokens) return paragraphs class Documentation_Token(object): def __init__(self, c_token_type, c_text): self._type = Eolian_Doc_Token_Type(c_token_type) self._text = _str_to_py(c_text) self._ref = None # TODO def __repr__(self): t = self.text if len(self.text) < 40 else self.text[:40] + '...' return "".format( self.type.name, t, len(self.text)) @property def type(self): return self._type @property def text(self): return self._text @property def ref(self): return self._ref ### internal string encode/decode ########################################### def _str_to_bytes(s): return s.encode('utf-8') def _str_to_py(s): if s is None: return None if isinstance(s, bytes): return s.decode('utf-8') if isinstance(s, c_char_p): return s.value.decode('utf-8') if isinstance(s, c_void_p): return cast(s, c_char_p).value.decode('utf-8') if isinstance(s, int): return cast(s, c_char_p).value.decode('utf-8') print('WARNING !!!!!!!!! Unknown type: %s' % type(s)) ### module init/shutdown #################################################### def _cleanup(): global _already_halted lib.eolian_shutdown() _already_halted = True lib.eolian_init() atexit.register(_cleanup) ### API coverage statistics ################################################# if __name__ == '__main__': import sys import os import re # find Eolian.h in source tree script_path = os.path.dirname(os.path.realpath(__file__)) eolian_header = os.path.join(script_path, '..', '..', 'lib', 'eolian', 'Eolian.h') eolian_header = os.path.abspath(eolian_header) # prepare the two regexp flags = re.S | re.M DEFINED_RE = re.compile('^EAPI[\w\n *]*(eolian_\w*)\([\w *,]*\);', flags) USED_RE = re.compile('lib\.(eolian_[\w]*)\(', flags) # extract all EAPI functions from Eolian.h defined_funcs = [] with open(eolian_header, 'r') as fh: header = fh.read() for match in re.finditer(DEFINED_RE, header): func_name = match.group(1) defined_funcs.append(func_name) defined_funcs = set(defined_funcs) # extract all called functions in eolian.py (this file) used_funcs = [] with open(__file__, 'r') as fh: source = fh.read() for match in re.finditer(USED_RE, source): func_name = match.group(1) used_funcs.append(func_name) used_funcs = set(used_funcs) # show general info num_def = len(defined_funcs) num_usd = len(used_funcs) print('Pyolian coverage results') print('========================') print('Found %d functions defined in Eolian.h (%s)' % (num_def, eolian_header)) print('Found %d functions used in eolian.py (hopefully not commented out)' % num_usd) print('Total API coverage %.1f%%' % (num_usd / num_def * 100)) print() # list all missing functions missing = defined_funcs - used_funcs print('{} Missing functions in eolian.py'.format(len(missing))) print('=================================') for i, func_name in enumerate(sorted(missing), 1): print('{:02d}. {}'.format(i, func_name)) print() # List all functions found in Eolian.h (--all option) if '--all' in sys.argv: print('{} functions found in Eolian.h'.format(num_def)) print('===============================') for i, func_name in enumerate(sorted(defined_funcs), 1): print('{:03d}. {}'.format(i, func_name)) print() else: print('Additional arguments') print('====================') print(' --all To list all functions found in Eolian.h') print()