[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [31835] trunk/blender: support for defining rna class properties as class attributes

Campbell Barton ideasman42 at gmail.com
Thu Sep 9 07:37:24 CEST 2010


Revision: 31835
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=31835
Author:   campbellbarton
Date:     2010-09-09 07:37:22 +0200 (Thu, 09 Sep 2010)

Log Message:
-----------
support for defining rna class properties as class attributes
eg:
 bpy.types.Scene.myprop = BoolProperty()
 
note, this uses an ugly python metaclass, this should be replaced with a C implimentation which is included but commented out, causing crashes in pythons GC which gives no hint as to where the bug comes from.

Modified Paths:
--------------
    trunk/blender/release/scripts/modules/bpy_types.py
    trunk/blender/source/blender/python/intern/bpy_rna.c

Modified: trunk/blender/release/scripts/modules/bpy_types.py
===================================================================
--- trunk/blender/release/scripts/modules/bpy_types.py	2010-09-09 04:07:28 UTC (rev 31834)
+++ trunk/blender/release/scripts/modules/bpy_types.py	2010-09-09 05:37:22 UTC (rev 31835)
@@ -18,6 +18,65 @@
 
 # <pep8 compliant>
 
+
+class RNA_IDProp_Meta(type):
+    # metaclass for all structures which can have rna prop's defined.
+    # important this class is defined first.
+
+    # setattr, so we can do this...
+    # bpy.types.Scene.myprop = bpy.props.BoolProperty()
+    def __setattr__(cls, attr, value):
+        if type(value) == tuple and len(value) == 2:
+            print(cls, attr, value)
+            if attr in cls.bl_rna.properties:
+                _bpy.props.RemoveProperty(cls, attr=attr)
+            func, kw = value
+            kw["attr"] = attr
+            func(cls, **kw)
+        else:
+            # XXX, pure evil, need to find a better way
+            _setattr = RNA_IDProp_Meta.__setattr__
+            del RNA_IDProp_Meta.__setattr__
+            try:
+                setattr(cls, attr, value)
+            except Exception as exc:
+                RNA_IDProp_Meta.__setattr__ = _setattr
+                raise exc
+            
+            RNA_IDProp_Meta.__setattr__ = _setattr
+
+    def __getattr__(cls, attr):
+        if attr in cls.bl_rna.properties:
+            return cls.bl_rna.properties[attr]
+        elif attr:
+            # XXX, pure evil, need to find a better way
+            _getattr = RNA_IDProp_Meta.__getattr__
+            del RNA_IDProp_Meta.__getattr__
+            try:
+                ret = getattr(cls, attr)
+            except Exception as exc:
+                RNA_IDProp_Meta.__getattr__ = _getattr
+                raise exc
+            
+            RNA_IDProp_Meta.__getattr__ = _getattr
+            return ret
+
+    def __delattr__(cls, attr):
+        if attr in cls.bl_rna.properties:
+            _bpy.props.RemoveProperty(cls, attr=attr)
+        elif attr:
+            # XXX, pure evil, need to find a better way
+            _delattr = RNA_IDProp_Meta.__delattr__
+            del RNA_IDProp_Meta.__delattr__
+            try:
+                delattr(cls, attr, value)
+            except Exception as exc:
+                RNA_IDProp_Meta.__delattr__ = _delattr
+                raise exc
+            
+            RNA_IDProp_Meta.__delattr__ = _delattr
+
+
 from _bpy import types as bpy_types
 import _bpy
 from mathutils import Vector
@@ -42,6 +101,10 @@
         return new_context
 
 
+class ID(StructRNA, metaclass=RNA_IDProp_Meta):
+    __slots__ = ()
+
+
 class Library(bpy_types.ID):
     __slots__ = ()
 
@@ -60,7 +123,7 @@
         return tuple(id_block for attr in attr_links for id_block in getattr(bpy.data, attr) if id_block.library == self)
 
 
-class Texture(bpy_types.ID):
+class Texture(bpy_types.ID, metaclass=RNA_IDProp_Meta):
     __slots__ = ()
 
     @property
@@ -257,15 +320,15 @@
         return bones
 
 
-class PoseBone(StructRNA, _GenericBone):
+class PoseBone(StructRNA, _GenericBone, metaclass=RNA_IDProp_Meta):
     __slots__ = ()
 
 
-class Bone(StructRNA, _GenericBone):
+class Bone(StructRNA, _GenericBone, metaclass=RNA_IDProp_Meta):
     __slots__ = ()
 
 
-class EditBone(StructRNA, _GenericBone):
+class EditBone(StructRNA, _GenericBone, metaclass=RNA_IDProp_Meta):
     __slots__ = ()
 
     def align_orientation(self, other):
@@ -617,12 +680,13 @@
         return result
 
 
-class RNAMetaRegister(RNAMeta):
+class RNAMetaRegister(RNAMeta, RNA_IDProp_Meta):
     @classmethod
     def _register_immediate(cls):
         return True
 
 
+
 class OrderedMeta(RNAMeta):
 
     def __init__(cls, name, bases, attributes):

Modified: trunk/blender/source/blender/python/intern/bpy_rna.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_rna.c	2010-09-09 04:07:28 UTC (rev 31834)
+++ trunk/blender/source/blender/python/intern/bpy_rna.c	2010-09-09 05:37:22 UTC (rev 31835)
@@ -50,6 +50,7 @@
 
 #define USE_MATHUTILS
 #define USE_STRING_COERCE
+#define USE_PY_METACLASS
 
 #ifdef USE_MATHUTILS
 #include "../generic/mathutils.h" /* so we can have mathutils callbacks */
@@ -61,6 +62,7 @@
 static Py_ssize_t pyrna_prop_array_length(BPy_PropertyArrayRNA *self);
 static Py_ssize_t pyrna_prop_collection_length(BPy_PropertyRNA *self);
 static short pyrna_rotation_euler_order_get(PointerRNA *ptr, PropertyRNA **prop_eul_order, short order_fallback);
+static int deferred_register_prop(StructRNA *srna, PyObject *item, PyObject *key);
 
 /* bpyrna vector/euler/quat callbacks */
 static int mathutils_rna_array_cb_index= -1; /* index for our callbacks */
@@ -2583,6 +2585,24 @@
 #endif
 
 //--------------- setattr-------------------------------------------
+
+#ifndef USE_PY_METACLASS
+static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *pyname, PyObject *value)
+{
+	/* check if the value is a property */
+	if(PyTuple_CheckExact(value) && PyTuple_GET_SIZE(value)==2) { /* weak */
+		StructRNA *srna= srna_from_self(cls, "struct_meta_idprop.setattr()");
+		if(srna==NULL) {
+			return -1;
+		}
+		return deferred_register_prop(srna, pyname, value);
+	}
+	else {
+		return PyType_Type.tp_setattro(cls, pyname, value);	
+	}
+}
+#endif
+		
 static int pyrna_struct_setattro( BPy_StructRNA *self, PyObject *pyname, PyObject *value )
 {
 	char *name = _PyUnicode_AsString(pyname);
@@ -3742,6 +3762,43 @@
 	Py_RETURN_NONE;
 }
 
