disallow setting RNA attributes while drawing, this is bad practice so enforcing...
authorCampbell Barton <ideasman42@gmail.com>
Sat, 4 Dec 2010 06:25:36 +0000 (06:25 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 4 Dec 2010 06:25:36 +0000 (06:25 +0000)
This is ifdef'd and may be disabled later on, or only enabled in debug mode.

This applies to setting any RNA value that has an ID and is not a screen or window-manager datablock.

Some addons break this rule and need fixing but from my tests blender UI scripts are ok.

source/blender/python/intern/bpy_rna.c

index 4265adde7f3cb176e49d0776f4d7c5b183af35f1..cda20348a9d432771e9e0c4acc805c344bc70b5b 100644 (file)
@@ -50,6 +50,7 @@
 #include "DNA_anim_types.h"
 #include "ED_keyframing.h"
 
+#define USE_PEDANTIC_WRITE
 #define USE_MATHUTILS
 #define USE_STRING_COERCE
 
@@ -67,6 +68,31 @@ static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item
 
 /* bpyrna vector/euler/quat callbacks */
 static int mathutils_rna_array_cb_index= -1; /* index for our callbacks */
+#ifdef USE_PEDANTIC_WRITE
+static short rna_disallow_writes= FALSE;
+
+static int rna_id_write_error(PointerRNA *ptr, PyObject *key)
+{
+       ID *id= ptr->id.data;
+       if(id) {
+               const short idcode= GS(id->name);
+               if(!ELEM(idcode, ID_WM, ID_SCR)) { /* may need more added here */
+                       const char *idtype= BKE_idcode_to_name(idcode);
+                       const char *pyname;
+                       if(key && PyUnicode_Check(key)) pyname= _PyUnicode_AsString(key);
+                       else                                                    pyname= "<UNKNOWN>";
+       
+                       /* make a nice string error */
+                       assert(idtype != NULL);
+                       PyErr_Format(PyExc_RuntimeError, "Writing to ID classes in this context is not allowed: %.200s, %.200s datablock, error setting %.200s.%.200s", id->name+2, idtype, RNA_struct_identifier(ptr->type), pyname);
+       
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+#endif
 
 /* subtype not used much yet */
 #define MATHUTILS_CB_SUBTYPE_EUL 0
@@ -104,7 +130,13 @@ static int mathutils_rna_vector_set(BaseMathObject *bmo, int subtype)
        float min, max;
        if(self->prop==NULL)
                return 0;
-       
+
+#ifdef USE_PEDANTIC_WRITE
+       if(rna_disallow_writes && rna_id_write_error(&self->ptr, NULL)) {
+               return 0;
+       }
+#endif
+
        if (!RNA_property_editable_flag(&self->ptr, self->prop)) {
                PyErr_Format(PyExc_AttributeError, "bpy_prop \"%.200s.%.200s\" is read-only", RNA_struct_identifier(self->ptr.type), RNA_property_identifier(self->prop));
                return 0;
@@ -157,6 +189,12 @@ static int mathutils_rna_vector_set_index(BaseMathObject *bmo, int UNUSED(subtyp
        if(self->prop==NULL)
                return 0;
        
+#ifdef USE_PEDANTIC_WRITE
+       if(rna_disallow_writes && rna_id_write_error(&self->ptr, NULL)) {
+               return 0;
+       }
+#endif
+       
        if (!RNA_property_editable_flag(&self->ptr, self->prop)) {
                PyErr_Format(PyExc_AttributeError, "bpy_prop \"%.200s.%.200s\" is read-only", RNA_struct_identifier(self->ptr.type), RNA_property_identifier(self->prop));
                return 0;
@@ -201,7 +239,13 @@ static int mathutils_rna_matrix_set(BaseMathObject *bmo, int UNUSED(subtype))
        
        if(self->prop==NULL)
                return 0;
-       
+
+#ifdef USE_PEDANTIC_WRITE
+       if(rna_disallow_writes && rna_id_write_error(&self->ptr, NULL)) {
+               return 0;
+       }
+#endif
+
        if (!RNA_property_editable_flag(&self->ptr, self->prop)) {
                PyErr_Format(PyExc_AttributeError, "bpy_prop \"%.200s.%.200s\" is read-only", RNA_struct_identifier(self->ptr.type), RNA_property_identifier(self->prop));
                return 0;
@@ -1903,6 +1947,12 @@ static int pyrna_struct_ass_subscript( BPy_StructRNA *self, PyObject *key, PyObj
 {
        IDProperty *group= RNA_struct_idprops(&self->ptr, 1);
 
+#ifdef USE_PEDANTIC_WRITE
+       if(rna_disallow_writes && rna_id_write_error(&self->ptr, key)) {
+               return -1;
+       }
+#endif
+
        if(group==NULL) {
                PyErr_SetString(PyExc_TypeError, "bpy_struct[key] = val: id properties not supported for this type");
                return -1;
@@ -2727,6 +2777,12 @@ static int pyrna_struct_setattro( BPy_StructRNA *self, PyObject *pyname, PyObjec
        char *name = _PyUnicode_AsString(pyname);
        PropertyRNA *prop= NULL;
 
+#ifdef USE_PEDANTIC_WRITE
+       if(rna_disallow_writes && rna_id_write_error(&self->ptr, pyname)) {
+               return -1;
+       }
+#endif
+
        if(name == NULL) {
                PyErr_SetString(PyExc_AttributeError, "bpy_struct: __setattr__ must be a string");
                return -1;
@@ -2840,6 +2896,12 @@ static int pyrna_prop_collection_setattro( BPy_PropertyRNA *self, PyObject *pyna
        PropertyRNA *prop;
        PointerRNA r_ptr;
 
+#ifdef USE_PEDANTIC_WRITE
+       if(rna_disallow_writes && rna_id_write_error(&self->ptr, pyname)) {
+               return -1;
+       }
+#endif
+
        if(name == NULL) {
                PyErr_SetString(PyExc_AttributeError, "bpy_prop: __setattr__ must be a string");
                return -1;
@@ -5080,7 +5142,11 @@ static int bpy_class_call(PointerRNA *ptr, FunctionRNA *func, ParameterList *par
        PyGILState_STATE gilstate;
 
        bContext *C= BPy_GetContext(); // XXX - NEEDS FIXING, QUITE BAD.
-       
+#ifdef USE_PEDANTIC_WRITE
+       /* testing, for correctness, not operator and not draw function */
+       const short is_readonly= strstr("draw", RNA_function_identifier(func)) || !RNA_struct_is_a(ptr->type, &RNA_Operator);
+#endif
+
        py_class= RNA_struct_py_type_get(ptr->type);
        
        /* rare case. can happen when registering subclasses */
@@ -5177,8 +5243,19 @@ static int bpy_class_call(PointerRNA *ptr, FunctionRNA *func, ParameterList *par
                                i++;
                        }
 
+#ifdef USE_PEDANTIC_WRITE
+                       rna_disallow_writes= is_readonly ? TRUE:FALSE;  
+#endif
+                       /* *** Main Caller *** */
+                       
                        ret = PyObject_Call(item, args, NULL);
 
+                       /* *** Done Calling *** */
+
+#ifdef USE_PEDANTIC_WRITE
+                       rna_disallow_writes= FALSE;
+#endif
+
                        RNA_parameter_list_end(&iter);
                        Py_DECREF(item);
                        Py_DECREF(args);