bpy/rna api class feature

- python defined classes will be used when available (otherwise automaically generated metaclasses are made as before)
- use properties rather then functions for python defined rna class's
- call the classes getattr AFTER doing an RNA lookup, avoids setting and clearing exceptions for most attribute lookups, tested UI scripts are ~25% faster.
- extending rna py classes this way is a nicer alternative to modifying the generated metaclasses in place.

Example class

--- snip
class Object(bpy.types.ID):

    def _get_children(self):
        return [child for child in bpy.data.objects if child.parent == self]

    children = property(_get_children)
--- snip

The C initialization function looks in bpy_types.py for classes matching RNA structure names, using them when available.
This means all objects in python will be instances of these classes.
Python properties/funcs defined in ID py class will also be available for subclasses for eg. (Group Mesh etc)
This commit is contained in:
Campbell Barton 2009-11-08 01:13:19 +00:00
parent 30c4c4599d
commit fac2ca1c7c
9 changed files with 202 additions and 170 deletions

@ -1,72 +0,0 @@
# ##### 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.
#
# ##### END GPL LICENSE BLOCK #####
def ord_ind(i1,i2):
if i1<i2: return i1,i2
return i2,i1
def edge_key(ed):
v1, v2 = tuple(ed.verts)
return ord_ind(v1, v2)
def face_edge_keys(face):
verts = tuple(face.verts)
if len(verts)==3:
return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[0])
return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[3]), ord_ind(verts[3], verts[0])
def mesh_edge_keys(mesh):
return [edge_key for face in mesh.faces for edge_key in face.edge_keys()]
def mesh_edge_face_count_dict(mesh, face_edge_keys=None):
# Optional speedup
if face_edge_keys==None:
face_edge_keys = [face.edge_keys() for face in face_list]
face_edge_count = {}
for face_keys in face_edge_keys:
for key in face_keys:
try:
face_edge_count[key] += 1
except:
face_edge_count[key] = 1
return face_edge_count
def mesh_edge_face_count(mesh, face_edge_keys=None):
edge_face_count_dict = mesh.edge_face_count_dict(face_edge_keys)
return [edge_face_count_dict.get(ed.key(), 0) for ed in mesh.edges]
import bpy
# * Edge *
class_obj = bpy.types.MeshEdge
class_obj.key = edge_key
# * Face *
class_obj = bpy.types.MeshFace
class_obj.edge_keys = face_edge_keys
# * Mesh *
class_obj = bpy.types.Mesh
class_obj.edge_keys = mesh_edge_keys
class_obj.edge_face_count = mesh_edge_face_count
class_obj.edge_face_count_dict = mesh_edge_face_count_dict

@ -1,22 +0,0 @@
# ##### 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.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
class_obj = bpy.types.Object
class_obj.getChildren = lambda self: [child for child in bpy.data.objects if child.parent == self]

@ -1,20 +0,0 @@
# ##### 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.
#
# ##### END GPL LICENSE BLOCK #####
import bpy_ext.Object
import bpy_ext.Mesh

@ -0,0 +1,82 @@
# ##### 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.
#
# ##### END GPL LICENSE BLOCK #####
import bpy
StructRNA = bpy.types.Struct.__bases__[0]
# StructRNA = bpy.types.Struct
class Object(bpy.types.ID):
def _get_children(self):
return [child for child in bpy.data.objects if child.parent == self]
children = property(_get_children)
def ord_ind(i1,i2):
if i1<i2: return i1,i2
return i2,i1
class Mesh(bpy.types.ID):
def _get_edge_keys(self):
return [edge_key for face in self.faces for edge_key in face.edge_keys]
edge_keys = property(_get_edge_keys)
def _get_edge_face_count_dict(self):
face_edge_keys = [face.edge_keys for face in self.faces]
face_edge_count = {}
for face_keys in face_edge_keys:
for key in face_keys:
try:
face_edge_count[key] += 1
except:
face_edge_count[key] = 1
return face_edge_count
edge_face_count_dict = property(_get_edge_face_count_dict)
def _get_edge_face_count(self):
edge_face_count_dict = self.edge_face_count_dict
return [edge_face_count_dict.get(ed.key, 0) for ed in mesh.edges]
edge_face_count = property(_get_edge_face_count)
class MeshEdge(StructRNA):
def _get_key(self):
return ord_ind(*tuple(self.verts))
key = property(_get_key)
class MeshFace(StructRNA):
def _get_edge_keys(self):
verts = tuple(self.verts)
if len(verts)==3:
return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[0])
return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[3]), ord_ind(verts[3], verts[0])
edge_keys = property(_get_edge_keys)