+#ifndef USE_PY_METACLASS
+/* subclasses of pyrna_struct_Type which support idprop definitions use this as a metaclass */
+/* note: tp_base member is set to &PyType_Type on init */
+PyTypeObject pyrna_struct_meta_idprop_Type = {
+	PyVarObject_HEAD_INIT(NULL, 0)
+	"bpy_struct_meta_idprop",	/* tp_name */
+	sizeof(PyTypeObject),		/* tp_basicsize */
+	0,							/* tp_itemsize */
+	/* methods */
+	NULL,						/* tp_dealloc */
+	NULL,                       /* printfunc tp_print; */
+	NULL,						/* getattrfunc tp_getattr; */
+	NULL,						/* setattrfunc tp_setattr; */
+	NULL,						/* tp_compare */ /* DEPRECATED in python 3.0! */
+	NULL,						/* 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; */
+	NULL,						/* ternaryfunc tp_call; */
+	NULL,						/* reprfunc tp_str; */
+	NULL,						/* pyrna_struct_meta_idprop_getattro*/	/* getattrofunc tp_getattro; */
+	NULL /*( setattrofunc ) pyrna_struct_meta_idprop_setattro*/,	/* 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; */
+};
+#endif
+
+
 /*-----------------------BPy_StructRNA method def------------------------------*/
 PyTypeObject pyrna_struct_Type = {
 	PyVarObject_HEAD_INIT(NULL, 0)
@@ -4199,9 +4256,14 @@
 
 /* check if we have a native python subclass, use it when it exists
  * return a borrowed reference */
+static PyObject *bpy_types_dict= NULL;
+#ifdef USE_PY_METACLASS
+#define BPY_SRNA_IDPROP_META "RNA_IDProp_Meta"
+static PyObject *bpy_types_rna_meta_base= NULL;
+#endif
+
 static PyObject* pyrna_srna_ExternalType(StructRNA *srna)
 {
-	PyObject *bpy_types_dict= NULL;
 	const char *idname= RNA_struct_identifier(srna);
 	PyObject *newclass;
 
@@ -4214,8 +4276,10 @@
 			fprintf(stderr, "pyrna_srna_ExternalType: failed to find 'bpy_types' module\n");
 			return NULL;
 		}
-
+#ifdef USE_PY_METACLASS
 		bpy_types_dict = PyModule_GetDict(bpy_types); // borrow
+		bpy_types_rna_meta_base = PyDict_GetItemString(bpy_types_dict, BPY_SRNA_IDPROP_META);
+#endif
 		Py_DECREF(bpy_types); // fairly safe to assume the dict is kept
 	}
 
@@ -4276,16 +4340,30 @@
 
 		/* Assume RNA_struct_py_type_get(srna) was already checked */
 		PyObject *py_base= pyrna_srna_PyBase(srna);
+		PyObject *metaclass;
+		const char *idname= RNA_struct_identifier(srna);
 
-		const char *idname= RNA_struct_identifier(srna);
-		
 		/* remove __doc__ for now */
 		// const char *descr= RNA_struct_ui_description(srna);
 		// if(!descr) descr= "(no docs)";
 		// "__doc__",descr
-		
+
+#ifdef USE_PY_METACLASS 
+		if(RNA_struct_idprops_check(srna) && !PyObject_IsSubclass(py_base, bpy_types_rna_meta_base)) {
+			metaclass= bpy_types_rna_meta_base;
+		}
+#else
+		if(RNA_struct_idprops_check(srna) && !PyObject_IsSubclass(py_base, &pyrna_struct_meta_idprop_Type)) {
+			metaclass= (PyObject *)&pyrna_struct_meta_idprop_Type;
+		}
+#endif
+		else {
+			metaclass= (PyObject *)&PyType_Type;
+		}
+
 		/* always use O not N when calling, N causes refcount errors */
-		newclass = PyObject_CallFunction((PyObject*)&PyType_Type, "s(O){sss()}", idname, py_base, "__module__","bpy.types", "__slots__");
+		newclass = PyObject_CallFunction(metaclass, "s(O){sss()}", idname, py_base, "__module__","bpy.types", "__slots__");
+
 		/* newclass will now have 2 ref's, ???, probably 1 is internal since decrefing here segfaults */
 
 		/* PyC_ObSpit("new class ref", newclass); */
@@ -4299,6 +4377,7 @@
 		}
 		else {
 			/* this should not happen */
+			printf("Error registering '%s'\n", idname);
 			PyErr_Print();
 			PyErr_Clear();
 		}
@@ -4403,7 +4482,14 @@
 	mathutils_rna_array_cb_index= Mathutils_RegisterCallback(&mathutils_rna_array_cb);
 	mathutils_rna_matrix_cb_index= Mathutils_RegisterCallback(&mathutils_rna_matrix_cb);
 #endif
-
+	
+#ifndef USE_PY_METACLASS
+	/* metaclass */
+	pyrna_struct_meta_idprop_Type.tp_base= &PyType_Type;
+	if( PyType_Ready( &pyrna_struct_meta_idprop_Type ) < 0 )
+		return;
+#endif
+	
 	if( PyType_Ready( &pyrna_struct_Type ) < 0 )
 		return;
 





More information about the Bf-blender-cvs mailing list