Skip to content

Commit

Permalink
WIP: User defined expression.
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerilk committed Aug 28, 2024
1 parent 04e867e commit 09dfb94
Show file tree
Hide file tree
Showing 27 changed files with 1,327 additions and 417 deletions.
99 changes: 40 additions & 59 deletions bindings/python/cconfigspace/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ctypes as ct
import json
import pickle
import sys
import traceback
from . import libcconfigspace
Expand Down Expand Up @@ -106,8 +106,8 @@ def __repr__(self):

class CEnumeration(ct.c_int, metaclass=CEnumerationType):
_members_ = {}
def __init__(self, value):
ct.c_int.__init__(self, value)
def __init__(*args):
ct.c_int.__init__(*args)

@property
def name(self):
Expand Down Expand Up @@ -421,8 +421,8 @@ def _ccs_get_function(method, argtypes = [], restype = Result):
ccs_object_get_user_data = _ccs_get_function("ccs_object_get_user_data", [ccs_object, ct.POINTER(ct.c_void_p)])
ccs_object_serialize_callback_type = ct.CFUNCTYPE(Result, ccs_object, ct.c_size_t, ct.c_void_p, ct.POINTER(ct.c_size_t), ct.c_void_p)
ccs_object_set_serialize_callback = _ccs_get_function("ccs_object_set_serialize_callback", [ccs_object, ccs_object_serialize_callback_type, ct.c_void_p])
ccs_object_deserialize_data_callback_type = ct.CFUNCTYPE(Result, ccs_object, ct.c_size_t, ct.c_void_p, ct.c_void_p)
ccs_object_deserialize_vector_callback_type = ct.CFUNCTYPE(Result, ct.c_int, ct.c_char_p, ct.c_void_p, ct.POINTER(ct.c_void_p), ct.POINTER(ct.py_object))
ccs_object_deserialize_data_callback_type = ct.CFUNCTYPE(Result, ccs_object, ct.c_size_t, ct.c_void_p, ct.POINTER(ct.py_object))
ccs_object_deserialize_vector_callback_type = ct.CFUNCTYPE(Result, ObjectType, ct.c_char_p, ct.c_void_p, ct.POINTER(ct.c_void_p), ct.POINTER(ct.py_object))
# Variadic methods
ccs_object_serialize = getattr(libcconfigspace, "ccs_object_serialize")
ccs_object_serialize.argtypes = ccs_object, SerializeFormat, SerializeOperation,
Expand Down Expand Up @@ -520,10 +520,10 @@ def from_handle(cls, h, retain = True):
auto_release = True
return cls._from_handle(h, retain, auto_release)

def set_destroy_callback(self, callback, user_data = None):
_set_destroy_callback(self.handle, callback, user_data = user_data)
def set_destroy_callback(self, callback):
_set_destroy_callback(self.handle, callback)