@ -28,9 +28,9 @@ def main(context):
mesh = ob.data
face_list = [face for face in mesh.faces]
face_edge_keys = [face.edge_keys() for face in face_list]
face_edge_keys = [face.edge_keys for face in face_list]
edge_face_count = mesh.edge_face_count_dict(face_edge_keys)
edge_face_count = mesh.edge_face_count_dict
def test_interior(index):
for key in face_edge_keys[index]:

@ -256,11 +256,11 @@ def getSelectedEdges(context, me, ob):
if MESH_MODE == 'FACE':
context.scene.tool_settings.mesh_selection_mode = 'EDGE'
# value is [edge, face_sel_user_in]
edge_dict= dict((ed.key(), [ed, 0]) for ed in me.edges)
edge_dict= dict((ed.key, [ed, 0]) for ed in me.edges)
for f in me.faces:
if f.selected:
for edkey in f.edge_keys():
for edkey in f.edge_keys:
edge_dict[edkey][1] += 1
context.scene.tool_settings.mesh_selection_mode = MESH_MODE
@ -280,7 +280,7 @@ def getVertLoops(selEdges, me):
vert_used = [False] * tot
for ed in selEdges:
i1, i2 = ed.key()
i1, i2 = ed.key
vert_siblings[i1].append(i2)
vert_siblings[i2].append(i1)

@ -176,35 +176,35 @@ static void bpy_init_modules( void )
{
PyObject *mod;
/* Needs to be first since this dir is needed for future modules */
char *modpath= BLI_gethome_folder("scripts/modules", BLI_GETHOME_ALL);
if(modpath) {
PyObject *sys_path= PySys_GetObject("path"); /* borrow */
PyObject *py_modpath= PyUnicode_FromString(modpath);
PyList_Insert(sys_path, 0, py_modpath); /* add first */
Py_DECREF(py_modpath);
}
mod = PyModule_New("bpy");
PyModule_AddObject( mod, "data", BPY_rna_module() );
/* PyModule_AddObject( mod, "doc", BPY_rna_doc() ); */
PyModule_AddObject( mod, "types", BPY_rna_types() );
PyModule_AddObject( mod, "props", BPY_rna_props() );
PyModule_AddObject( mod, "__ops__", BPY_operator_module() ); /* ops is now a python module that does the conversion from SOME_OT_foo -> some.foo */
PyModule_AddObject( mod, "ui", BPY_ui_module() ); // XXX very experimental, consider this a test, especially PyCObject is not meant to be permanent
/* add the module so we can import it */
PyDict_SetItemString(PySys_GetObject("modules"), "bpy", mod);
Py_DECREF(mod);
/* add our own modules dir */
{
char *modpath= BLI_gethome_folder("scripts/modules", BLI_GETHOME_ALL);
if(modpath) {
PyObject *sys_path= PySys_GetObject("path"); /* borrow */
PyObject *py_modpath= PyUnicode_FromString(modpath);
PyList_Insert(sys_path, 0, py_modpath); /* add first */
Py_DECREF(py_modpath);
}
bpy_import_test("bpy_ops"); /* adds its self to bpy.ops */
bpy_import_test("bpy_utils"); /* adds its self to bpy.sys */
bpy_import_test("bpy_ext"); /* extensions to our existing types */
}
/* run first, initializes rna types */
BPY_rna_init();
PyModule_AddObject( mod, "types", BPY_rna_types() ); /* needs to be first so bpy_types can run */
bpy_import_test("bpy_types");
PyModule_AddObject( mod, "data", BPY_rna_module() ); /* imports bpy_types by running this */
bpy_import_test("bpy_types");
/* PyModule_AddObject( mod, "doc", BPY_rna_doc() ); */
PyModule_AddObject( mod, "props", BPY_rna_props() );
PyModule_AddObject( mod, "__ops__", BPY_operator_module() ); /* ops is now a python module that does the conversion from SOME_OT_foo -> some.foo */
PyModule_AddObject( mod, "ui", BPY_ui_module() ); // XXX very experimental, consider this a test, especially PyCObject is not meant to be permanent
/* bpy context */
{
bpy_context_module= ( BPy_StructRNA * ) PyObject_NEW( BPy_StructRNA, &pyrna_struct_Type );
@ -213,11 +213,16 @@ static void bpy_init_modules( void )
PyModule_AddObject(mod, "context", (PyObject *)bpy_context_module);
}
/* stand alone utility modules not related to blender directly */
Geometry_Init();
Mathutils_Init();
BGL_Init();
/* add our own modules dir */
{
bpy_import_test("bpy_ops"); /* adds its self to bpy.ops */
bpy_import_test("bpy_utils"); /* adds its self to bpy.sys */
}
}
void BPY_update_modules( void )

