Starts implementing RPNIterExpr python object
This commit is contained in:
parent
5d0f8519fd
commit
d535b5c64c
4 changed files with 462 additions and 8 deletions
325
python_if.c
325
python_if.c
|
|
@ -1,6 +1,36 @@
|
|||
#include "python_if.h"
|
||||
|
||||
static PyStructSequence_Field params_fields[] = {
|
||||
{ .name = "pos_flag",
|
||||
.doc = "Position flag, indicating coordinate system",
|
||||
},
|
||||
{ .name = "res_flag",
|
||||
.doc = "Result flag, indicate how to handle expr results",
|
||||
},
|
||||
{ .name = "size_lim",
|
||||
.doc = "Size limit, an array indicating the space size (depends \
|
||||
on pos_flag",
|
||||
},
|
||||
{
|
||||
.name = "const_values",
|
||||
.doc = "Constant values to use when setting value in space \
|
||||
(expr gives the position, and we set this value",
|
||||
},
|
||||
{ NULL, NULL },
|
||||
};
|
||||
PyStructSequence_Desc rpnif_params_desc = {
|
||||
.name = "RPNIterParams",
|
||||
.doc = "Named tuple for RPNIter parameters",
|
||||
.n_in_sequence = (sizeof(params_fields) / sizeof(*params_fields))-1,
|
||||
.fields = params_fields,
|
||||
};
|
||||
|
||||
PyTypeObject rpnif_params_SeqDesc;
|
||||
|
||||
|
||||
PyMethodDef RPNIterExpr_methods[] = {
|
||||
{"get_params", (PyCFunction)rpnif_get_params, METH_NOARGS,
|
||||
"Get a named tuple with parameters"},
|
||||
{"__getstate__", (PyCFunction)rpnif_getstate, METH_NOARGS,
|
||||
"Pickling method. Return a bytes repr of tokenized expression \
|
||||
and the stack state."},
|
||||
|
|
@ -17,6 +47,12 @@ PyGetSetDef RPNIterExpr_getset[] = {
|
|||
{NULL}
|
||||
};
|
||||
|
||||
// Allows manipulation from numpy
|
||||
static PyBufferProcs RPNIterExpr_as_buffer = {
|
||||
(getbufferproc)rpnif_getbuffer,
|
||||
(releasebufferproc)rpnif_releasebuffer,
|
||||
};
|
||||
|
||||
PyTypeObject RPNIterExprType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"pyrpn.RPNIterExpr", /* tp_name */
|
||||
|
|
@ -36,7 +72,7 @@ PyTypeObject RPNIterExprType = {
|
|||
rpnif_str, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
&RPNIterExpr_as_buffer, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT |
|
||||
Py_TPFLAGS_BASETYPE, /* tp_flags */
|
||||
"RPN expression evaluator", /* tp_doc */
|
||||
|
|
@ -78,21 +114,22 @@ PyObject* rpnif_new(PyTypeObject *subtype, PyObject *args, PyObject* kwds)
|
|||
|
||||
int rpnif_init(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
/**@todo TODO write the function */
|
||||
PyRPNIterExpr_t *expr_self;
|
||||
char *names[] = {"pos_flag", "res_flag", "lim", "value", "stack_size", NULL};
|
||||
char *names[] = {"pos_flag", "res_flag", "size_lim", "const_values", "stack_size", NULL};
|
||||
unsigned short pos_flag, res_flag, stack_size;
|
||||
PyObject *lim, *value;
|
||||
PyObject *lim_obj, *const_values_obj;
|
||||
int ndim;
|
||||
|
||||
char err_str[256];
|
||||
|
||||
expr_self = (PyRPNIterExpr_t*)self;
|
||||
|
||||
stack_size = 16;
|
||||
value = lim = NULL;
|
||||
const_values_obj = lim_obj = NULL;
|
||||
expr_self->rif = NULL;
|
||||
|
||||
if(!PyArg_ParseTupleAndKeywords(args, kwds, "HHO|OH:RPNIterExpr.__init__", names,
|
||||
&pos_flag, &res_flag, lim, value, &stack_size))
|
||||
&pos_flag, &res_flag, &lim_obj, &const_values_obj, &stack_size))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -106,13 +143,213 @@ int rpnif_init(PyObject *self, PyObject *args, PyObject *kwds)
|
|||
PyErr_SetString(PyExc_ValueError, err_str);
|
||||
return -1;
|
||||
}
|
||||
//Check & convert lim
|
||||
|
||||
//Check & convert value
|
||||
// Checks flags & fetch expected sizes for size_lim & const_values
|
||||
short expt_sizes[2];
|
||||
if(rpn_if_sizes_from_flag(pos_flag, res_flag, expt_sizes) < 0)
|
||||
{
|
||||
if(expt_sizes[0] < 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Invalid position flag given");
|
||||
}
|
||||
else
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Invalid result flag given");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Check & convert lim
|
||||
PyObject *tmp;
|
||||
tmp = lim_obj;
|
||||
if(!PyTuple_Check(tmp))
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Invalid type for size_lim argument");
|
||||
return -1;
|
||||
}
|
||||
Py_ssize_t lim_obj_sz = PyTuple_Size(tmp);
|
||||
ndim = lim_obj_sz;
|
||||
if(PyErr_Occurred())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if(lim_obj_sz < 1)
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Size limits cannot be empty");
|
||||
}
|
||||
|
||||
if(pos_flag == RPN_IF_POSITION_XDIM)
|
||||
{
|
||||
PyObject *item = PyTuple_GET_ITEM(tmp, 0);
|
||||
Py_ssize_t tmp = PyLong_AsSsize_t(item);
|
||||
if(PyErr_Occurred())
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Unable to convert size_lim[0] to int");
|
||||
return -1;
|
||||
}
|
||||
if(lim_obj_sz != tmp + 1)
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Xdim indicate %d size_lim but len(size_lim)=%d",
|
||||
tmp+1, lim_obj_sz);
|
||||
return -1;
|
||||
}
|
||||
expt_sizes[0] = ndim = tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(lim_obj_sz != expt_sizes[0])
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Expected %d size_lim but len(size_lim)=%d",
|
||||
expt_sizes[0], lim_obj_sz);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
size_t sz_limits[lim_obj_sz];
|
||||
for(Py_ssize_t i = 0; i<lim_obj_sz; i++)
|
||||
{
|
||||
PyObject *item = PyTuple_GET_ITEM(tmp, i);
|
||||
sz_limits[i] = PyLong_AsSize_t(item);
|
||||
if(PyErr_Occurred())
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Unable to convert size_lim[%d] to unsigned int",
|
||||
i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
expr_self->ndim = ndim;
|
||||
|
||||
//Check & convert const values
|
||||
Py_ssize_t values_obj_sz = 0;
|
||||
tmp = NULL;
|
||||
if(expt_sizes[1] > 0)
|
||||
{
|
||||
tmp = const_values_obj;
|
||||
if(!PyTuple_Check(tmp))
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Invalid type for const_values argument");
|
||||
return -1;
|
||||
}
|
||||
values_obj_sz = PyTuple_Size(tmp);
|
||||
if(values_obj_sz != expt_sizes[1])
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Expected %d const_values but len(const_values)=%d",
|
||||
expt_sizes[1], values_obj_sz);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
rpn_value_t const_values[values_obj_sz];
|
||||
for(Py_ssize_t i = 0; i<values_obj_sz; i++)
|
||||
{
|
||||
PyObject *item = PyTuple_GET_ITEM(tmp, i);
|
||||
const_values[i] = PyLong_AsRpnValue_t(item);
|
||||
if(PyErr_Occurred())
|
||||
{
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Unable to convert size_lim[%d] to unsigned int",
|
||||
i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Creating rif params
|
||||
rpn_if_param_t *rif_params;
|
||||
|
||||
if(!(rif_params = rpn_if_default_params(pos_flag, res_flag,
|
||||
sz_limits, const_values, stack_size)))
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Unable to create parameters \
|
||||
with given arguments");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Creating rif with a new memory map
|
||||
expr_self->rif = rpn_if_new(rif_params, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**@brief Returns the parameters in a named tuple */
|
||||
PyObject *rpnif_get_params(PyObject *self)
|
||||
{
|
||||
PyObject *res, *val;
|
||||
rpn_if_default_data_t *params;
|
||||
|
||||
params = (rpn_if_default_data_t*)(((PyRPNIterExpr_t*)self)->rif->params->data);
|
||||
|
||||
short expt_sizes[2];
|
||||
if(rpn_if_sizes_from_flag(params->pos_flag, params->res_flag, expt_sizes) < 0)
|
||||
{
|
||||
PyErr_SetString(PyExc_RuntimeError, "Invalid internal state");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
res = PyStructSequence_New(&rpnif_params_SeqDesc);
|
||||
if(!res)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
val = PyLong_FromLong(params->pos_flag);
|
||||
PyStructSequence_SET_ITEM(res, 0, val);
|
||||
|
||||
val = PyLong_FromLong(params->res_flag);
|
||||
PyStructSequence_SET_ITEM(res, 1, val);
|
||||
|
||||
if(params->pos_flag == RPN_IF_POSITION_XDIM)
|
||||
{
|
||||
expt_sizes[0] = params->size_lim[0] + 1;
|
||||
}
|
||||
|
||||
PyObject *lim = PyTuple_New(expt_sizes[0]);
|
||||
if(!lim)
|
||||
{
|
||||
Py_DECREF(res);
|
||||
return NULL;
|
||||
}
|
||||
PyStructSequence_SET_ITEM(res, 2, lim);
|
||||
|
||||
for(Py_ssize_t i=0; i<expt_sizes[0]; i++)
|
||||
{
|
||||
val = PyLong_FromSize_t(params->size_lim[i]);
|
||||
PyTuple_SET_ITEM(lim, i, val);
|
||||
}
|
||||
|
||||
if(!params->const_val)
|
||||
{
|
||||
Py_INCREF(Py_None);
|
||||
PyTuple_SET_ITEM(res, 3, Py_None);
|
||||
}
|
||||
else
|
||||
{
|
||||
PyObject *values = PyTuple_New(expt_sizes[1]);
|
||||
if(!values)
|
||||
{
|
||||
Py_DECREF(res);
|
||||
return NULL;
|
||||
}
|
||||
PyStructSequence_SET_ITEM(res, 3, values);
|
||||
for(Py_ssize_t i=0; i<expt_sizes[1]; i++)
|
||||
{
|
||||
val = PyLong_FromRpnValue_t(params->const_val[i]);
|
||||
PyTuple_SET_ITEM(values, i, val);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void rpnif_del(PyObject *self)
|
||||
{
|
||||
PyRPNIterExpr_t *expr_self;
|
||||
|
|
@ -125,26 +362,98 @@ void rpnif_del(PyObject *self)
|
|||
}
|
||||
}
|
||||
|
||||
int rpnif_getbuffer(PyObject *self, Py_buffer *view, int flags)
|
||||
{
|
||||
PyRPNIterExpr_t *expr_self;
|
||||
expr_self = (PyRPNIterExpr_t*)self;
|
||||
|
||||
rpn_if_default_data_t *data = (rpn_if_default_data_t*)expr_self->rif->params->data;
|
||||
|
||||
view->buf = expr_self->rif->mem;
|
||||
view->obj = self;
|
||||
view->len = expr_self->rif->params->mem_sz * expr_self->rif->params->value_sz;
|
||||
view->readonly = 0;
|
||||
//view->itemsize = expr_self->rif->params->value_sz;
|
||||
view->itemsize = sizeof(rpn_value_t);
|
||||
if(flags & PyBUF_FORMAT)
|
||||
{
|
||||
view->format = "L";
|
||||
}
|
||||
else
|
||||
{
|
||||
view->format = NULL;
|
||||
}
|
||||
view->ndim = 1;
|
||||
view->shape = NULL;
|
||||
if(flags & PyBUF_ND)
|
||||
{
|
||||
view->ndim = expr_self->ndim;
|
||||
|
||||
// !! Error if value_sz < sizeof(rpn_value_t) !!
|
||||
short nval = view->itemsize / sizeof(rpn_value_t);
|
||||
if(nval > 1)
|
||||
{
|
||||
view->ndim++;
|
||||
}
|
||||
view->shape = malloc(sizeof(Py_ssize_t) * view->ndim); /**@todo check error*/
|
||||
for(size_t i=0; i<expr_self->ndim; i++)
|
||||
{
|
||||
int idx = i;
|
||||
if(data->pos_flag == RPN_IF_POSITION_XDIM)
|
||||
{
|
||||
idx++;
|
||||
}
|
||||
view->shape[i] = data->size_lim[idx];
|
||||
}
|
||||
if(nval>1)
|
||||
{
|
||||
view->shape[expr_self->ndim] = nval;
|
||||
}
|
||||
}
|
||||
view->strides = NULL;
|
||||
view->suboffsets = NULL;
|
||||
|
||||
Py_INCREF(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rpnif_releasebuffer(PyObject *self, Py_buffer *view)
|
||||
{
|
||||
free(view->shape);
|
||||
}
|
||||
|
||||
PyObject* rpnif_str(PyObject *self)
|
||||
{
|
||||
PyErr_SetString(PyExc_NotImplementedError,
|
||||
"Not implemented");
|
||||
return NULL;
|
||||
/**@todo TODO write the function */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject* rpnif_repr(PyObject *self)
|
||||
{
|
||||
PyErr_SetString(PyExc_NotImplementedError,
|
||||
"Not implemented");
|
||||
return NULL;
|
||||
/**@todo TODO write the function */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject* rpnif_getstate(PyObject *self, PyObject *noargs)
|
||||
{
|
||||
PyErr_SetString(PyExc_NotImplementedError,
|
||||
"Not implemented");
|
||||
return NULL;
|
||||
/**@todo TODO write the function */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject* rpnif_setstate(PyObject *self, PyObject *state_bytes)
|
||||
{
|
||||
PyErr_SetString(PyExc_NotImplementedError,
|
||||
"Not implemented");
|
||||
return NULL;
|
||||
/**@todo TODO write the function */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
|
|
|||
13
python_if.h
13
python_if.h
|
|
@ -49,6 +49,10 @@
|
|||
* This file is the header of the RPNIterExpr Python class
|
||||
*/
|
||||
|
||||
/**@brief A single instance of this object is used for parameters representation */
|
||||
extern PyTypeObject rpnif_params_SeqDesc;
|
||||
extern PyStructSequence_Desc rpnif_params_desc;
|
||||
|
||||
/**@brief RPNIterExpr Python class methods list
|
||||
* @ingroup python_if */
|
||||
extern PyMethodDef RPNIterExpr_methods[];
|
||||
|
|
@ -65,6 +69,9 @@ typedef struct
|
|||
{
|
||||
PyObject_VAR_HEAD;
|
||||
|
||||
/** @brief Number of dimention in map */
|
||||
size_t ndim;
|
||||
|
||||
/**@brief Pointer on @ref rpn_if_s */
|
||||
rpn_if_t *rif;
|
||||
|
||||
|
|
@ -93,6 +100,12 @@ int rpnif_init(PyObject *self, PyObject *args, PyObject *kwds);
|
|||
*/
|
||||
void rpnif_del(PyObject *self);
|
||||
|
||||
|
||||
int rpnif_getbuffer(PyObject *self, Py_buffer *view, int flags);
|
||||
void rpnif_releasebuffer(PyObject *self, Py_buffer *view);
|
||||
|
||||
PyObject *rpnif_get_params(PyObject *self);
|
||||
|
||||
/**@brief RPNIterExpr __getstate__ method for pickling
|
||||
* @param cls RPNIterExpr type object
|
||||
* @param noargs Not an argument...
|
||||
|
|
|
|||
|
|
@ -100,6 +100,24 @@ PyInit_pyrpn(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
// Named tuple for RPNIterExpr's params
|
||||
PyStructSequence_InitType(&rpnif_params_SeqDesc,&rpnif_params_desc);
|
||||
Py_INCREF(&rpnif_params_SeqDesc);
|
||||
/*
|
||||
PyObject_SetAttrString((PyObject*)rpnif_params_SeqDesc, "__module__",
|
||||
mod);
|
||||
*/
|
||||
if(PyModule_AddObject(mod, "RPNIterExprParams",
|
||||
(PyObject*)&rpnif_params_SeqDesc) < 0)
|
||||
{
|
||||
Py_DECREF(&RPNExprType);
|
||||
Py_DECREF(&RPNIterExprType);
|
||||
Py_DECREF(&rpnif_params_SeqDesc);
|
||||
Py_DECREF(mod);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
|
|
|
|||
114
tests/tests_rpniter.py
Executable file
114
tests/tests_rpniter.py
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
#!/usr/bin/python3
|
||||
import unittest
|
||||
import warnings
|
||||
|
||||
try:
|
||||
import numpy as np
|
||||
except (ImportError, NameError) as e:
|
||||
np = None
|
||||
warnings.warn("Numpy not found, some tests skipped", warnings.ImportWarning)
|
||||
|
||||
try:
|
||||
import pyrpn
|
||||
except (ImportError, NameError) as e:
|
||||
print("Error importing pyrpn. Try to run make.",
|
||||
file=sys.stderr)
|
||||
raise e
|
||||
|
||||
def skipIfNoNumpy():
|
||||
if np is not None:
|
||||
return lambda func: func
|
||||
return unittest.skip("Numpy not found")
|
||||
|
||||
|
||||
|
||||
class TestRPNIterInit(unittest.TestCase):
|
||||
|
||||
def test_init(self):
|
||||
""" Test instanciation """
|
||||
rif = pyrpn.RPNIterExpr(pyrpn.const.POS_XY,
|
||||
pyrpn.const.RESULT_COUNT,
|
||||
(640,480))
|
||||
|
||||
def test_params_simple(self):
|
||||
""" Test parameters fetch from instanciation """
|
||||
args = [(pyrpn.const.POS_XY,
|
||||
pyrpn.const.RESULT_COUNT,
|
||||
(640,480)),
|
||||
(pyrpn.const.POS_LINEAR,
|
||||
pyrpn.const.RESULT_BOOL,
|
||||
(1024,))
|
||||
]
|
||||
for arg in args:
|
||||
with self.subTest(args=arg):
|
||||
rif = pyrpn.RPNIterExpr(*arg)
|
||||
params = rif.get_params()
|
||||
self.assertEqual(params.pos_flag, arg[0])
|
||||
self.assertEqual(params.res_flag, arg[1])
|
||||
self.assertEqual(params.size_lim, arg[2])
|
||||
self.assertIsNone(params.const_values)
|
||||
|
||||
def test_params_const(self):
|
||||
""" Test parameters from instanciation when const values used as result """
|
||||
args = [(pyrpn.const.POS_XY,
|
||||
pyrpn.const.RESULT_CONST,
|
||||
(640,480),
|
||||
(42,)),
|
||||
(pyrpn.const.POS_LINEAR,
|
||||
pyrpn.const.RESULT_CONST_RGBA,
|
||||
(1024,), (13,37,13,12))
|
||||
]
|
||||
for arg in args:
|
||||
with self.subTest(args=arg):
|
||||
rif = pyrpn.RPNIterExpr(*arg)
|
||||
params = rif.get_params()
|
||||
self.assertEqual(params.pos_flag, arg[0])
|
||||
self.assertEqual(params.res_flag, arg[1])
|
||||
self.assertEqual(params.size_lim, arg[2])
|
||||
self.assertEqual(params.const_values, arg[3])
|
||||
|
||||
def test_params_xdim(self):
|
||||
""" Test parameters from instanciation when using xdim positionning """
|
||||
args = [(pyrpn.const.POS_XDIM,
|
||||
pyrpn.const.RESULT_BOOL,
|
||||
(2,640,480)),
|
||||
(pyrpn.const.POS_XDIM,
|
||||
pyrpn.const.RESULT_BOOL,
|
||||
(5,13,37,13,12,42)),
|
||||
]
|
||||
for arg in args:
|
||||
with self.subTest(args=arg):
|
||||
rif = pyrpn.RPNIterExpr(*arg)
|
||||
params = rif.get_params()
|
||||
self.assertEqual(params.pos_flag, arg[0])
|
||||
self.assertEqual(params.res_flag, arg[1])
|
||||
self.assertEqual(params.size_lim, arg[2])
|
||||
self.assertIsNone(params.const_values)
|
||||
|
||||
@skipIfNoNumpy()
|
||||
def test_buffer(self):
|
||||
""" Test the buffer interface using numpy """
|
||||
args = [((pyrpn.const.POS_XY,
|
||||
pyrpn.const.RESULT_COUNT,
|
||||
(640,480)), 640*480),
|
||||
((pyrpn.const.POS_LINEAR,
|
||||
pyrpn.const.RESULT_BOOL,
|
||||
(1024,)), 1024),
|
||||
((pyrpn.const.POS_XDIM,
|
||||
pyrpn.const.RESULT_BOOL,
|
||||
(2,640,480)), 640*480),
|
||||
((pyrpn.const.POS_XDIM,
|
||||
pyrpn.const.RESULT_BOOL,
|
||||
(5,13,37,13,12,42)), 13*37*13*12*42),
|
||||
]
|
||||
|
||||
for arg, expt_sz in args:
|
||||
with self.subTest(args=arg):
|
||||
rif = pyrpn.RPNIterExpr(*arg)
|
||||
arr = np.frombuffer(rif, dtype=np.uint64)
|
||||
self.assertEqual(len(arr), expt_sz)
|
||||
arr += 0x11111111 # check writing to all bytes
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue