WIP PyAPI from winter camp discussions, make subtypes of the base RNA python type, eventually allowing us to have python defined RNA classes in

python - lux/pov/renderman materials, lamps etc as well as operators.

At the moment there are 2 ways to do this, The first is like subclassing from python, another (disabled) method copies the base PyTypeObject struct 
and makes some changes.

The PyType is stored in the RNA Struct for reuse, right now there are no access functions - needs to be improved.

Added a python script for printing all blend file data to the console which helps testing the api.

dir(rna) wont work for python 2.x now, use rna.__dir__() instead.
This commit is contained in:
Campbell Barton 2009-03-11 17:28:37 +00:00
parent 891c3bc663
commit 64512d3e8e
10 changed files with 488 additions and 44 deletions

View File

@ -1201,7 +1201,9 @@ static void rna_generate_struct(BlenderRNA *brna, StructRNA *srna, FILE *f)
else fprintf(f, "\tNULL, ");
if(srna->prev) fprintf(f, "&RNA_%s,\n", srna->prev->identifier);
else fprintf(f, "NULL,\n");
fprintf(f, "\tNULL,\n"); /* PyType - Cant initialize here */
fprintf(f, "\t");
rna_print_c_string(f, srna->identifier);
fprintf(f, ", %d, ", srna->flag);

View File

@ -208,6 +208,10 @@ typedef struct CollectionPropertyRNA {
struct StructRNA {
struct StructRNA *next, *prev;
/* python type, this is a subtype of pyrna_struct_Type but used so each struct can have its own type
* which is useful for subclassing RNA */
void *py_type;
/* unique identifier */
const char *identifier;
/* various options */

View File

@ -162,6 +162,7 @@ def rna2epy(target_path):
for rna_struct in structs:
# print(type(rna_struct))
if rna_struct.nested:
continue

View File

@ -80,6 +80,8 @@ void BPY_start_python( void )
// todo - sys paths - our own imports
BPY_rna_init_types();
py_tstate = PyGILState_GetThisThreadState();
PyEval_ReleaseThread(py_tstate);
@ -90,8 +92,11 @@ void BPY_end_python( void )
PyGILState_Ensure(); /* finalizing, no need to grab the state */
// free other python data.
//BPY_rna_free_types();
Py_Finalize( );
BPY_rna_free_types(); /* this MUST run after Py_Finalize since it frees Dynamic allocated PyTypes so we cant free them first */
return;
}
@ -123,7 +128,7 @@ int BPY_run_python_script( bContext *C, const char *fn, struct Text *text )
if( PyErr_Occurred( ) ) {
BPY_free_compiled_text( text );
return NULL;
return 0;
}
}
py_result = PyEval_EvalCode( text->compiled, py_dict, py_dict );

View File

@ -190,10 +190,9 @@ PyObject *pyop_func_get_rna(BPy_OperatorFunc *self)
return NULL;
}
pyrna= (BPy_StructRNA *)pyrna_struct_CreatePyObject(&ptr); /* were not really using &ptr, overwite next */
/* XXX POINTER - if this 'ot' is python generated, it could be free'd */
RNA_pointer_create(NULL, ot->srna, NULL, &pyrna->ptr);
RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
pyrna= (BPy_StructRNA *)pyrna_struct_CreatePyObject(&ptr); /* were not really using &ptr, overwite next */
pyrna->freeptr= 1;
return (PyObject *)pyrna;

View File

