efl/src/scripts/pyolian/eolian.py

1486 lines
44 KiB
Python

#!/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
SLICE = 31
RW_SLICE = 32
VOID = 33
ACCESSOR = 34
ARRAY = 35
FUTURE = 36
ITERATOR = 37
HASH = 38
LIST = 39
ANY_VALUE = 40
ANY_VALUE_REF = 41
BINBUF = 42
EVENT = 43
MSTRING = 44
STRING = 45
STRINGSHARE = 46
STRBUF = 47
VOID_PTR = 48
FREE_CB = 49
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_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
### 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 stable)
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, 'name'):
return self.name == other
elif hasattr(self, 'short_name'):
return self.short_name == other
return False
def __gt__(self, other):
if isinstance(other, EolianBaseObject):
if hasattr(self, 'name'):
return self.name > other.name
elif hasattr(self, 'short_name'):
return self.short_name > other.short_name
def __hash__(self):
return self._obj.value
@property
def _as_parameter_(self):
""" Used by ctypes to convert instances when calling C functions """
return self._obj
### Main Eolian Unit ########################################################
class Eolian_Unit(EolianBaseObject):
def __repr__(self):
return "<eolian.Eolian_Unit '{0.file}'>".format(self)
@property
def children(self):
return Iterator(Eolian_Unit, lib.eolian_unit_children_get(self))
@cached_property
def file(self):
return _str_to_py(lib.eolian_unit_file_get(self))
@cached_property
def state(self):
c_state = lib.eolian_unit_state_get(self)
return Eolian_State(c_state) if c_state else None
@property
def objects(self):
return Iterator(Object, lib.eolian_unit_objects_get(self))
def object_by_name_get(self, name):
c_obj = lib.eolian_unit_object_by_name_get(self, _str_to_bytes(name))
return Object(c_obj) if c_obj else None
@property
def classes(self):
return Iterator(Class, lib.eolian_unit_classes_get(self))
def class_by_name_get(self, class_name):
c_cls = lib.eolian_unit_class_by_name_get(self, _str_to_bytes(class_name))
return Class(c_cls) if c_cls else None
@property
def constants(self):
return Iterator(Variable, lib.eolian_unit_constants_get(self))
def constant_by_name_get(self, name):
c_var = lib.eolian_unit_constant_by_name_get(self, _str_to_bytes(name))
return Variable(c_var) if c_var else None
@property
def globals(self):
return Iterator(Variable, lib.eolian_unit_globals_get(self))
def global_by_name_get(self, name):
c_var = lib.eolian_unit_global_by_name_get(self, _str_to_bytes(name))
return Variable(c_var) if c_var else None
@property
def enums(self):
return Iterator(Typedecl, lib.eolian_unit_enums_get(self))
def enum_by_name_get(self, name):
c_tdecl = lib.eolian_unit_enum_by_name_get(self, _str_to_bytes(name))
return Typedecl(c_tdecl) if c_tdecl else None
@property
def structs(self):
return Iterator(Typedecl, lib.eolian_unit_structs_get(self))
def struct_by_name_get(self, name):
c_tdecl = lib.eolian_unit_struct_by_name_get(self, _str_to_bytes(name))
return Typedecl(c_tdecl) if c_tdecl else None
@property
def aliases(self):
return Iterator(Typedecl, lib.eolian_unit_aliases_get(self))
def alias_by_name_get(self, name):
c_tdecl = lib.eolian_unit_alias_by_name_get(self, _str_to_bytes(name))
return Typedecl(c_tdecl) if c_tdecl else None
@property
def all_namespaces(self):
# TODO find a better way to find namespaces (maybe inside eolian?)
nspaces = set()
for obj in self.classes:
nspaces.add(Namespace(self, obj.namespace))
for obj in self.aliases:
nspaces.add(Namespace(self, obj.namespace))
for obj in self.structs:
nspaces.add(Namespace(self, obj.namespace))
for obj in self.enums:
nspaces.add(Namespace(self, obj.namespace))
return sorted(nspaces)
def namespace_get_by_name(self, name):
return Namespace(self, name)
class Eolian_State(Eolian_Unit):
def __init__(self, c_state=None):
if c_state is None:
c_state = lib.eolian_state_new() # Eolian_State *
EolianBaseObject.__init__(self, c_state)
def __del__(self):
if not _already_halted: # do not free after eolian_shutdown
lib.eolian_state_free(self)
def __repr__(self):
return "<eolian.Eolian_State, %d units loaded>" % len(list(self.units))
def file_parse(self, filename):
c_unit = lib.eolian_state_file_parse(self, _str_to_bytes(filename))
return Eolian_Unit(c_unit) if c_unit else None
def file_path_parse(self, filepath):
c_unit = lib.eolian_state_file_path_parse(self, _str_to_bytes(filepath))
return Eolian_Unit(c_unit) if c_unit else None
@property
def eo_file_paths(self):
return Iterator(_str_to_py, lib.eolian_state_eo_file_paths_get(self))
@property
def eot_file_paths(self):
return Iterator(_str_to_py, lib.eolian_state_eot_file_paths_get(self))
@property
def eo_files(self):
return Iterator(_str_to_py, lib.eolian_state_eo_files_get(self))
@property
def eot_files(self):
return Iterator(_str_to_py, lib.eolian_state_eot_files_get(self))
def directory_add(self, dir_path):
return bool(lib.eolian_state_directory_add(self, _str_to_bytes(dir_path)))
def system_directory_add(self):
return bool(lib.eolian_state_system_directory_add(self))
def all_eo_files_parse(self):
return bool(lib.eolian_state_all_eo_files_parse(self))
def all_eot_files_parse(self):
return bool(lib.eolian_state_all_eot_files_parse(self))
def unit_by_file_get(self, file_name):
c_unit = lib.eolian_state_unit_by_file_get(self, _str_to_bytes(file_name))
return Eolian_Unit(c_unit) if c_unit else None
@property
def units(self):
return Iterator(Eolian_Unit, lib.eolian_state_units_get(self))
def objects_by_file_get(self, file_name):
return Iterator(Object,
lib.eolian_state_objects_by_file_get(self, _str_to_bytes(file_name)))
def class_by_file_get(self, file_name):
c_cls = lib.eolian_state_class_by_file_get(self, _str_to_bytes(file_name))
return Class(c_cls) if c_cls else None
def constants_by_file_get(self, file_name):
return Iterator(Variable,
lib.eolian_state_constants_by_file_get(self, _str_to_bytes(file_name)))
def globals_by_file_get(self, file_name):
return Iterator(Variable,
lib.eolian_state_globals_by_file_get(self, _str_to_bytes(file_name)))
def aliases_by_file_get(self, file_name):
return Iterator(Typedecl,
lib.eolian_state_aliases_by_file_get(self, _str_to_bytes(file_name)))
def structs_by_file_get(self, file_name):
return Iterator(Typedecl,
lib.eolian_state_structs_by_file_get(self, _str_to_bytes(file_name)))
def enums_by_file_get(self, file_name):
return Iterator(Typedecl,
lib.eolian_state_enums_by_file_get(self, _str_to_bytes(file_name)))
### Namespace Utility Class #################################################
class Namespace(object):
def __init__(self, unit, namespace_name):
self._name = namespace_name
self._unit = unit
def __repr__(self):
return "<eolian.Namespace '{0._name}'>".format(self)
def __eq__(self, other):
if isinstance(other, Namespace):
return self.name == other.name
if isinstance(other, str):
return self.name == other
raise TypeError('Namespace can only compare with Namespace or str')
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 sub_namespaces(self):
base = self._name + '.'
deep = self._name.count('.') + 1
return [ ns for ns in self._unit.all_namespaces
if ns.name.startswith(base) and ns.name.count('.') == deep ]
@property
def classes(self):
return sorted([ c for c in self._unit.classes
if c.namespace == self._name ])
@property
def regulars(self):
return sorted([ c for c in self._unit.classes
if c.type == Eolian_Class_Type.REGULAR and
c.namespace == self._name])
@property
def abstracts(self):
return sorted([ c for c in self._unit.classes
if c.type == Eolian_Class_Type.ABSTRACT and
c.namespace == self._name])
@property
def mixins(self):
return sorted([ c for c in self._unit.classes
if c.type == Eolian_Class_Type.MIXIN and
c.namespace == self._name])
@property
def interfaces(self):
return sorted([ c for c in self._unit.classes
if c.type == Eolian_Class_Type.INTERFACE and
c.namespace == self._name])
@property
def aliases(self):
return sorted([ td for td in self._unit.aliases
if td.namespace == self._name])
@property
def structs(self):
return sorted([ td for td in self._unit.structs
if td.namespace == self._name])
@property
def enums(self):
return sorted([ td for td in self._unit.enums
if td.namespace == self._name])
### Eolian Classes ##########################################################
class Object(EolianBaseObject):
def __new__(cls, c_obj_pointer):
if cls is Object:
c_type = lib.eolian_object_type_get(c_obj_pointer)
cls = _eolian_type_class_mapping[c_type]
return super().__new__(cls)
def __repr__(self):
return "<eolian.Object '{0.name}', {0.type!s}>".format(self)
@cached_property
def unit(self):
c_unit = lib.eolian_object_unit_get(self)
return Eolian_Unit(c_unit) if c_unit else None
@cached_property
def name(self):
return _str_to_py(lib.eolian_object_name_get(self))
@cached_property
def short_name(self):
return _str_to_py(lib.eolian_object_short_name_get(self))
@property
def namespaces(self):
return Iterator(_str_to_py, lib.eolian_object_namespaces_get(self))
@cached_property
def namespace(self):
return '.'.join(self.namespaces)
@cached_property
def file(self):
return _str_to_py(lib.eolian_object_file_get(self))
@cached_property
def line(self):
return int(lib.eolian_object_line_get(self))
@cached_property
def column(self):
return int(lib.eolian_object_column_get(self))
@cached_property
def is_beta(self):
return bool(lib.eolian_object_is_beta(self))
class Class(Object):
def __repr__(self):
return "<eolian.Class '{0.name}', {0.type!s}>".format(self)
@cached_property
def c_macro(self):
s = lib.eolian_class_c_macro_get(self)
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)
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))
@cached_property
def data_type(self):
return _str_to_py(lib.eolian_class_data_type_get(self))
@cached_property
def c_data_type(self):
s = lib.eolian_class_c_data_type_get(self)
ret = _str_to_py(s)
lib.eina_stringshare_del(c_void_p(s))
return ret
@cached_property
def eo_prefix(self):
return _str_to_py(lib.eolian_class_eo_prefix_get(self))
@cached_property
def event_prefix(self):
return _str_to_py(lib.eolian_class_event_prefix_get(self))
@cached_property
def documentation(self):
c_doc = lib.eolian_class_documentation_get(self)
return Documentation(c_doc) if c_doc else None
@property
def constructors(self):
return Iterator(Constructor, lib.eolian_class_constructors_get(self))
@property
def events(self):
return Iterator(Event, lib.eolian_class_events_get(self))
def event_by_name_get(self, event_name):
c_event = lib.eolian_class_event_by_name_get(self,
_str_to_bytes(event_name))
return Event(c_event) if c_event else None
@cached_property
def parent(self):
c_class = lib.eolian_class_parent_get(self)
return Class(c_class) if c_class else None
@property
def extensions(self):
return Iterator(Class, lib.eolian_class_extensions_get(self))
@cached_property
def inherits_full(self):
L = []
def do_class_recursive(cls):
if cls.parent:
L.append(cls.parent)
for other in cls.extensions:
if other not in L:
L.append(other)
do_class_recursive(other)
do_class_recursive(self)
return L
@cached_property
def hierarchy(self):
L = []
base = self.parent
while base:
L.append(base)
base = base.parent
return L
@cached_property
def ctor_enable(self):
return bool(lib.eolian_class_ctor_enable_get(self))
@cached_property
def dtor_enable(self):
return bool(lib.eolian_class_dtor_enable_get(self))
def function_by_name_get(self, func_name,
ftype=Eolian_Function_Type.UNRESOLVED):
f = lib.eolian_class_function_by_name_get(self,
_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, 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))
@property
def parts(self):
return Iterator(Part, lib.eolian_class_parts_get(self))
class Part(Object):
def __repr__(self):
return "<eolian.Part '{0.name}'>".format(self)
@cached_property
def class_(self):
return Class(lib.eolian_part_class_get(self))
@cached_property
def documentation(self):
c_doc = lib.eolian_part_documentation_get(self)
return Documentation(c_doc) if c_doc else None
class Constructor(Object):
def __repr__(self):
return "<eolian.Constructor '{0.name}', optional={0.is_optional}>".format(self)
@cached_property
def function(self):
return Function(lib.eolian_constructor_function_get(self))
@cached_property
def is_optional(self):
return bool(lib.eolian_constructor_is_optional(self))
@cached_property
def class_(self):
return Class(lib.eolian_constructor_class_get(self))
class Event(Object):
def __repr__(self):
return "<eolian.Event '{0.name}', c_name='{0.c_name}'>".format(self)
@cached_property
def c_macro(self):
s = lib.eolian_event_c_macro_get(self)
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)
return Type(c_type) if c_type else None
@cached_property
def documentation(self):
c_doc = lib.eolian_event_documentation_get(self)
return Documentation(c_doc) if c_doc else None
@cached_property
def scope(self):
return Eolian_Object_Scope(lib.eolian_event_scope_get(self))
@cached_property
def is_hot(self):
return bool(lib.eolian_event_is_hot(self))
@cached_property
def is_restart(self):
return bool(lib.eolian_event_is_restart(self))
class Function(Object):
def __repr__(self):
return "<eolian.Function '{0.name}'>".format(self)
def full_c_name_get(self, ftype):
s = lib.eolian_function_full_c_name_get(self, ftype)
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 type(self):
return Eolian_Function_Type(lib.eolian_function_type_get(self))
def scope_get(self, ftype):
return Eolian_Object_Scope(lib.eolian_function_scope_get(self, 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)
@cached_property
def is_static(self):
return bool(lib.eolian_function_is_static(self))
@cached_property
def object_is_const(self):
return bool(lib.eolian_function_object_is_const(self))
@cached_property
def class_(self):
c_cls = lib.eolian_function_class_get(self)
return Class(c_cls) if c_cls else None
def is_constructor(self, klass):
return bool(lib.eolian_function_is_constructor(self, klass._obj))
# @cached_property
# def is_function_pointer(self):
# return bool(lib.eolian_function_is_function_pointer(self))
@property
def parameters(self):
return Iterator(Function_Parameter,
lib.eolian_function_parameters_get(self))
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, 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, 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, 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, ftype)
return Documentation(c_doc) if c_doc else None
def return_allow_unused(self, ftype):
return bool(lib.eolian_function_return_allow_unused(self, 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, 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, ftype)
return True if scope != Eolian_Object_Scope.UNKNOWN else False
@cached_property
def implement(self):
c_impl = lib.eolian_function_implement_get(self)
return Implement(c_impl) if c_impl else None
class Function_Parameter(Object):
def __repr__(self):
return "<eolian.Function_Parameter '{0.name}', type={0.type}," \
" optional={0.is_optional}>".format(self)
@cached_property
def direction(self):
return Eolian_Parameter_Dir(lib.eolian_parameter_direction_get(self))
@cached_property
def documentation(self):
c_doc = lib.eolian_parameter_documentation_get(self)
return Documentation(c_doc) if c_doc else None
@cached_property
def is_optional(self):
return bool(lib.eolian_parameter_is_optional(self))
@cached_property
def type(self):
c_type = lib.eolian_parameter_type_get(self)
return Type(c_type) if c_type else None
@cached_property
def default_value(self):
c_expr = lib.eolian_parameter_default_value_get(self)
return Expression(c_expr) if c_expr else None
class Implement(Object):
def __repr__(self):
return "<eolian.Implement '{0.name}'>".format(self)
@cached_property
def class_(self):
c_cls = lib.eolian_implement_class_get(self)
return Class(c_cls) if c_cls else None
@cached_property
def function(self):
c_func = lib.eolian_implement_function_get(self, 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, 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, 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, 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, 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))
@cached_property
def is_prop_get(self):
return bool(lib.eolian_implement_is_prop_get(self))
@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(Object):
def __repr__(self):
# return "<eolian.Type '{0.name}', type: {0.type!s}, c_type: '{0.c_type}'>".format(self)
return "<eolian.Type '{0.name}', type={0.type!s}>".format(self)
@cached_property
def type(self):
return Eolian_Type_Type(lib.eolian_type_type_get(self))
@cached_property
def builtin_type(self):
return Eolian_Type_Builtin_Type(lib.eolian_type_builtin_type_get(self))
def c_type_get(self):
s = lib.eolian_type_c_type_get(self)
ret = _str_to_py(s)
lib.eina_stringshare_del(c_void_p(s))
return ret
@cached_property
def typedecl(self):
c_tdecl = lib.eolian_type_typedecl_get(self)
return Typedecl(c_tdecl) if c_tdecl else None
@cached_property
def base_type(self):
c_type = lib.eolian_type_base_type_get(self)
return Type(c_type) if c_type else None
@cached_property
def next_type(self):
c_type = lib.eolian_type_next_type_get(self)
return Type(c_type) if c_type else None
@cached_property
def aliased_base(self):
c_type = lib.eolian_type_aliased_base_get(self)
return Type(c_type) if c_type else None
@cached_property
def class_(self):
c_cls = lib.eolian_type_class_get(self)
return Class(c_cls) if c_cls else None
@cached_property
def is_owned(self):
return bool(lib.eolian_type_is_owned(self))
@cached_property
def is_const(self):
return bool(lib.eolian_type_is_const(self))
@cached_property
def is_ptr(self):
return bool(lib.eolian_type_is_ptr(self))
class Typedecl(Object):
def __repr__(self):
return "<eolian.Typedecl '{0.name}', type={0.type!s}>".format(self)
@cached_property
def type(self):
return Eolian_Typedecl_Type(lib.eolian_typedecl_type_get(self))
@cached_property
def c_type(self):
s = lib.eolian_typedecl_c_type_get(self)
ret = _str_to_py(s)
lib.eina_stringshare_del(c_void_p(s))
return ret
@cached_property
def free_func(self):
return _str_to_py(lib.eolian_typedecl_free_func_get(self))
@cached_property
def is_extern(self):
return bool(lib.eolian_typedecl_is_extern(self))
@property
def enum_fields(self):
return Iterator(Enum_Type_Field,
lib.eolian_typedecl_enum_fields_get(self))
def enum_field_get(self, field):
c_field = lib.eolian_typedecl_enum_field_get(self, _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))
def struct_field_get(self, field):
c_field = lib.eolian_typedecl_struct_field_get(self, _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)
return Type(c_type) if c_type else None
@cached_property
def aliased_base(self):
c_type = lib.eolian_typedecl_aliased_base_get(self)
return Type(c_type) if c_type else None
@cached_property
def documentation(self):
c_doc = lib.eolian_typedecl_documentation_get(self)
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))
@cached_property
def function_pointer(self):
c_func = lib.eolian_typedecl_function_pointer_get(self)
return Function(c_func) if c_func else None
class Enum_Type_Field(Object):
def __repr__(self):
return "<eolian.Enum_Type_Field '{0.name}', c_name='{0.c_name}'>".format(self)
@cached_property
def c_constant(self):
s = lib.eolian_typedecl_enum_field_c_constant_get(self)
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, 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)
return Documentation(c_doc) if c_doc else None
class Struct_Type_Field(Object):
def __repr__(self):
return "<eolian.Struct_Type_Field '{0.name}', type={0.type!s}>".format(self)
@cached_property
def type(self):
c_type = lib.eolian_typedecl_struct_field_type_get(self)
return Type(c_type) if c_type else None
@cached_property
def documentation(self):
c_doc = lib.eolian_typedecl_struct_field_documentation_get(self)
return Documentation(c_doc) if c_doc else None
class Expression(Object):
def __repr__(self):
return "<eolian.Expression type={0.type!s}, serialize='{0.serialize}'>".format(self)
@cached_property
def type(self):
return Eolian_Expression_Type(lib.eolian_expression_type_get(self))
# TODO: EAPI Eolian_Value eolian_expression_value_get(const Eolian_Expression *expr);
@cached_property
def serialize(self):
s = lib.eolian_expression_serialize(self)
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)
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)
return Expression(c_expr) if c_expr else None
@cached_property
def binary_rhs(self):
c_expr = lib.eolian_expression_binary_rhs_get(self)
return Expression(c_expr) if c_expr else None
@cached_property
def unary_operator(self):
c_op = lib.eolian_expression_unary_operator_get(self)
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)
return Expression(c_expr) if c_expr is not None else None
class Variable(Object):
def __repr__(self):
return "<eolian.Variable '{0.name}', type={0.type!s}, file={0.file}>".format(self)
@cached_property
def type(self):
return Eolian_Variable_Type(lib.eolian_variable_type_get(self))
@cached_property
def value(self):
c_expr = lib.eolian_variable_value_get(self)
return Expression(c_expr) if c_expr else None
@cached_property
def base_type(self):
c_type = lib.eolian_variable_base_type_get(self)
return Type(c_type) if c_type else None
@cached_property
def is_extern(self):
return bool(lib.eolian_variable_is_extern(self))
@cached_property
def documentation(self):
c_doc = lib.eolian_variable_documentation_get(self)
return Documentation(c_doc) if c_doc else None
class _Eolian_Doc_Token_Struct(ctypes.Structure):
_fields_ = [("type", c_int),
("text", c_char_p),
("text_end", c_char_p)]
class Documentation(Object): # OK (1 TODO Unit*)
def __repr__(self):
t = self.summary if len(self.summary) < 40 else self.summary[:40] + '...'
return "<eolian.Documentation summary='{}'>".format(t)
# 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))
@cached_property
def description(self):
return _str_to_py(lib.eolian_documentation_description_get(self))
@cached_property
def since(self):
return _str_to_py(lib.eolian_documentation_since_get(self))
@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 "<eolian.Doc_Token ({}), text='{}', len={}>".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))
### internal Object type -> Class mapping ###################################
class _Eolian_Object_Type(IntEnum):
UNKNOWN = 0
CLASS = 1
TYPEDECL = 2
STRUCT_FIELD = 3
ENUM_FIELD = 4
TYPE = 5
VARIABLE = 6
EXPRESSION = 7
FUNCTION = 8
FUNCTION_PARAMETER = 9
EVENT = 10
PART = 11
IMPLEMENT = 12
CONSTRUCTOR = 13
DOCUMENTATION = 14
_eolian_type_class_mapping = {
_Eolian_Object_Type.UNKNOWN: Object,
_Eolian_Object_Type.CLASS: Class,
_Eolian_Object_Type.TYPEDECL: Typedecl,
_Eolian_Object_Type.STRUCT_FIELD: Struct_Type_Field,
_Eolian_Object_Type.ENUM_FIELD: Enum_Type_Field,
_Eolian_Object_Type.TYPE: Type,
_Eolian_Object_Type.VARIABLE: Variable,
_Eolian_Object_Type.EXPRESSION: Expression,
_Eolian_Object_Type.FUNCTION: Function,
_Eolian_Object_Type.FUNCTION_PARAMETER: Function_Parameter,
_Eolian_Object_Type.EVENT: Event,
_Eolian_Object_Type.PART: Part,
_Eolian_Object_Type.IMPLEMENT: Implement,
_Eolian_Object_Type.CONSTRUCTOR: Constructor,
_Eolian_Object_Type.DOCUMENTATION: Documentation,
}
### 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()