RNA: Commit of the API patch by vekoon. This adds Functions to RNA,

which can be defined to call C functions with defined parameters.

* Parameters are RNA properties, with the same types.
* Parameters are stored in a ParameterList, which is like a small
  stack with the values. This is then used to call the C function.
* Includes Python integration.

* Only one test function is part of this commit, ID.rename.
* Integration with the editors/ module is not included in this
  commit, there's some issues to be worked out for that still.
This commit is contained in:
2009-04-07 00:49:39 +00:00
parent 88ab62c031
commit 767db1b716
16 changed files with 2026 additions and 308 deletions

View File

@@ -24,6 +24,7 @@
#include "bpy_rna.h"
#include "bpy_compat.h"
#include "bpy_util.h"
//#include "blendef.h"
#include "BLI_dynstr.h"
#include "BLI_listbase.h"
@@ -45,6 +46,11 @@ static int pyrna_prop_compare( BPy_PropertyRNA * a, BPy_PropertyRNA * b )
return (a->prop==b->prop && a->ptr.data==b->ptr.data ) ? 0 : -1;
}
static int pyrna_func_compare( BPy_FunctionRNA * a, BPy_FunctionRNA * b )
{
return (a->func==b->func && a->ptr.data==b->ptr.data ) ? 0 : -1;
}
/* For some reason python3 needs these :/ */
static PyObject *pyrna_struct_richcmp(BPy_StructRNA * a, BPy_StructRNA * b, int op)
@@ -67,6 +73,16 @@ static PyObject *pyrna_prop_richcmp(BPy_PropertyRNA * a, BPy_PropertyRNA * b, in
return Py_CmpToRich(op, cmp_result);
}
static PyObject *pyrna_func_richcmp(BPy_FunctionRNA * a, BPy_FunctionRNA * b, int op)
{
int cmp_result= -1; /* assume false */
if (BPy_FunctionRNA_Check(a) && BPy_FunctionRNA_Check(b)) {
cmp_result= pyrna_func_compare(a, b);
}
return Py_CmpToRich(op, cmp_result);
}
/*----------------------repr--------------------------------------------*/
static PyObject *pyrna_struct_repr( BPy_StructRNA * self )
{
@@ -105,6 +121,11 @@ static PyObject *pyrna_prop_repr( BPy_PropertyRNA * self )
return PyUnicode_FromFormat( "[BPy_PropertyRNA \"%s\" -> \"%s\"]", RNA_struct_identifier(&self->ptr), RNA_property_identifier(&self->ptr, self->prop));
}
static PyObject *pyrna_func_repr( BPy_FunctionRNA * self )
{
return PyUnicode_FromFormat( "[BPy_FunctionRNA \"%s\"]", RNA_function_identifier(&self->ptr, self->func));
}
static long pyrna_struct_hash( BPy_StructRNA * self )
{
return (long)self->ptr.data;
@@ -124,13 +145,22 @@ static void pyrna_struct_dealloc( BPy_StructRNA * self )
return;
}
/* use our own dealloc so we can free a property if we use one */
static void pyrna_function_dealloc( BPy_FunctionRNA * self )
{
if (Py_TYPE(self)->tp_free)
Py_TYPE(self)->tp_free(self);
return;
}
static char *pyrna_enum_as_string(PointerRNA *ptr, PropertyRNA *prop)
{
const EnumPropertyItem *item;
int totitem, i;
int totitem;
RNA_property_enum_items(ptr, prop, &item, &totitem);
return BPy_enum_as_string(item);
return (char*)BPy_enum_as_string((EnumPropertyItem*)item);
}
PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
@@ -201,6 +231,10 @@ PyObject * pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop)
return ret;
}
PyObject *pyrna_func_to_py(PointerRNA *ptr, FunctionRNA *func)
{
return pyrna_func_CreatePyObject(ptr, func);
}
int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
{
@@ -401,7 +435,6 @@ int pyrna_py_to_prop(PointerRNA *ptr, PropertyRNA *prop, PyObject *value)
return 0;
}
static PyObject * pyrna_prop_to_py_index(PointerRNA *ptr, PropertyRNA *prop, int index)
{
PyObject *ret;
@@ -661,6 +694,7 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
char *name = _PyUnicode_AsString(pyname);
PyObject *ret;
PropertyRNA *prop;
FunctionRNA *func;
/* Include this incase this instance is a subtype of a python class
* In these instances we may want to return a function or variable provided by the subtype
@@ -671,13 +705,14 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
if (ret) return ret;
else PyErr_Clear();
/* done with subtypes */
prop = RNA_struct_find_property(&self->ptr, name);
if (prop) {
ret = pyrna_prop_to_py(&self->ptr, prop);
if ((prop = RNA_struct_find_property(&self->ptr, name))) {
ret = pyrna_prop_to_py(&self->ptr, prop);
}
else if ((func = RNA_struct_find_function(&self->ptr, name))) {
ret = pyrna_func_to_py(&self->ptr, func);
}
else if (/*self->ptr.type == &RNA_Context*/0) {
else if (self->ptr.type == &RNA_Context) {
PointerRNA newptr;
ListBase newlb;
@@ -918,6 +953,393 @@ static PyObject * pyrna_prop_new(PyTypeObject *type, PyObject *args, PyObject *k
}
}
/* only needed for subtyping, so a new class gets a valid BPy_FunctionRNA
* todo - also accept useful args */
static PyObject * pyrna_func_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
BPy_FunctionRNA *base = NULL;
if (!PyArg_ParseTuple(args, "O!:Base BPy_FunctionRNA", &pyrna_func_Type, &base))
return NULL;
if (type == &pyrna_func_Type) {
return pyrna_func_CreatePyObject(&base->ptr, base->func);
} else {
BPy_FunctionRNA *ret = (BPy_FunctionRNA *) type->tp_alloc(type, 0);
ret->ptr = base->ptr;
ret->func = base->func;
return (PyObject *)ret;
}
}
int pyrna_py_to_param(PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *value)
{
/* XXX hard limits should be checked here */
int type = RNA_property_type(ptr, prop);
int len = RNA_property_array_length(ptr, prop);
if (len > 0) {
PyObject *item;
int i;
if (!PySequence_Check(value)) {
PyErr_SetString(PyExc_TypeError, "expected a python sequence type assigned to an RNA array.");
return -1;
}
if ((int)PySequence_Length(value) != len) {
PyErr_SetString(PyExc_AttributeError, "python sequence length did not match the RNA array.");
return -1;
}
/* for arrays we have a limited number of types */
switch (type) {
case PROP_BOOLEAN:
{
int *param_arr = (int*)data;
/* collect the variables before assigning, incase one of them is incorrect */
for (i=0; i<len; i++) {
item = PySequence_GetItem(value, i);
param_arr[i] = PyObject_IsTrue( item );
Py_DECREF(item);
if (param_arr[i] < 0) {
PyErr_SetString(PyExc_AttributeError, "one or more of the values in the sequence is not a boolean");
return -1;
}
}
break;
}
case PROP_INT:
{
int *param_arr = (int*)data;
/* collect the variables */
for (i=0; i<len; i++) {
item = PySequence_GetItem(value, i);
param_arr[i] = (int)PyLong_AsSsize_t(item); /* deal with any errors later */
Py_DECREF(item);
}
if (PyErr_Occurred()) {
PyErr_SetString(PyExc_AttributeError, "one or more of the values in the sequence could not be used as an int");
return -1;
}
break;
}
case PROP_FLOAT:
{
float *param_arr = (float*)data;
/* collect the variables */
for (i=0; i<len; i++) {
item = PySequence_GetItem(value, i);
param_arr[i] = (float)PyFloat_AsDouble(item); /* deal with any errors later */
Py_DECREF(item);
}
if (PyErr_Occurred()) {
PyErr_SetString(PyExc_AttributeError, "one or more of the values in the sequence could not be used as a float");
return -1;
}
break;
}
}
} else {
/* Normal Property (not an array) */
/* see if we can coorce into a python type - PropertyType */
switch (type) {
case PROP_BOOLEAN:
{
int param = PyObject_IsTrue( value );
if( param < 0 ) {
PyErr_SetString(PyExc_TypeError, "expected True/False or 0/1");
return -1;
} else {
*((int*)data)= param;
}
break;
}
case PROP_INT:
{
int param = PyLong_AsSsize_t(value);
if (PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "expected an int type");
return -1;
} else {
*((int*)data)= param;
}
break;
}
case PROP_FLOAT:
{
float param = PyFloat_AsDouble(value);
if (PyErr_Occurred()) {
PyErr_SetString(PyExc_TypeError, "expected a float type");
return -1;
} else {
*((float*)data)= param;
}
break;
}
case PROP_STRING:
{
char *param = _PyUnicode_AsString(value);
if (param==NULL) {
PyErr_SetString(PyExc_TypeError, "expected a string type");
return -1;
} else {
*((char**)data)= param;
}
break;
}
case PROP_ENUM:
{
char *param = _PyUnicode_AsString(value);
if (param==NULL) {
char *enum_str= pyrna_enum_as_string(ptr, prop);
PyErr_Format(PyExc_TypeError, "expected a string enum type in (%s)", enum_str);
MEM_freeN(enum_str);
return -1;
} else {
int val;
if (RNA_property_enum_value(ptr, prop, param, &val)) {
*((int*)data)= val;
} else {
char *enum_str= pyrna_enum_as_string(ptr, prop);
PyErr_Format(PyExc_AttributeError, "enum \"%s\" not found in (%s)", param, enum_str);
MEM_freeN(enum_str);
return -1;
}
}
break;
}
case PROP_POINTER:
{
StructRNA *ptype= RNA_property_pointer_type(ptr, prop);
if(!BPy_StructRNA_Check(value)) {
PointerRNA tmp;
RNA_pointer_create(NULL, ptype, NULL, &tmp);
PyErr_Format(PyExc_TypeError, "expected a %s type", RNA_struct_identifier(&tmp));
return -1;
} else {
BPy_StructRNA *param= (BPy_StructRNA*)value;
if(ptype == &RNA_AnyType) {
*((PointerRNA*)data)= param->ptr;
}
else if(RNA_struct_is_a(&param->ptr, ptype)) {
*((void**)data)= param->ptr.data;
} else {
PointerRNA tmp;
RNA_pointer_create(NULL, ptype, NULL, &tmp);
PyErr_Format(PyExc_TypeError, "expected a %s type", RNA_struct_identifier(&tmp));
return -1;
}
}
break;
}
case PROP_COLLECTION:
PyErr_SetString(PyExc_AttributeError, "cant pass collections yet");
return -1;
break;
default:
PyErr_SetString(PyExc_AttributeError, "unknown property type (pyrna_py_to_param)");
return -1;
break;
}
}
return 0;
}
PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *data)
{
PyObject *ret;
int type = RNA_property_type(ptr, prop);
int len = RNA_property_array_length(ptr, prop);
int a;
if(len > 0) {
/* resolve the array from a new pytype */
ret = PyTuple_New(len);
switch (type) {
case PROP_BOOLEAN:
for(a=0; a<len; a++)
PyTuple_SET_ITEM(ret, a, PyBool_FromLong( ((int*)data)[a] ));
break;
case PROP_INT:
for(a=0; a<len; a++)
PyTuple_SET_ITEM(ret, a, PyLong_FromSsize_t( (Py_ssize_t)((int*)data)[a] ));
break;
case PROP_FLOAT:
for(a=0; a<len; a++)
PyTuple_SET_ITEM(ret, a, PyFloat_FromDouble( ((float*)data)[a] ));
break;
default:
PyErr_Format(PyExc_AttributeError, "RNA Error: unknown array type \"%d\" (pyrna_param_to_py)", type);
ret = NULL;
break;
}
}
else {
/* see if we can coorce into a python type - PropertyType */
switch (type) {
case PROP_BOOLEAN:
ret = PyBool_FromLong( *(int*)data );
break;
case PROP_INT:
ret = PyLong_FromSsize_t( (Py_ssize_t)*(int*)data );
break;
case PROP_FLOAT:
ret = PyFloat_FromDouble( *(float*)data );
break;
case PROP_STRING:
{
ret = PyUnicode_FromString( *(char**)data );
break;
}
case PROP_ENUM:
{
const char *identifier;
int val = *(int*)data;
if (RNA_property_enum_identifier(ptr, prop, val, &identifier)) {
ret = PyUnicode_FromString( identifier );
} else {
PyErr_Format(PyExc_AttributeError, "RNA Error: Current value \"%d\" matches no enum", val);
ret = NULL;
}
break;
}
case PROP_POINTER:
{
PointerRNA newptr;
StructRNA *type= RNA_property_pointer_type(ptr, prop);
if(type == &RNA_AnyType) {
/* in this case we get the full ptr */
newptr= *(PointerRNA*)data;
}
else {
/* XXX this is missing the ID part! */
RNA_pointer_create(NULL, type, *(void**)data, &newptr);
}
if (newptr.data) {
ret = pyrna_struct_CreatePyObject(&newptr);
} else {
ret = Py_None;
Py_INCREF(ret);
}
break;
}
case PROP_COLLECTION:
/* XXX not supported yet
* ret = pyrna_prop_CreatePyObject(ptr, prop); */
break;
default:
PyErr_Format(PyExc_AttributeError, "RNA Error: unknown type \"%d\" (pyrna_param_to_py)", type);
ret = NULL;
break;
}
}
return ret;
}
static PyObject * pyrna_func_call(BPy_FunctionRNA * self, PyObject *args, PyObject *kw)
{
PointerRNA funcptr;
ParameterList *parms;
ParameterIterator iter;
PropertyRNA *pret, *parm;
PyObject *ret, *item;
int i, tlen, err= 0;
const char *tid, *fid, *pid;
void *retdata= NULL;
/* setup */
RNA_pointer_create(NULL, &RNA_Function, self->func, &funcptr);
pret= RNA_function_return(&self->ptr, self->func);
tlen= PyTuple_GET_SIZE(args);
parms= RNA_parameter_list_create(&self->ptr, self->func);
RNA_parameter_list_begin(parms, &iter);
/* parse function parameters */
for (i= 0; iter.valid; RNA_parameter_list_next(&iter), i++) {
parm= iter.parm;
if (parm==pret) {
retdata= iter.data;
continue;
}
pid= RNA_property_identifier(&funcptr, parm);
item= NULL;
if (i < tlen)
item= PyTuple_GET_ITEM(args, i);
if (kw != NULL)
item= PyDict_GetItemString(kw, pid);
if (item==NULL) {
/* XXX need to add flag for optional required parameters
if (flag & PARAM_OPTIONAL)
continue; */
tid= RNA_struct_identifier(&self->ptr);
fid= RNA_function_identifier(&self->ptr, self->func);
PyErr_Format(PyExc_AttributeError, "%s.%s(): required parameter \"%s\" not specified", tid, fid, pid);
err= -1;
break;
}
err= pyrna_py_to_param(&funcptr, parm, iter.data, item);
if(err!=0)
break;
}
ret= NULL;
if (err==0) {
/* call function */
RNA_function_call(&self->ptr, self->func, parms);
/* return value */
if(pret)
ret= pyrna_param_to_py(&funcptr, pret, retdata);
}
/* cleanup */
RNA_parameter_list_end(&iter);
RNA_parameter_list_free(parms);
if (ret)
return ret;
if (err==-1)
return NULL;
Py_RETURN_NONE;
}
/*-----------------------BPy_StructRNA method def------------------------------*/
PyTypeObject pyrna_struct_Type = {
#if (PY_VERSION_HEX >= 0x02060000)
@@ -1089,6 +1511,92 @@ PyTypeObject pyrna_prop_Type = {
NULL
};
/*-----------------------BPy_FunctionRNA method def------------------------------*/
PyTypeObject pyrna_func_Type = {
#if (PY_VERSION_HEX >= 0x02060000)
PyVarObject_HEAD_INIT(NULL, 0)
#else
/* python 2.5 and below */
PyObject_HEAD_INIT( NULL ) /* required py macro */
0, /* ob_size */
#endif
"FunctionRNA", /* tp_name */
sizeof( BPy_FunctionRNA ), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
( destructor ) pyrna_function_dealloc, /* tp_dealloc */
NULL, /* printfunc tp_print; */
NULL, /* getattrfunc tp_getattr; */
NULL, /* setattrfunc tp_setattr; */
( cmpfunc ) pyrna_func_compare, /* tp_compare */
( reprfunc ) pyrna_func_repr, /* tp_repr */
/* Method suites for standard classes */
NULL, /* PyNumberMethods *tp_as_number; */
NULL, /* PySequenceMethods *tp_as_sequence; */
NULL, /* PyMappingMethods *tp_as_mapping; */
/* More standard operations (here for binary compatibility) */
NULL, /* hashfunc tp_hash; */
(ternaryfunc)pyrna_func_call, /* ternaryfunc tp_call; */
NULL, /* reprfunc tp_str; */
NULL, /*PyObject_GenericGetAttr - MINGW Complains, assign later */ /* getattrofunc tp_getattro; */ /* will only use these if this is a subtype of a py class */
NULL, /*PyObject_GenericSetAttr - MINGW Complains, assign later */ /* setattrofunc tp_setattro; */
/* Functions to access object as input/output buffer */
NULL, /* PyBufferProcs *tp_as_buffer; */
/*** Flags to define presence of optional/expanded features ***/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* long tp_flags; */
NULL, /* char *tp_doc; Documentation string */
/*** Assigned meaning in release 2.0 ***/
/* call function for all accessible objects */
NULL, /* traverseproc tp_traverse; */
/* delete references to contained objects */
NULL, /* inquiry tp_clear; */
/*** Assigned meaning in release 2.1 ***/
/*** rich comparisons ***/
(richcmpfunc)pyrna_func_richcmp, /* richcmpfunc tp_richcompare; */
/*** weak reference enabler ***/
0, /* long tp_weaklistoffset; */
/*** Added in release 2.2 ***/
/* Iterators */
(getiterfunc)NULL, /* getiterfunc tp_iter; */
NULL, /* iternextfunc tp_iternext; */
/*** Attribute descriptor and subclassing stuff ***/
NULL, /* struct PyMethodDef *tp_methods; */
NULL, /* struct PyMemberDef *tp_members; */
NULL, /* struct PyGetSetDef *tp_getset; */
NULL, /* struct _typeobject *tp_base; */
NULL, /* PyObject *tp_dict; */
NULL, /* descrgetfunc tp_descr_get; */
NULL, /* descrsetfunc tp_descr_set; */
0, /* long tp_dictoffset; */
NULL, /* initproc tp_init; */
NULL, /* allocfunc tp_alloc; */
pyrna_func_new, /* newfunc tp_new; */
/* Low-level free-memory routine */
NULL, /* freefunc tp_free; */
/* For PyObject_IS_GC */
NULL, /* inquiry tp_is_gc; */
NULL, /* PyObject *tp_bases; */
/* method resolution order */
NULL, /* PyObject *tp_mro; */
NULL, /* PyObject *tp_cache; */
NULL, /* PyObject *tp_subclasses; */
NULL, /* PyObject *tp_weaklist; */
NULL
};
PyObject* pyrna_struct_Subtype(PointerRNA *ptr)
{
PyObject *newclass = NULL;
@@ -1096,12 +1604,12 @@ PyObject* pyrna_struct_Subtype(PointerRNA *ptr)
if (ptr->type==NULL) {
newclass= NULL; /* Nothing to do */
} else if ((newclass= (PyObject *)BPy_RNA_PYTYPE(ptr->data))) {
} else if ((newclass= RNA_struct_py_type_get(ptr->data))) {
Py_INCREF(newclass);
} else if ((nameprop = RNA_struct_name_property(ptr))) {
/* for now, return the base RNA type rather then a real module */
/* Assume BPy_RNA_PYTYPE(ptr->data) was alredy checked */
/* Assume RNA_struct_py_type_get(ptr->data) was alredy checked */
/* subclass equivelents
- class myClass(myBase):
@@ -1140,7 +1648,7 @@ PyObject* pyrna_struct_Subtype(PointerRNA *ptr)
if (newclass) {
PyObject *rna;
BPy_RNA_PYTYPE(ptr->data) = (void *)newclass; /* Store for later use */
RNA_struct_py_type_set(ptr->data, (void *)newclass); /* Store for later use */
/* Not 100% needed but useful,
* having an instance within a type looks wrong however this instance IS an rna type */
@@ -1152,9 +1660,8 @@ PyObject* pyrna_struct_Subtype(PointerRNA *ptr)
Py_DECREF(args);
if ((char *)&name != nameptr)
if (name != nameptr)
MEM_freeN(nameptr);
}
return newclass;
@@ -1211,6 +1718,25 @@ PyObject *pyrna_prop_CreatePyObject( PointerRNA *ptr, PropertyRNA *prop )
return ( PyObject * ) pyrna;
}
PyObject *pyrna_func_CreatePyObject( PointerRNA *ptr, FunctionRNA *func )
{
BPy_FunctionRNA *pyrna;
pyrna = ( BPy_FunctionRNA * ) PyObject_NEW( BPy_FunctionRNA, &pyrna_func_Type );
if( !pyrna ) {
PyErr_SetString( PyExc_MemoryError, "couldn't create BPy_FunctionRNA object" );
return NULL;
}
pyrna->ptr = *ptr;
pyrna->func = func;
/* TODO - iterator? */
return ( PyObject * ) pyrna;
}
PyObject *BPY_rna_module( void )
{
@@ -1226,6 +1752,8 @@ PyObject *BPY_rna_module( void )
if( PyType_Ready( &pyrna_prop_Type ) < 0 )
return NULL;
if( PyType_Ready( &pyrna_func_Type ) < 0 )
return NULL;
/* for now, return the base RNA type rather then a real module */
RNA_main_pointer_create(G.main, &ptr);
@@ -1291,7 +1819,7 @@ static PyObject *pyrna_basetype_dir(BPy_BaseTypeRNA *self)
return list;
}
PyTypeObject pyrna_basetype_Type = {NULL};
PyTypeObject pyrna_basetype_Type = {};
PyObject *BPY_rna_types(void)
{