@ -1364,16 +1364,6 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
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
*
* Also needed to return methods when its not a subtype
* */
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
if (ret) return ret;
else PyErr_Clear();
/* done with subtypes */
if ((prop = RNA_struct_find_property(&self->ptr, name))) {
ret = pyrna_prop_to_py(&self->ptr, prop);
}
@ -1419,8 +1409,18 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA * self, PyObject *pyname )
}
}
else {
#if 0
PyErr_Format( PyExc_AttributeError, "StructRNA - Attribute \"%.200s\" not found", name);
ret = NULL;
#endif
/* 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
*
* Also needed to return methods when its not a subtype
* */
/* The error raised here will be displayed */
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
}
return ret;
@ -2551,6 +2551,7 @@ static PyObject* pyrna_srna_PyBase(StructRNA *srna) //, PyObject *bpy_types_dict
/* get the base type */
base= RNA_struct_base(srna);
if(base && base != srna) {
/*/printf("debug subtype %s %p\n", RNA_struct_identifier(srna), srna); */
py_base= pyrna_srna_Subtype(base); //, bpy_types_dict);
@ -2564,6 +2565,56 @@ static PyObject* pyrna_srna_PyBase(StructRNA *srna) //, PyObject *bpy_types_dict
return py_base;
}
/* check if we have a native python subclass, use it when it exists
* return a borrowed reference */
static PyObject* pyrna_srna_ExternalType(StructRNA *srna)
{
PyObject *bpy_types_dict= NULL;
const char *idname= RNA_struct_identifier(srna);
PyObject *newclass;
if(bpy_types_dict==NULL) {
PyObject *bpy_types= PyImport_ImportModuleLevel("bpy_types", NULL, NULL, NULL, 0);
if(bpy_types==NULL) {
PyErr_Print();
PyErr_Clear();
fprintf(stderr, "pyrna_srna_ExternalType: failed to find 'bpy_types' module\n");
return NULL;
}
bpy_types_dict = PyModule_GetDict(bpy_types); // borrow
Py_DECREF(bpy_types); // fairly safe to assume the dict is kept
}
newclass= PyDict_GetItemString(bpy_types_dict, idname);
/* sanity check, could skip this unless in debug mode */
if(newclass) {
PyObject *base_compare= pyrna_srna_PyBase(srna);
PyObject *bases= PyObject_GetAttrString(newclass, "__bases__");
if(PyTuple_GET_SIZE(bases)) {
PyObject *base= PyTuple_GET_ITEM(bases, 0);
if(base_compare != base) {
PyLineSpit();
fprintf(stderr, "pyrna_srna_ExternalType: incorrect subclassing of SRNA '%s'\n", idname);
PyObSpit("Expected! ", base_compare);
newclass= NULL;
}
else {
if(G.f & G_DEBUG)
fprintf(stderr, "SRNA Subclassed: '%s'\n", idname);
}
}
Py_DECREF(bases);
}
return newclass;
}
static PyObject* pyrna_srna_Subtype(StructRNA *srna)
{
PyObject *newclass = NULL;
@ -2572,6 +2623,9 @@ static PyObject* pyrna_srna_Subtype(StructRNA *srna)
newclass= NULL; /* Nothing to do */
} else if ((newclass= RNA_struct_py_type_get(srna))) {
Py_INCREF(newclass);
} else if ((newclass= pyrna_srna_ExternalType(srna))) {
pyrna_subtype_set_rna(newclass, srna);
Py_INCREF(newclass);
} else {
/* subclass equivelents
- class myClass(myBase):
@ -2681,23 +2735,26 @@ PyObject *pyrna_prop_CreatePyObject( PointerRNA *ptr, PropertyRNA *prop )
return ( PyObject * ) pyrna;
}
/* bpy.data from python */
static PointerRNA *rna_module_ptr= NULL;
PyObject *BPY_rna_module( void )
void BPY_rna_init(void)
{
BPy_StructRNA *pyrna;
PointerRNA ptr;
#ifdef USE_MATHUTILS // register mathutils callbacks, ok to run more then once.
mathutils_rna_array_cb_index= Mathutils_RegisterCallback(&mathutils_rna_array_cb);
mathutils_rna_matrix_cb_index= Mathutils_RegisterCallback(&mathutils_rna_matrix_cb);
#endif
if( PyType_Ready( &pyrna_struct_Type ) < 0 )
return NULL;
return;
if( PyType_Ready( &pyrna_prop_Type ) < 0 )
return NULL;
return;
}
/* bpy.data from python */
static PointerRNA *rna_module_ptr= NULL;
PyObject *BPY_rna_module(void)
{
BPy_StructRNA *pyrna;
PointerRNA ptr;
/* for now, return the base RNA type rather then a real module */
RNA_main_pointer_create(G.main, &ptr);
@ -2735,21 +2792,22 @@ static PyObject *pyrna_basetype_getattro( BPy_BaseTypeRNA * self, PyObject *pyna
PointerRNA newptr;
PyObject *ret;
ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
if (ret) return ret;
else PyErr_Clear();
if (RNA_property_collection_lookup_string(&self->ptr, self->prop, _PyUnicode_AsString(pyname), &newptr)) {
ret= pyrna_struct_Subtype(&newptr);
if (ret==NULL) {
PyErr_Format(PyExc_SystemError, "bpy.types.%.200s subtype could not be generated, this is a bug!", _PyUnicode_AsString(pyname));
}
return ret;
}
else { /* Override the error */
else {
#if 0
PyErr_Format(PyExc_AttributeError, "bpy.types.%.200s RNA_Struct does not exist", _PyUnicode_AsString(pyname));
return NULL;
#endif
/* The error raised here will be displayed */
ret= PyObject_GenericGetAttr((PyObject *)self, pyname);
}
return ret;
}
static PyObject *pyrna_basetype_dir(BPy_BaseTypeRNA *self);

@ -64,6 +64,7 @@ typedef struct {
/* cheap trick */
#define BPy_BaseTypeRNA BPy_PropertyRNA
void BPY_rna_init( void );
PyObject *BPY_rna_module( void );
void BPY_update_rna_module( void );
/*PyObject *BPY_rna_doc( void );*/