def serialize(self, format = 'binary', path = None, file_descriptor = None, callback = None, callback_data = None):
def serialize(self, format = 'binary', path = None, file_descriptor = None, callback = None):
if format != 'binary':
raise Error(Result(Result.ERROR_INVALID_VALUE))
if path and file_descriptor:
Expand All @@ -532,7 +532,7 @@ def serialize(self, format = 'binary', path = None, file_descriptor = None, call
if callback:
cb_wrapper = _get_serialize_callback_wrapper(callback)
cb_wrapper_func = ccs_object_serialize_callback_type(cb_wrapper)
options = [SerializeOption.CALLBACK, cb_wrapper_func, ct.py_object(callback_data)] + options
options = [SerializeOption.CALLBACK, cb_wrapper_func, ct.py_object()] + options
elif _default_user_data_serializer:
options = [SerializeOption.CALLBACK, _default_user_data_serializer, ct.py_object()] + options
if path:
Expand All @@ -556,7 +556,7 @@ def serialize(self, format = 'binary', path = None, file_descriptor = None, call
return v

@classmethod
def deserialize(cls, format = 'binary', handle_map = None, vector_callback = None, vector_callback_data = None, path = None, buffer = None, file_descriptor = None, callback = None, callback_data = None):
def deserialize(cls, format = 'binary', handle_map = None, vector_callback = None, path = None, buffer = None, file_descriptor = None, callback = None):
if format != 'binary':
raise Error(Result(Result.ERROR_INVALID_VALUE))
mode_count = 0;
Expand All @@ -575,11 +575,11 @@ def deserialize(cls, format = 'binary', handle_map = None, vector_callback = Non
if vector_callback:
vector_cb_wrapper = _get_deserialize_vector_callback_wrapper(vector_callback)
vector_cb_wrapper_func = ccs_object_deserialize_vector_callback_type(vector_cb_wrapper)
options = [DeserializeOption.VECTOR_CALLBACK, vector_cb_wrapper_func, ct.py_object(vector_callback_data)] + options
options = [DeserializeOption.VECTOR_CALLBACK, vector_cb_wrapper_func, ct.py_object()] + options
if callback:
cb_wrapper = _get_deserialize_data_callback_wrapper(callback)
cb_wrapper_func = ccs_object_deserialize_data_callback_type(cb_wrapper)
options = [DeserializeOption.DATA_CALLBACK, cb_wrapper_func, ct.py_object(callback_data)] + options
options = [DeserializeOption.DATA_CALLBACK, cb_wrapper_func, ct.py_object()] + options
elif _default_user_data_deserializer:
options = [DeserializeOption.DATA_CALLBACK, _default_user_data_deserializer, ct.py_object()] + options
if buffer:
Expand All @@ -602,39 +602,24 @@ def deserialize(cls, format = 'binary', handle_map = None, vector_callback = Non
def _get_serialize_callback_wrapper(callback):
def serialize_callback_wrapper(obj, serialize_data_size, serialize_data, serialize_data_size_ret, cb_data):
try:
p_sd = ct.cast(serialize_data, ct.c_void_p)
p_sdsz = ct.cast(serialize_data_size_ret, ct.POINTER(ct.c_size_t))
cb_data = ct.cast(cb_data, ct.c_void_p)
if cb_data:
cb_data = ct.cast(cb_data, ct.py_object).value
else:
cb_data = None
serialized = callback(Object.from_handle(ccs_object(obj)), cb_data, True if serialize_data_size == 0 else False)
if p_sd and serialize_data_size < ct.sizeof(serialized):
serialized = callback(Object.from_handle(obj).user_data)
state = ct.create_string_buffer(serialized, len(serialized))
if serialize_data and serialize_data_size < ct.sizeof(state):
raise Error(Result(Result.ERROR_INVALID_VALUE))
if p_sd:
ct.memmove(p_sd, ct.byref(serialized), ct.sizeof(serialized))
if p_sdsz:
p_sdsz[0] = ct.sizeof(serialized)
if serialize_data:
ct.memmove(serialize_data, ct.byref(state), ct.sizeof(state))
if serialize_data_size_ret:
serialize_data_size_ret[0] = ct.sizeof(state)
return Result.SUCCESS
except Exception as e:
return Error.set_error(e)
return serialize_callback_wrapper

def _get_deserialize_data_callback_wrapper(callback):
def deserialize_data_callback_wrapper(obj, serialize_data_size, serialize_data, cb_data):
def deserialize_data_callback_wrapper(obj, serialize_data_size, p_serialize_data, cb_data):
try:
p_sd = ct.cast(serialize_data, ct.c_void_p)
cb_data = ct.cast(cb_data, ct.c_void_p)
if cb_data:
cb_data = ct.cast(cb_data, ct.py_object).value
else:
cb_data = None
if p_sd:
serialized = ct.cast(p_sd, ct.POINTER(ct.c_byte * serialize_data_size))
else:
serialized = None
callback(Object.from_handle(ccs_object(obj)), serialized, cb_data)
user_data = callback(ct.string_at(p_serialize_data, serialize_data_size))
Object.from_handle(ccs_object(obj)).user_data = user_data
return Result.SUCCESS
except Exception as e:
return Error.set_error(e)
Expand All @@ -643,9 +628,7 @@ def deserialize_data_callback_wrapper(obj, serialize_data_size, serialize_data,
def _get_deserialize_vector_callback_wrapper(callback):
def deserialize_vector_callback_wrapper(obj_type, name, callback_user_data, vector_ret, data_ret):
try:
cb_data = ct.cast(callback_user_data, ct.py_object).value if callback_user_data else None
o_type = ObjectType(obj_type)
(vector, data) = callback(o_type, name, cb_data)
(vector, data) = callback(obj_type.value, name.decode())
c_vector = ct.py_object(vector)
c_data = ct.py_object(data)
vector_ret[0] = ct.cast(ct.byref(vector), ct.c_void_p)
Expand All @@ -657,22 +640,20 @@ def deserialize_vector_callback_wrapper(obj_type, name, callback_user_data, vect
return Error.set_error(e)
return deserialize_vector_callback_wrapper

def _json_user_data_serializer(obj, data, size):
string = json.dumps(obj.user_data).encode("ascii")
return ct.create_string_buffer(string)
def _pickle_user_data_serializer(user_data):
return pickle.dumps(user_data)

def _json_user_data_deserializer(obj, serialized, data):
serialized = ct.cast(serialized, ct.c_char_p)
obj.user_data = json.loads(serialized.value)
def _pickle_user_data_deserializer(serialized):
return pickle.loads(serialized)

_json_user_data_serializer_wrap = _get_serialize_callback_wrapper(_json_user_data_serializer)
_json_user_data_serializer_func = ccs_object_serialize_callback_type(_json_user_data_serializer_wrap)
_pickle_user_data_serializer_wrap = _get_serialize_callback_wrapper(_pickle_user_data_serializer)
_pickle_user_data_serializer_func = ccs_object_serialize_callback_type(_pickle_user_data_serializer_wrap)

_json_user_data_deserializer_wrap = _get_deserialize_data_callback_wrapper(_json_user_data_deserializer)
_json_user_data_deserializer_func = ccs_object_deserialize_data_callback_type(_json_user_data_deserializer_wrap)
_pickle_user_data_deserializer_wrap = _get_deserialize_data_callback_wrapper(_pickle_user_data_deserializer)
_pickle_user_data_deserializer_func = ccs_object_deserialize_data_callback_type(_pickle_user_data_deserializer_wrap)

_default_user_data_serializer = _json_user_data_serializer_func
_default_user_data_deserializer = _json_user_data_deserializer_func
_default_user_data_serializer = _pickle_user_data_serializer_func
_default_user_data_deserializer = _pickle_user_data_deserializer_func

# If objects don't have a user-defined del operation, then the first time a
# data needs to be registered a destruction callback is attached.
Expand Down Expand Up @@ -712,29 +693,29 @@ def _register_serialize_callback(handle, callback_data):
_data_store[value]['serialize_calback'] = callback_data

def deserialize(format = 'binary', handle_map = None, path = None, buffer = None, file_descriptor = None, vector_callback = None, vector_callback_data = None, callback = None, callback_data = None):
return Object.deserialize(format = format, handle_map = handle_map, path = path, buffer = buffer, file_descriptor = file_descriptor, vector_callback = vector_callback, vector_callback_data = vector_callback_data, callback = callback, callback_data = callback_data)
return Object.deserialize(format = format, handle_map = handle_map, path = path, buffer = buffer, file_descriptor = file_descriptor, vector_callback = vector_callback, callback = callback)

def _set_destroy_callback(handle, callback, user_data = None):
def _set_destroy_callback(handle, callback):
if callback is None:
raise Error(Result(Result.ERROR_INVALID_VALUE))
def cb_wrapper(obj, data):
callback(Object.from_handle(obj), data)
cb_wrapper_func = ccs_object_destroy_callback_type(cb_wrapper)
res = ccs_object_set_destroy_callback(handle, cb_wrapper_func, user_data)
res = ccs_object_set_destroy_callback(handle, cb_wrapper_func, None)
Error.check(res)
_register_callback(handle, [callback, cb_wrapper, cb_wrapper_func, user_data])
_register_callback(handle, [callback, cb_wrapper, cb_wrapper_func])

def _set_serialize_callback(handle, callback, user_data = None):
def _set_serialize_callback(handle, callback):
if callback is None:
res = ccs_object_set_serialize_callback(handle, None, None)
Error.check(res)
_register_serialize_callback(handle, None)
else:
cb_wrapper = _get_serialize_callback_wrapper(callback)
cb_wrapper_func = ccs_object_serialize_callback_type(cb_wrapper)
res = ccs_object_set_serialize_callback(handle, cb_wrapper_func, user_data)
res = ccs_object_set_serialize_callback(handle, cb_wrapper_func, None)
Error.check(res)
_register_serialize_callback(handle, [callback, cb_wrapper, cb_wrapper_func, user_data])
_register_serialize_callback(handle, [callback, cb_wrapper, cb_wrapper_func])

_ccs_id = 0
def _ccs_get_id():
Expand Down
154 changes: 153 additions & 1 deletion bindings/python/cconfigspace/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ class ExpressionType(CEnumeration):
'IN',
'LIST',
'LITERAL',
'VARIABLE' ]
'VARIABLE',
'USER_DEFINED' ]

class AssociativityType(CEnumeration):
_members_ = [
Expand Down Expand Up @@ -476,6 +477,156 @@ def __str__(self):

Expression.List = ExpressionList

ccs_user_defined_expression_del_type = ct.CFUNCTYPE(Result, ccs_expression)
ccs_user_defined_expression_eval_type = ct.CFUNCTYPE(Result, ccs_expression, ct.c_size_t, ct.POINTER(Datum), ct.POINTER(Datum))
ccs_user_defined_expression_serialize_type = ct.CFUNCTYPE(Result, ccs_expression, ct.c_size_t, ct.c_void_p, ct.POINTER(ct.c_size_t))
ccs_user_defined_expression_deserialize_type = ct.CFUNCTYPE(Result, ct.c_size_t, ct.c_void_p, ct.POINTER(ct.py_object))

class UserDefinedExpressionVector(ct.Structure):
_fields_ = [
('delete', ccs_user_defined_expression_del_type),
('evaluate', ccs_user_defined_expression_eval_type),
('serialize', ccs_user_defined_expression_serialize_type),
('deserialize', ccs_user_defined_expression_deserialize_type) ]

ccs_create_user_defined_expression = _ccs_get_function("ccs_create_user_defined_expression", [ct.c_char_p, ct.c_size_t, ct.POINTER(Datum), ct.POINTER(UserDefinedExpressionVector), ct.py_object, ct.POINTER(ccs_expression)])
ccs_user_defined_expression_get_name = _ccs_get_function("ccs_user_defined_expression_get_name", [ccs_expression, ct.POINTER(ct.c_char_p)])
ccs_user_defined_expression_get_expression_data = _ccs_get_function("ccs_user_defined_expression_get_expression_data", [ccs_expression, ct.POINTER(ct.c_void_p)])

class ExpressionUserDefined(Expression):
def __init__(self, handle = None, retain = False, auto_release = True,
name = "", nodes = [], delete = None, evaluate = None, serialize = None, deserialize = None, expression_data = None):
if handle is None:
if evaluate is None:
raise Error(Result(Result.ERROR_INVALID_VALUE))

vec = self.get_vector(delete, evaluate, serialize, deserialize)
c_expression_data = None
if expression_data is not None:
c_expression_data = ct.py_object(expression_data)
handle = ccs_expression()
sz = len(nodes)
v = (Datum*sz)()
ss = []
for i in range(sz):
v[i].set_value(nodes[i], string_store = ss)
res = ccs_create_user_defined_expression(str.encode(name), sz, v, ct.byref(vec), c_expression_data, ct.byref(handle))
Error.check(res)
super().__init__(handle = handle, retain = False)
ct.pythonapi.Py_IncRef(ct.py_object(vec))
if c_expression_data is not None:
ct.pythonapi.Py_IncRef(c_expression_data)
else:
super().__init__(handle = handle, retain = retain, auto_release = auto_release)

@property
def name(self):
if hasattr(self, "_name"):
return self._name
v = ct.c_char_p()
res = ccs_user_defined_expression_get_name(self.handle, ct.byref(v))
Error.check(res)
self._name = v.value.decode()
return self._name

@property
def expression_data(self):
if hasattr(self, "_expression_data"):
return self._expression_data
v = ct.c_void_p()
res = ccs_user_defined_expression_get_expression_data(self.handle, ct.byref(v))
Error.check(res)
if v:
self._expression_data = ct.cast(v, ct.py_object).value
else:
self._expression_data = None
return self._expression_data

def __str__(self):
return "{}({})".format(self.name, ", ".join(map(str, self.nodes)))

@classmethod
def get_vector(self, delete = None, evaluate = None, serialize = None, deserialize = None):
vec = UserDefinedExpressionVector()
setattr(vec, '_string_store', list())
setattr(vec, '_object_store', list())
def delete_wrapper(expr):
try:
o = Object.from_handle(expr)
edata = o.expression_data
if delete is not None:
delete(o)
if edata is not None:
ct.pythonapi.Py_DecRef(ct.py_object(edata))
ct.pythonapi.Py_DecRef(ct.py_object(vec))
return Result.SUCCESS
except Exception as e:
return Error.set_error(e)

def evaluate_wrapper(expr, num_values, p_values, p_value_ret):
try:
if num_values == 0:
value_ret = evaluate(Expression.from_handle(expr))
else:
values = tuple(p_values[i].value for i in range(num_values))
value_ret = evaluate(Expression.from_handle(expr), *values)
p_value_ret[0].set_value(value_ret, string_store = getattr(vec, '_string_store'), object_store = getattr(vec, '_object_store'))
return Result.SUCCESS
except Exception as e:
return Error.set_error(e)

if serialize is not None:
def serialize_wrapper(expr, state_size, p_state, p_state_size):
try:
serialized = serialize(Expression.from_handle(expr))
state = ct.create_string_buffer(serialized, len(serialized))
if p_state and state_size < ct.sizeof(state):
raise Error(Result(Result.ERROR_INVALID_VALUE))
if p_state:
ct.memmove(p_state, ct.byref(state), ct.sizeof(state))
if p_state_size:
p_state_size[0] = ct.sizeof(state)
return Result.SUCCESS
except Exception as e:
return Error.set_error(e)
else:
serialize_wrapper = 0

if deserialize is not None:
def deserialize_wrapper(state_size, p_state, p_expression_data):
try:
expression_data = deserialize(ct.string_at(p_state, state_size))
c_expression_data = ct.py_object(expression_data)
p_expression_data[0] = c_expression_data
ct.pythonapi.Py_IncRef(c_expression_data)
return Result.SUCCESS
except Exception as e:
return Error.set_error(e)
else:
deserialize_wrapper = 0

delete_wrapper_func = ccs_user_defined_expression_del_type(delete_wrapper)
evaluate_wrapper_func = ccs_user_defined_expression_eval_type(evaluate_wrapper)
serialize_wrapper_func = ccs_user_defined_expression_serialize_type(serialize_wrapper)
deserialize_wrapper_func = ccs_user_defined_expression_deserialize_type(deserialize_wrapper)
vec.delete = delete_wrapper_func
vec.evaluate = evaluate_wrapper_func
vec.serialize = serialize_wrapper_func
vec.deserialize = deserialize_wrapper_func

setattr(vec, '_wrappers', (
delete_wrapper,
evaluate_wrapper,
serialize_wrapper,
deserialize_wrapper,
delete_wrapper_func,
evaluate_wrapper_func,
serialize_wrapper_func,
deserialize_wrapper_func))
return vec

Expression.UserDefined = ExpressionUserDefined

setattr(Expression, 'EXPRESSION_MAP', {
ExpressionType.OR: ExpressionOr,
ExpressionType.AND: ExpressionAnd,
Expand All @@ -497,4 +648,5 @@ def __str__(self):
ExpressionType.LIST: ExpressionList,
ExpressionType.LITERAL: ExpressionLiteral,
ExpressionType.VARIABLE: ExpressionVariable,
ExpressionType.USER_DEFINED: ExpressionUserDefined,
})
Loading

0 comments on commit 09dfb94

Please sign in to comment.