@ -30,8 +30,11 @@
#include "MEM_guardedalloc.h"
#include "BKE_global.h" /* evil G.* */
/* floats bigger then this are displayed as inf in the docstrings */
#define MAXFLOAT_DOC 10000000
/* There are 2 ways subclassing can work, PY_CLASS_SUBTYPE - Uses pythons internal subclassing
* - class MyClass(SomeClass): ...
* When PY_CLASS_SUBTYPE is not defined use a C subclass which copies the PyTypeObject and makes some changes.
*/
#define PY_CLASS_SUBTYPE
static int pyrna_struct_compare( BPy_StructRNA * a, BPy_StructRNA * b )
{
@ -565,6 +568,55 @@ static PyMappingMethods pyrna_prop_as_mapping = {
( objobjargproc ) pyrna_prop_assign_subscript, /* mp_ass_subscript */
};
static PyObject *pyrna_struct_dir(BPy_StructRNA * self)
{
PyObject *ret, *dict;
PyObject *pystring = PyUnicode_FromString("__dict__");
/* 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
* */
dict = PyObject_GenericGetAttr((PyObject *)self, pystring);
Py_DECREF(pystring);
if (dict==NULL) {
PyErr_Clear();
ret = PyList_New(0);
}
else {
ret = PyDict_Keys(dict);
Py_DECREF(dict);
}
/* Collect RNA items*/
{
PropertyRNA *iterprop, *nameprop;
CollectionPropertyIterator iter;
char name[256], *nameptr;
iterprop= RNA_struct_iterator_property(&self->ptr);
RNA_property_collection_begin(&self->ptr, iterprop, &iter);
for(; iter.valid; RNA_property_collection_next(&iter)) {
if(iter.ptr.data && (nameprop = RNA_struct_name_property(&iter.ptr))) {
nameptr= RNA_property_string_get_alloc(&iter.ptr, nameprop, name, sizeof(name));
pystring = PyUnicode_FromString(nameptr);
PyList_Append(ret, pystring);
Py_DECREF(pystring);
if ((char *)&name != nameptr)
MEM_freeN(nameptr);
}
}
RNA_property_collection_end(&iter);
}
return ret;
}
//---------------getattr--------------------------------------------
static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
{
@ -585,27 +637,6 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
if (prop) {
ret = pyrna_prop_to_py(&self->ptr, prop);
}
else if (strcmp(name, "__dict__")==0) { /* Not quite correct, adding this so dir() gives good feedback */
PropertyRNA *iterprop, *nameprop;
CollectionPropertyIterator iter;
char name[256], *nameptr;
iterprop= RNA_struct_iterator_property(&self->ptr);
RNA_property_collection_begin(&self->ptr, iterprop, &iter);
ret = PyDict_New();
for(; iter.valid; RNA_property_collection_next(&iter)) {
if(iter.ptr.data && (nameprop = RNA_struct_name_property(&iter.ptr))) {
nameptr= RNA_property_string_get_alloc(&iter.ptr, nameprop, name, sizeof(name));
PyDict_SetItemString(ret, nameptr, Py_None);
if ((char *)&name != nameptr)
MEM_freeN(nameptr);
}
}
RNA_property_collection_end(&iter);
}
else {
PyErr_Format( PyExc_AttributeError, "Attribute \"%s\" not found", name);
ret = NULL;
@ -765,10 +796,10 @@ PyObject *pyrna_prop_iter(BPy_PropertyRNA *self)
return NULL;
}
/*static struct PyMethodDef pyrna_struct_methods[] = {
static struct PyMethodDef pyrna_struct_methods[] = {
{"__dir__", (PyCFunction)pyrna_struct_dir, METH_NOARGS, ""},
{NULL, NULL, 0, NULL}
};*/
};
static struct PyMethodDef pyrna_prop_methods[] = {
{"keys", (PyCFunction)pyrna_prop_keys, METH_NOARGS, ""},
@ -875,7 +906,7 @@ PyTypeObject pyrna_struct_Type = {
NULL, /* iternextfunc tp_iternext; */
/*** Attribute descriptor and subclassing stuff ***/
NULL, /* struct PyMethodDef *tp_methods; */
pyrna_struct_methods, /* struct PyMethodDef *tp_methods; */
NULL, /* struct PyMemberDef *tp_members; */
NULL, /* struct PyGetSetDef *tp_getset; */
NULL, /* struct _typeobject *tp_base; */
@ -990,12 +1021,32 @@ PyObject *pyrna_struct_CreatePyObject( PointerRNA *ptr )
{
BPy_StructRNA *pyrna;
if (ptr->data==NULL) {
if (ptr->data==NULL && ptr->type==NULL) {
Py_RETURN_NONE;
}
#ifdef PY_CLASS_SUBTYPE
pyrna = ( BPy_StructRNA * ) PyObject_NEW( BPy_StructRNA, &pyrna_struct_Type );
if (ptr->type && BPy_RNA_PYTYPE(ptr->type)) {
PyTypeObject *tp = BPy_RNA_PYTYPE(ptr->type);
pyrna = (BPy_StructRNA *) tp->tp_alloc(tp, 0);
}
else {
pyrna = ( BPy_StructRNA * ) PyObject_NEW( BPy_StructRNA, &pyrna_struct_Type );
}
#else
/* get subtype from RNA struct if its been set */
PyTypeObject *tp;
if (ptr->type && ptr->type)
tp = BPy_RNA_PYTYPE(ptr->type);
if (tp==NULL)
tp= &pyrna_struct_Type;
pyrna = ( BPy_StructRNA * ) PyObject_NEW( BPy_StructRNA, tp );
#endif
if( !pyrna ) {
PyErr_SetString( PyExc_MemoryError, "couldn't create BPy_StructRNA object" );
return NULL;
@ -1029,15 +1080,7 @@ PyObject *BPY_rna_module( void )
{
PointerRNA ptr;
if( PyType_Ready( &pyrna_struct_Type ) < 0 )
return NULL;
/* This can't be set in the pytype struct because some compilers complain */
pyrna_prop_Type.tp_getattro = PyObject_GenericGetAttr;
pyrna_prop_Type.tp_setattro = PyObject_GenericSetAttr;
if( PyType_Ready( &pyrna_prop_Type ) < 0 )
return NULL;
/* types init moved to BPY_rna_init_types */
/* for now, return the base RNA type rather then a real module */
RNA_main_pointer_create(G.main, &ptr);
@ -1058,4 +1101,203 @@ PyObject *BPY_rna_doc( void )
return pyrna_struct_CreatePyObject(&ptr);
}
#ifdef PY_CLASS_SUBTYPE
void BPY_rna_init_types(void)
{
/* Now initialize new subtypes based on pyrna_struct_Type */
char tp_name[64];
PointerRNA ptr;
CollectionPropertyIterator iter;
PropertyRNA *nameprop, *prop;
char name[256], *nameptr;
/* This can't be set in the pytype struct because some compilers complain */
pyrna_prop_Type.tp_getattro = PyObject_GenericGetAttr;
pyrna_prop_Type.tp_setattro = PyObject_GenericSetAttr;
if( PyType_Ready( &pyrna_struct_Type ) < 0 )
return;
if( PyType_Ready( &pyrna_prop_Type ) < 0 )
return;
/* for now, return the base RNA type rather then a real module */
RNA_blender_rna_pointer_create(&ptr);
prop = RNA_struct_find_property(&ptr, "structs");
RNA_property_collection_begin(&ptr, prop, &iter);
for(; iter.valid; RNA_property_collection_next(&iter)) {
if(iter.ptr.data && (nameprop = RNA_struct_name_property(&iter.ptr))) {
/* subclass = type(name='myClass', bases=(myBase,), dict={'some':'value'}) */
PyObject *args = PyTuple_New(3);
PyObject *bases = PyTuple_New(1);
PyObject *dict = PyDict_New();
PyObject *newclass;
nameptr= RNA_property_string_get_alloc(&iter.ptr, nameprop, name, sizeof(name));
snprintf(tp_name, 64, "BPyRNA_%s", nameptr);
// arg 1
PyTuple_SET_ITEM(args, 0, PyUnicode_FromString(tp_name));
// arg 2
PyTuple_SET_ITEM(bases, 0, (PyObject *)&pyrna_struct_Type);
Py_INCREF(&pyrna_struct_Type);
PyTuple_SET_ITEM(args, 1, bases);
// arg 3
PyTuple_SET_ITEM(args, 2, dict); // fill with useful subclass things!
if (PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
newclass = PyObject_CallObject((PyObject *)&PyType_Type, args);
Py_DECREF(args);
BPy_RNA_PYTYPE(iter.ptr.data) = (void * )newclass;
// printf("BPyRNA_PyTypes: %s\n", tp_name);
if ((char *)&name != nameptr)
MEM_freeN(nameptr);
}
}
RNA_property_collection_end(&iter);
}
void BPY_rna_free_types(void) {};
#else // Other method uses C defined PyTypes
static void *tp_mem = NULL;
void BPY_rna_init_types(void)
{
PyTypeObject init_struct_type = pyrna_struct_Type; /* store this type to make copies from */
/* Now initialize new subtypes based on pyrna_struct_Type */
typedef struct PyTypeObject_Name {PyTypeObject tp; char name[64];} PyTypeObject_Name;
PyTypeObject_Name *tp_mem_ptr;
PyTypeObject *tp;
char *tp_name;
PointerRNA ptr;
CollectionPropertyIterator iter;
PropertyRNA *nameprop, *prop;
char name[256], *nameptr;
/* This can't be set in the pytype struct because some compilers complain */
pyrna_prop_Type.tp_getattro = PyObject_GenericGetAttr;
pyrna_prop_Type.tp_setattro = PyObject_GenericSetAttr;
if( PyType_Ready( &pyrna_struct_Type ) < 0 )
return;
if( PyType_Ready( &pyrna_prop_Type ) < 0 )
return;
/* Note, making subtypes could be done by using an equivelent of
class MyClass(RNA_Struct)
*/
/* for now, return the base RNA type rather then a real module */
RNA_blender_rna_pointer_create(&ptr);
prop = RNA_struct_find_property(&ptr, "structs");
tp_mem = tp_mem_ptr = MEM_mallocN(sizeof(PyTypeObject_Name) * RNA_property_collection_length(&ptr, prop), "BPyRNA_PyTypes");
RNA_property_collection_begin(&ptr, prop, &iter);
for(; iter.valid; RNA_property_collection_next(&iter)) {
if(iter.ptr.data && (nameprop = RNA_struct_name_property(&iter.ptr))) {
nameptr= RNA_property_string_get_alloc(&iter.ptr, nameprop, name, sizeof(name));
//tp = MEM_mallocN(sizeof(PyTypeObject_Name), "pyrna");
tp = &(tp_mem_ptr->tp);
tp_name = tp_mem_ptr->name;
snprintf(tp_name, 64, "BPyRNA_%s", nameptr);
*tp= init_struct_type; /* Copy the uninitialized pyrna_struct_Type */
tp->tp_name= tp_name;
tp->tp_base= &pyrna_struct_Type;
/* Todo - add special tp->tp_new function that lets us subtupe rna! */
if( PyType_Ready( tp ) < 0 ) {
printf("PyType_Ready failed\n");
}
BPy_RNA_PYTYPE(iter.ptr.data) = tp;
// printf("BPyRNA_PyTypes: %s\n", tp_name);
if ((char *)&name != nameptr)
MEM_freeN(nameptr);
}
tp_mem_ptr++;
}
RNA_property_collection_end(&iter);
}
/* Runs after python is finished, dont use any python functions */
void BPY_rna_free_types(void)
{
if (tp_mem==NULL)
return;
/* We dont really have to clear each structs type but may want to, also might allocate each type on its own */
#if 0
PointerRNA ptr;
CollectionPropertyIterator iter;
PropertyRNA *prop;
/* for now, return the base RNA type rather then a real module */
RNA_blender_rna_pointer_create(&ptr);
prop = RNA_struct_find_property(&ptr, "structs");
RNA_property_collection_begin(&ptr, prop, &iter);
for(; iter.valid; RNA_property_collection_next(&iter)) {
if(iter.ptr.data) {
if (BPy_RNA_PYTYPE(iter.ptr.data)) {
/*
PyTypeObject *tp = BPy_RNA_PYTYPE(iter.ptr.data);
printf("BPyRNA clear: %s %d\n", tp->tp_name, (int)(long)BPy_RNA_PYTYPE(iter.ptr.data));
*/
/* May want to alloc each type on its own however this makes it hard to know if RNA subtypes are using the type */
/* PyMem_Free( BPy_RNA_PYTYPE(iter.ptr.data) );
MEM_freeN( BPy_RNA_PYTYPE(iter.ptr.data) ); */
BPy_RNA_PYTYPE(iter.ptr.data)= NULL;
}
}
}
RNA_property_collection_end(&iter);
#endif
MEM_freeN( tp_mem );
tp_mem = NULL;
}
#endif // END PYTYPE COPY METHOD

View File

@ -36,6 +36,17 @@ extern PyTypeObject pyrna_prop_Type;
#define BPy_StructRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_struct_Type))
#define BPy_PropertyRNA_Check(v) (PyObject_TypeCheck(v, &pyrna_prop_Type))
//XXX add propper accessor function, we know this is just after next/prev pointers
#define BPy_RNA_PYTYPE( _data ) (((BPy_StructFakeType *)(_data))->py_type)
typedef struct {
void * _a;
void * _b;
PyTypeObject *py_type;
} BPy_StructFakeType;
typedef struct {
PyObject_HEAD /* required python macro */
PointerRNA ptr;
@ -50,6 +61,9 @@ typedef struct {
PyObject *BPY_rna_module( void );
PyObject *BPY_rna_doc( void );
void BPY_rna_init_types( void );
void BPY_rna_free_types( void );
PyObject *pyrna_struct_CreatePyObject( PointerRNA *ptr );
PyObject *pyrna_prop_CreatePyObject( PointerRNA *ptr, PropertyRNA *prop );

View File

@ -163,7 +163,7 @@ void PyObSpit(char *name, PyObject *var) {
}
void PyLineSpit(void) {
char filename[512];
char *filename;
int lineno;
PyErr_Clear();

View File

@ -0,0 +1,112 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Contributor(s): Campbell Barton
#
# #**** END GPL LICENSE BLOCK #****
PRINT_DATA = True
VERBOSE = False
VERBOSE_TYPE = False
SKIP_RECURSIVE = False
def seek(r, txt):
print(txt)
newtxt = ''
if len(txt) > 200:
print ("Somthing is wrong")
print (txt)
return
type_r = type(r)
# print(type_r)
# print(dir(r))
# basic types
if type_r in (float, int, bool, type(None)):
if PRINT_DATA:
print(txt + ' -> ' + str(r))
return
if type_r == str:
if PRINT_DATA:
print(txt + ' -> "' + str(r) + '"')
return
try: keys = r.keys()
except: keys = None
if keys != None:
if PRINT_DATA:
print(txt + '.keys() - ' + str(r.keys()))
try: __members__ = dir(r)
except: __members__ = []
for item in __members__:
if item.startswith('__'):
continue
if PRINT_DATA: newtxt = txt + '.' + item
if item == 'rna_type' and VERBOSE_TYPE==False: # just avoid because it spits out loads of data
continue
if SKIP_RECURSIVE:
if item in txt:
if PRINT_DATA:
print(newtxt + ' - (skipping to avoid recursive search)')
continue
try: value = getattr(r, item)
except: value = None
seek( value, newtxt)
if keys:
for k in keys:
if PRINT_DATA: newtxt = txt + '["' + k + '"]'
seek(r.__getitem__(k), newtxt)
else:
try: length = len( r )
except: length = 0
if VERBOSE==False and length >= 4:
for i in (0, length-1):
if i>0:
if PRINT_DATA:
print((' '*len(txt)) + ' ... skipping '+str(length-2)+' items ...')
if PRINT_DATA: newtxt = txt + '[' + str(i) + ']'
seek(r[i], newtxt)
else:
for i in range(length):
if PRINT_DATA: newtxt = txt + '[' + str(i) + ']'
seek(r[i], newtxt)
#print (dir(bpy))
seek(bpy, 'bpy')
#print dir(bpy)
#import sys
#sys.exit()

View File

@ -0,0 +1,65 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Contributor(s): Campbell Barton
#
# #**** END GPL LICENSE BLOCK #****
defs = """
SPACE_EMPTY,
SPACE_VIEW3D,
SPACE_IPO,
SPACE_OOPS,
SPACE_BUTS,
SPACE_FILE,
SPACE_IMAGE,
SPACE_INFO,
SPACE_SEQ,
SPACE_TEXT,
SPACE_IMASEL,
SPACE_SOUND,
SPACE_ACTION,
SPACE_NLA,
SPACE_SCRIPT,
SPACE_TIME,
SPACE_NODE,
SPACEICONMAX
"""
print '\tmod = PyModule_New("dummy");'
print '\tPyModule_AddObject( submodule, "key", mod );'
for d in defs.split('\n'):
d = d.replace(',', ' ')
w = d.split()
if not w:
continue
try: w.remove("#define")
except: pass
# print w
val = w[0]
py_val = w[0]
print '\tPyModule_AddObject( mod, "%s", PyLong_FromSize_t(%s) );' % (val, py_val)