Support for update callbacks in python defined RNA properties as discussed last meeting.
authorCampbell Barton <ideasman42@gmail.com>
Mon, 6 Jun 2011 17:50:20 +0000 (17:50 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 6 Jun 2011 17:50:20 +0000 (17:50 +0000)
This means script authors can perform actions using these callbacks rather then on drawing which puts blender in a readonly state.

Simple example:

import bpy
def up_func(self, context):
    print("test")

bpy.types.Scene.testprop = bpy.props.FloatProperty(update=up_func)
bpy.context.scene.testprop = 11

# prints -> test

source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_define.h
source/blender/makesrna/RNA_types.h
source/blender/makesrna/intern/rna_access.c
source/blender/makesrna/intern/rna_define.c
source/blender/makesrna/intern/rna_internal_types.h
source/blender/python/intern/bpy_props.c

index e0feba3f2fd4a49f698678dc88d22fe07cc218e9..ca19a86e42cd231c16d51542d6d5470a6df254a4 100644 (file)
@@ -650,6 +650,7 @@ PropertyType RNA_property_type(PropertyRNA *prop);
 PropertySubType RNA_property_subtype(PropertyRNA *prop);
 PropertyUnit RNA_property_unit(PropertyRNA *prop);
 int RNA_property_flag(PropertyRNA *prop);
+void *RNA_property_py_data_get(PropertyRNA *prop);
 
 int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop);
 int RNA_property_array_check(PointerRNA *ptr, PropertyRNA *prop);
index b076393ef3de6acde86944d079440c76a9456db6..ac2a89161d96824e272075372a848f71bf8f86e2 100644 (file)
@@ -163,6 +163,8 @@ void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *update
 void RNA_def_property_editable_func(PropertyRNA *prop, const char *editable);
 void RNA_def_property_editable_array_func(PropertyRNA *prop, const char *editable);
 
+void RNA_def_property_update_runtime(PropertyRNA *prop, void *func);
+
 void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength);
 void RNA_def_property_boolean_funcs(PropertyRNA *prop, const char *get, const char *set);
 void RNA_def_property_int_funcs(PropertyRNA *prop, const char *get, const char *set, const char *range);
@@ -172,6 +174,7 @@ void RNA_def_property_string_funcs(PropertyRNA *prop, const char *get, const cha
 void RNA_def_property_pointer_funcs(PropertyRNA *prop, const char *get, const char *set, const char *typef, const char *poll);
 void RNA_def_property_collection_funcs(PropertyRNA *prop, const char *begin, const char *next, const char *end, const char *get, const char *length, const char *lookupint, const char *lookupstring);
 void RNA_def_property_srna(PropertyRNA *prop, const char *type);
+void RNA_def_py_data(PropertyRNA *prop, void *py_data);
 
 /* Function */
 
index b3f2ae01c9955ac9a39c205f353bb8e97fa79cba..090b87e9e39a22170a06af34ef48a0f97d416041 100644 (file)
@@ -190,6 +190,7 @@ typedef enum PropertyFlag {
 
        /* need context for update function */
        PROP_CONTEXT_UPDATE = 1<<22,
+       PROP_CONTEXT_PROPERTY_UPDATE = (1<<22)|(1<<27),
 
        /* Use for arrays or for any data that should not have a referene kept
         * most common case is functions that return arrays where the array */
index 9b8db639cbb4ac2c891454ced01e9259a288f845..ab11f88e0f620bb945a57b4e25006bf5bf529287 100644 (file)
@@ -755,6 +755,11 @@ int RNA_property_flag(PropertyRNA *prop)
        return rna_ensure_property(prop)->flag;
 }
 
+void *RNA_property_py_data_get(PropertyRNA *prop)
+{
+       return prop->py_data;
+}
+
 int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
 {
        return rna_ensure_property_array_length(ptr, prop);
@@ -1344,7 +1349,14 @@ static void rna_property_update(bContext *C, Main *bmain, Scene *scene, PointerR
                        /* ideally no context would be needed for update, but there's some
                           parts of the code that need it still, so we have this exception */
                        if(prop->flag & PROP_CONTEXT_UPDATE) {
-                               if(C) ((ContextUpdateFunc)prop->update)(C, ptr);
+                               if(C) {
+                                       if(prop->flag & PROP_CONTEXT_PROPERTY_UPDATE) {
+                                               ((ContextPropUpdateFunc)prop->update)(C, ptr, prop);
+                                       }
+                                       else {
+                                               ((ContextUpdateFunc)prop->update)(C, ptr);
+                                       }
+                               }
                        }
                        else
                                prop->update(bmain, scene, ptr);
index 66d1036ec44578cd90c7b2222126437d88383ab3..8e9c7e287d63120691ea4a0e7f1ead39a737b980 100644 (file)
@@ -1839,6 +1839,11 @@ void RNA_def_property_update(PropertyRNA *prop, int noteflag, const char *func)
        prop->update= (UpdateFunc)func;
 }
 
+void RNA_def_property_update_runtime(PropertyRNA *prop, void *func)
+{
+       prop->update= func;
+}
+
 void RNA_def_property_dynamic_array_funcs(PropertyRNA *prop, const char *getlength)
 {
        if(!DefRNA.preprocess) {
@@ -2057,6 +2062,11 @@ void RNA_def_property_srna(PropertyRNA *prop, const char *type)
        prop->srna= (StructRNA*)type;
 }
 
+void RNA_def_py_data(PropertyRNA *prop, void *py_data)
+{
+       prop->py_data= py_data;
+}
+
 /* Compact definitions */
 
 PropertyRNA *RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, int default_value, const char *ui_name, const char *ui_description)
@@ -2822,6 +2832,7 @@ void RNA_def_property_free_pointers(PropertyRNA *prop)
                if(prop->identifier) MEM_freeN((void*)prop->identifier);
                if(prop->name) MEM_freeN((void*)prop->name);
                if(prop->description) MEM_freeN((void*)prop->description);
+               if(prop->py_data) MEM_freeN(prop->py_data);
 
                switch(prop->type) {
                        case PROP_BOOLEAN: {
index 771d2afcd96fc8e43e80c7c86b5a1fa411860b24..6ff7bc20b05754183d32f7606e802ced6246327b 100644 (file)
@@ -62,6 +62,7 @@ struct Scene;
 /* Function Callbacks */
 
 typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr);
+typedef void (*ContextPropUpdateFunc)(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop);
 typedef void (*ContextUpdateFunc)(struct bContext *C, struct PointerRNA *ptr);
 typedef int (*EditableFunc)(struct PointerRNA *ptr);
 typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index);
@@ -177,6 +178,9 @@ struct PropertyRNA {
         * any property can have this but should only be used for collections and arrays
         * since python will convert int/bool/pointer's */
        struct StructRNA *srna; /* attributes attached directly to this collection */
+
+       /* python handle to hold all callbacks in a tuple */
+       void *py_data;
 };
 
 /* Property Types */
index 4fef084f0933270fd19ecc8a4837b63fbc26ed38..c6576fe231954fd88aab2db3e3ea311a33ab3741 100644 (file)
 
 #include "../generic/py_capi_utils.h"
 
+/* initial definition of callback slots we'll probably have more then 1 */
+#define BPY_DATA_CB_SLOT_SIZE 1
+
+#define BPY_DATA_CB_SLOT_UPDATE 0
+
 extern BPy_StructRNA *bpy_context_module;
 
 static EnumPropertyItem property_flag_items[]= {
@@ -110,6 +115,45 @@ static PyObject *pymeth_PointerProperty= NULL;
 static PyObject *pymeth_CollectionProperty= NULL;
 static PyObject *pymeth_RemoveProperty= NULL;
 
+PyObject *pyrna_struct_as_instance(PointerRNA *ptr)
+{
+       PyObject *self= NULL;
+       /* first get self */
+       /* operators can store their own instance for later use */
+       if(ptr->data) {
+               void **instance= RNA_struct_instance(ptr);
+
+               if(instance) {
+                       if(*instance) {
+                               self= *instance;
+                               Py_INCREF(self);
+                       }
+               }
+       }
+
+       /* in most cases this will run */
+       if(self == NULL) {
+               self= pyrna_struct_CreatePyObject(ptr);
+       }
+
+       return self;
+}
+
+/* could be moved into bpy_utils */
+static void printf_func_error(PyObject *py_func)
+{
+       /* since we return to C code we can't leave the error */
+       PyCodeObject *f_code= (PyCodeObject *)PyFunction_GET_CODE(py_func);
+       PyErr_Print();
+       PyErr_Clear();
+
+       /* use py style error */
+       fprintf(stderr, "File \"%s\", line %d, in %s\n",
+                       _PyUnicode_AsString(f_code->co_filename),
+                       f_code->co_firstlineno,
+                       _PyUnicode_AsString(((PyFunctionObject *)py_func)->func_name)
+                       );
+}
 
 /* operators and classes use this so it can store the args given but defer
  * running it until the operator runs where these values are used to setup
@@ -130,6 +174,87 @@ static PyObject *bpy_prop_deferred_return(PyObject *func, PyObject *kw)
        return ret;
 }
 
+/* callbacks */
+void bpy_prop_update_cb(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop)
+{
+       PyGILState_STATE gilstate;
+       PyObject **py_data= (PyObject **)RNA_property_py_data_get(prop);
+       PyObject *py_func;
+       PyObject *args;
+       PyObject *self;
+       PyObject *ret;
+
+       BLI_assert(py_data != NULL);
+
+       bpy_context_set(C, &gilstate);
+
+       py_func= py_data[BPY_DATA_CB_SLOT_UPDATE];
+
+       args= PyTuple_New(2);
+       self= pyrna_struct_as_instance(ptr);
+       PyTuple_SET_ITEM(args, 0, self);
+
+       PyTuple_SET_ITEM(args, 1, (PyObject *)bpy_context_module);
+       Py_INCREF(bpy_context_module);
+
+       ret= PyObject_CallObject(py_func, args);
+
+       Py_DECREF(args);
+
+       if(ret == NULL) {
+               printf_func_error(py_func);
+       }
+       else {
+               if(ret != Py_None) {
+                       PyErr_SetString(PyExc_ValueError, "the return value must be None");
+                       printf_func_error(py_func);
+               }
+
+               Py_DECREF(ret);
+       }
+
+       bpy_context_clear(C, &gilstate);
+}
+
+static int bpy_prop_callback_check(PyObject *py_func, int argcount)
+{
+       if(py_func) {
+               if(!PyFunction_Check(py_func)) {
+                       PyErr_Format(PyExc_TypeError,
+                                    "update keyword: expected a function type, not a %.200s",
+                                    Py_TYPE(py_func)->tp_name);
+                       return -1;
+               }
+               else {
+                       PyCodeObject *f_code= (PyCodeObject *)PyFunction_GET_CODE(py_func);
+                       if (f_code->co_argcount != argcount) {
+                               PyErr_Format(PyExc_TypeError,
+                                            "update keyword: expected a function taking %d arguments, not %d",
+                                            argcount, f_code->co_argcount);
+                               return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+static int bpy_prop_callback_assign(struct PropertyRNA *prop, PyObject *update_cb)
+{
+       /* assume this is already checked for type and arg length */
+       if(update_cb) {
+               PyObject **py_data= MEM_callocN(sizeof(PyObject *) * BPY_DATA_CB_SLOT_SIZE, "bpy_prop_callback_assign");
+               RNA_def_property_update_runtime(prop, (void *)bpy_prop_update_cb);
+               py_data[BPY_DATA_CB_SLOT_UPDATE]= update_cb;
+               RNA_def_py_data(prop, py_data);
+
+               RNA_def_property_flag(prop, PROP_CONTEXT_PROPERTY_UPDATE);
+       }
+
+       return 0;
+}
+
 /* this define runs at the start of each function and deals with 
  * returning a deferred property (to be registered later) */
 #define BPY_PROPDEF_HEAD(_func)        \
@@ -184,6 +309,11 @@ static PyObject *bpy_prop_deferred_return(PyObject *func, PyObject *kw)
 "   :type description: string\n" \
 
 
+#define BPY_PROPDEF_UPDATE_DOC \
+"   :arg update: function to be called when this value is modified,\n" \
+"      This function must take 2 values (self, context) and return None.\n" \
+"   :type update: function\n" \
+
 #if 0
 static int bpy_struct_id_used(StructRNA *srna, char *identifier)
 {
@@ -197,7 +327,7 @@ static int bpy_struct_id_used(StructRNA *srna, char *identifier)
 /* Function that sets RNA, NOTE - self is NULL when called from python, but being abused from C so we can pass the srna allong
  * This isnt incorrect since its a python object - but be careful */
 PyDoc_STRVAR(BPy_BoolProperty_doc,
-".. function:: BoolProperty(name=\"\", description=\"\", default=False, options={'ANIMATABLE'}, subtype='NONE')\n"
+".. function:: BoolProperty(name=\"\", description=\"\", default=False, options={'ANIMATABLE'}, subtype='NONE', update=None)\n"
 "\n"
 "   Returns a new boolean property definition.\n"
 "\n"
@@ -207,6 +337,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type options: set\n"
 "   :arg subtype: Enumerator in ['UNSIGNED', 'PERCENTAGE', 'FACTOR', 'ANGLE', 'TIME', 'DISTANCE', 'NONE'].\n"
 "   :type subtype: string\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -215,7 +346,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
        BPY_PROPDEF_HEAD(BoolProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "options", "subtype", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "options", "subtype", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                int def=0;
@@ -224,18 +355,24 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
                int opts=0;
                char *pysubtype= NULL;
                int subtype= PROP_NONE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|ssiO!s:BoolProperty",
+                                                "s#|ssiO!sO:BoolProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &def,
-                                                &PySet_Type, &pyopts, &pysubtype))
+                                                &PySet_Type, &pyopts, &pysubtype,
+                                                &update_cb))
                {
                        return NULL;
                }
 
                BPY_PROPDEF_SUBTYPE_CHECK(BoolProperty, property_flag_items, property_subtype_number_items)
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                prop= RNA_def_property(srna, id, PROP_BOOLEAN, subtype);
                RNA_def_property_boolean_default(prop, def);
                RNA_def_property_ui_text(prop, name, description);
@@ -244,6 +381,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
 
@@ -251,7 +389,7 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
 }
 
 PyDoc_STRVAR(BPy_BoolVectorProperty_doc,
-".. function:: BoolVectorProperty(name=\"\", description=\"\", default=(False, False, False), options={'ANIMATABLE'}, subtype='NONE', size=3)\n"
+".. function:: BoolVectorProperty(name=\"\", description=\"\", default=(False, False, False), options={'ANIMATABLE'}, subtype='NONE', size=3, update=None)\n"
 "\n"
 "   Returns a new vector boolean property definition.\n"
 "\n"
@@ -265,6 +403,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type subtype: string\n"
 "   :arg size: Vector dimensions in [1, and " STRINGIFY(PYRNA_STACK_ARRAY) "].\n"
 "   :type size: int\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -273,7 +412,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
        BPY_PROPDEF_HEAD(BoolVectorProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "options", "subtype", "size", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "options", "subtype", "size", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                int def[PYRNA_STACK_ARRAY]={0};
@@ -284,12 +423,14 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
                int opts=0;
                char *pysubtype= NULL;
                int subtype= PROP_NONE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|ssOO!si:BoolVectorProperty",
+                                                "s#|ssOO!siO:BoolVectorProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &pydef,
-                                                &PySet_Type, &pyopts, &pysubtype,&size))
+                                                &PySet_Type, &pyopts, &pysubtype, &size,
+                                                &update_cb))
                {
                        return NULL;
                }
@@ -304,6 +445,10 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
                if(pydef && PyC_AsArray(def, pydef, size, &PyBool_Type, "BoolVectorProperty(default=sequence)") < 0)
                        return NULL;
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                // prop= RNA_def_boolean_array(srna, id, size, pydef ? def:NULL, name, description);
                prop= RNA_def_property(srna, id, PROP_BOOLEAN, subtype);
                RNA_def_property_array(prop, size);
@@ -314,6 +459,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
        
@@ -321,7 +467,7 @@ static PyObject *BPy_BoolVectorProperty(PyObject *self, PyObject *args, PyObject
 }
 
 PyDoc_STRVAR(BPy_IntProperty_doc,
-".. function:: IntProperty(name=\"\", description=\"\", default=0, min=-sys.maxint, max=sys.maxint, soft_min=-sys.maxint, soft_max=sys.maxint, step=1, options={'ANIMATABLE'}, subtype='NONE')\n"
+".. function:: IntProperty(name=\"\", description=\"\", default=0, min=-sys.maxint, max=sys.maxint, soft_min=-sys.maxint, soft_max=sys.maxint, step=1, options={'ANIMATABLE'}, subtype='NONE', update=None)\n"
 "\n"
 "   Returns a new int property definition.\n"
 "\n"
@@ -331,6 +477,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type options: set\n"
 "   :arg subtype: Enumerator in ['UNSIGNED', 'PERCENTAGE', 'FACTOR', 'ANGLE', 'TIME', 'DISTANCE', 'NONE'].\n"
 "   :type subtype: string\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -339,7 +486,7 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
        BPY_PROPDEF_HEAD(IntProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "options", "subtype", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "options", "subtype", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                int min=INT_MIN, max=INT_MAX, soft_min=INT_MIN, soft_max=INT_MAX, step=1, def=0;
@@ -348,19 +495,25 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
                int opts=0;
                char *pysubtype= NULL;
                int subtype= PROP_NONE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|ssiiiiiiO!s:IntProperty",
+                                                "s#|ssiiiiiiO!sO:IntProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &def,
                                                 &min, &max, &soft_min, &soft_max,
-                                                &step, &PySet_Type, &pyopts, &pysubtype))
+                                                &step, &PySet_Type, &pyopts, &pysubtype,
+                                                &update_cb))
                {
                        return NULL;
                }
 
                BPY_PROPDEF_SUBTYPE_CHECK(IntProperty, property_flag_items, property_subtype_number_items)
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                prop= RNA_def_property(srna, id, PROP_INT, subtype);
                RNA_def_property_int_default(prop, def);
                RNA_def_property_range(prop, min, max);
@@ -371,13 +524,14 @@ static PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
        Py_RETURN_NONE;
 }
 
 PyDoc_STRVAR(BPy_IntVectorProperty_doc,
-".. function:: IntVectorProperty(name=\"\", description=\"\", default=(0, 0, 0), min=-sys.maxint, max=sys.maxint, soft_min=-sys.maxint, soft_max=sys.maxint, options={'ANIMATABLE'}, subtype='NONE', size=3)\n"
+".. function:: IntVectorProperty(name=\"\", description=\"\", default=(0, 0, 0), min=-sys.maxint, max=sys.maxint, soft_min=-sys.maxint, soft_max=sys.maxint, options={'ANIMATABLE'}, subtype='NONE', size=3, update=None)\n"
 "\n"
 "   Returns a new vector int property definition.\n"
 "\n"
@@ -391,6 +545,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type subtype: string\n"
 "   :arg size: Vector dimensions in [1, and " STRINGIFY(PYRNA_STACK_ARRAY) "].\n"
 "   :type size: int\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -399,7 +554,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
        BPY_PROPDEF_HEAD(IntVectorProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "options", "subtype", "size", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "options", "subtype", "size", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                int min=INT_MIN, max=INT_MAX, soft_min=INT_MIN, soft_max=INT_MAX, step=1, def[PYRNA_STACK_ARRAY]={0};
@@ -410,14 +565,16 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
                int opts=0;
                char *pysubtype= NULL;
                int subtype= PROP_NONE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|ssOiiiiiO!si:IntVectorProperty",
+                                                "s#|ssOiiiiiO!siO:IntVectorProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &pydef,
                                                 &min, &max, &soft_min, &soft_max,
                                                 &step, &PySet_Type, &pyopts,
-                                                &pysubtype, &size))
+                                                &pysubtype, &size,
+                                                &update_cb))
                {
                        return NULL;
                }
@@ -432,6 +589,10 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
                if(pydef && PyC_AsArray(def, pydef, size, &PyLong_Type, "IntVectorProperty(default=sequence)") < 0)
                        return NULL;
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                prop= RNA_def_property(srna, id, PROP_INT, subtype);
                RNA_def_property_array(prop, size);
                if(pydef) RNA_def_property_int_array_default(prop, def);
@@ -443,6 +604,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
        Py_RETURN_NONE;
@@ -450,7 +612,7 @@ static PyObject *BPy_IntVectorProperty(PyObject *self, PyObject *args, PyObject
 
 
 PyDoc_STRVAR(BPy_FloatProperty_doc,
-".. function:: FloatProperty(name=\"\", description=\"\", default=0.0, min=sys.float_info.min, max=sys.float_info.max, soft_min=sys.float_info.min, soft_max=sys.float_info.max, step=3, precision=2, options={'ANIMATABLE'}, subtype='NONE', unit='NONE')\n"
+".. function:: FloatProperty(name=\"\", description=\"\", default=0.0, min=sys.float_info.min, max=sys.float_info.max, soft_min=sys.float_info.min, soft_max=sys.float_info.max, step=3, precision=2, options={'ANIMATABLE'}, subtype='NONE', unit='NONE', update=None)\n"
 "\n"
 "   Returns a new float property definition.\n"
 "\n"
@@ -462,6 +624,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type subtype: string\n"
 "   :arg unit: Enumerator in ['NONE', 'LENGTH', 'AREA', 'VOLUME', 'ROTATION', 'TIME', 'VELOCITY', 'ACCELERATION'].\n"
 "   :type unit: string\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -470,7 +633,7 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
        BPY_PROPDEF_HEAD(FloatProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "precision", "options", "subtype", "unit", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "precision", "options", "subtype", "unit", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                float min=-FLT_MAX, max=FLT_MAX, soft_min=-FLT_MAX, soft_max=FLT_MAX, step=3, def=0.0f;
@@ -482,14 +645,16 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
                int subtype= PROP_NONE;
                char *pyunit= NULL;
                int unit= PROP_UNIT_NONE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|ssffffffiO!ss:FloatProperty",
+                                                "s#|ssffffffiO!ssO:FloatProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &def,
                                                 &min, &max, &soft_min, &soft_max,
                                                 &step, &precision, &PySet_Type,
-                                                &pyopts, &pysubtype, &pyunit))
+                                                &pyopts, &pysubtype, &pyunit,
+                                                &update_cb))
                {
                        return NULL;
                }
@@ -501,6 +666,10 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
                        return NULL;
                }
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                prop= RNA_def_property(srna, id, PROP_FLOAT, subtype | unit);
                RNA_def_property_float_default(prop, def);
                RNA_def_property_range(prop, min, max);
@@ -511,13 +680,14 @@ static PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
        Py_RETURN_NONE;
 }
 
 PyDoc_STRVAR(BPy_FloatVectorProperty_doc,
-".. function:: FloatVectorProperty(name=\"\", description=\"\", default=(0.0, 0.0, 0.0), min=sys.float_info.min, max=sys.float_info.max, soft_min=sys.float_info.min, soft_max=sys.float_info.max, step=3, precision=2, options={'ANIMATABLE'}, subtype='NONE', size=3)\n"
+".. function:: FloatVectorProperty(name=\"\", description=\"\", default=(0.0, 0.0, 0.0), min=sys.float_info.min, max=sys.float_info.max, soft_min=sys.float_info.min, soft_max=sys.float_info.max, step=3, precision=2, options={'ANIMATABLE'}, subtype='NONE', size=3, update=None)\n"
 "\n"
 "   Returns a new vector float property definition.\n"
 "\n"
@@ -531,6 +701,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type subtype: string\n"
 "   :arg size: Vector dimensions in [1, and " STRINGIFY(PYRNA_STACK_ARRAY) "].\n"
 "   :type size: int\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -539,7 +710,7 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
        BPY_PROPDEF_HEAD(FloatVectorProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "precision", "options", "subtype", "size", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "min", "max", "soft_min", "soft_max", "step", "precision", "options", "subtype", "size", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                float min=-FLT_MAX, max=FLT_MAX, soft_min=-FLT_MAX, soft_max=FLT_MAX, step=3, def[PYRNA_STACK_ARRAY]={0.0f};
@@ -550,14 +721,16 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
                int opts=0;
                char *pysubtype= NULL;
                int subtype= PROP_NONE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|ssOfffffiO!si:FloatVectorProperty",
+                                                "s#|ssOfffffiO!siO:FloatVectorProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &pydef,
                                                 &min, &max, &soft_min, &soft_max,
                                                 &step, &precision, &PySet_Type,
-                                                &pyopts, &pysubtype, &size))
+                                                &pyopts, &pysubtype, &size,
+                                                &update_cb))
                {
                        return NULL;
                }
@@ -572,6 +745,10 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
                if(pydef && PyC_AsArray(def, pydef, size, &PyFloat_Type, "FloatVectorProperty(default=sequence)") < 0)
                        return NULL;
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                prop= RNA_def_property(srna, id, PROP_FLOAT, subtype);
                RNA_def_property_array(prop, size);
                if(pydef) RNA_def_property_float_array_default(prop, def);
@@ -583,13 +760,14 @@ static PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObjec
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
        Py_RETURN_NONE;
 }
 
 PyDoc_STRVAR(BPy_StringProperty_doc,
-".. function:: StringProperty(name=\"\", description=\"\", default=\"\", maxlen=0, options={'ANIMATABLE'}, subtype='NONE')\n"
+".. function:: StringProperty(name=\"\", description=\"\", default=\"\", maxlen=0, options={'ANIMATABLE'}, subtype='NONE', update=None)\n"
 "\n"
 "   Returns a new string property definition.\n"
 "\n"
@@ -599,6 +777,7 @@ BPY_PROPDEF_DESC_DOC
 "   :type options: set\n"
 "   :arg subtype: Enumerator in ['FILE_PATH', 'DIR_PATH', 'FILENAME', 'NONE'].\n"
 "   :type subtype: string\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -607,7 +786,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
        BPY_PROPDEF_HEAD(StringProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "name", "description", "default", "maxlen", "options", "subtype", NULL};
+               static const char *kwlist[]= {"attr", "name", "description", "default", "maxlen", "options", "subtype", "update", NULL};
                const char *id=NULL, *name="", *description="", *def="";
                int id_len;
                int maxlen=0;
@@ -616,18 +795,24 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
                int opts=0;
                char *pysubtype= NULL;
                int subtype= PROP_NONE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#|sssiO!s:StringProperty",
+                                                "s#|sssiO!sO:StringProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &name, &description, &def,
-                                                &maxlen, &PySet_Type, &pyopts, &pysubtype))
+                                                &maxlen, &PySet_Type, &pyopts, &pysubtype,
+                                                &update_cb))
                {
                        return NULL;
                }
 
                BPY_PROPDEF_SUBTYPE_CHECK(StringProperty, property_flag_items, property_subtype_string_items)
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                prop= RNA_def_property(srna, id, PROP_STRING, subtype);
                if(maxlen != 0) RNA_def_property_string_maxlength(prop, maxlen + 1); /* +1 since it includes null terminator */
                if(def) RNA_def_property_string_default(prop, def);
@@ -637,6 +822,7 @@ static PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
        Py_RETURN_NONE;
@@ -803,23 +989,7 @@ static EnumPropertyItem *bpy_props_enum_itemf(struct bContext *C, PointerRNA *pt
        bpy_context_set(C, &gilstate);
 
        args= PyTuple_New(2);
-
-       /* first get self */
-       /* operators can store their own instance for later use */
-       if(ptr->data) {
-               void **instance = RNA_struct_instance(ptr);
-
-               if(instance) {
-                       if(*instance) {
-                               self= *instance;
-                               Py_INCREF(self);
-                       }
-               }
-       }
-       if(self == NULL) {
-               self= pyrna_struct_CreatePyObject(ptr);
-       }
-
+       self= pyrna_struct_as_instance(ptr);
        PyTuple_SET_ITEM(args, 0, self);
 
        /* now get the context */
@@ -857,17 +1027,7 @@ static EnumPropertyItem *bpy_props_enum_itemf(struct bContext *C, PointerRNA *pt
                *free= 1;
        }
        else {
-               /* since we return to C code we can't leave the error */
-               PyCodeObject *f_code= (PyCodeObject *)PyFunction_GET_CODE(py_func);
-               PyErr_Print();
-               PyErr_Clear();
-
-               /* use py style error */
-               fprintf(stderr, "File \"%s\", line %d, in %s\n",
-                       _PyUnicode_AsString(f_code->co_filename),
-                       f_code->co_firstlineno,
-                               _PyUnicode_AsString(((PyFunctionObject *)py_func)->func_name)
-                       );
+               printf_func_error(py_func);
 
                eitems= DummyRNA_NULL_items;
        }
@@ -878,7 +1038,7 @@ static EnumPropertyItem *bpy_props_enum_itemf(struct bContext *C, PointerRNA *pt
 }
 
 PyDoc_STRVAR(BPy_EnumProperty_doc,
-".. function:: EnumProperty(items, name=\"\", description=\"\", default=\"\", options={'ANIMATABLE'})\n"
+".. function:: EnumProperty(items, name=\"\", description=\"\", default=\"\", options={'ANIMATABLE'}, update=None)\n"
 "\n"
 "   Returns a new enumerator property definition.\n"
 "\n"
@@ -897,6 +1057,7 @@ BPY_PROPDEF_DESC_DOC
 "      the same format as the static list.\n"
 "      This function must take 2 arguments (self, context)\n"
 "   :type items: sequence of string triplets or a function\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -905,7 +1066,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
        BPY_PROPDEF_HEAD(EnumProperty)
        
        if(srna) {
-               static const char *kwlist[]= {"attr", "items", "name", "description", "default", "options", NULL};
+               static const char *kwlist[]= {"attr", "items", "name", "description", "default", "options", "update", NULL};
                const char *id=NULL, *name="", *description="";
                PyObject *def= NULL;
                int id_len;
@@ -916,18 +1077,24 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
                PyObject *pyopts= NULL;
                int opts=0;
                short is_itemf= FALSE;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#O|ssOO!:EnumProperty",
+                                                "s#O|ssOO!O:EnumProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &items, &name, &description,
-                                                &def, &PySet_Type, &pyopts))
+                                                &def, &PySet_Type, &pyopts,
+                                                &update_cb))
                {
                        return NULL;
                }
 
                BPY_PROPDEF_CHECK(EnumProperty, property_flag_enum_items)
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                /* items can be a list or a callable */
                if(PyFunction_Check(items)) { /* dont use PyCallable_Check because we need the function code for errors */
                        PyCodeObject *f_code= (PyCodeObject *)PyFunction_GET_CODE(items);
@@ -975,6 +1142,7 @@ static PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
 
                if(is_itemf == FALSE) {
@@ -1017,7 +1185,7 @@ static StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix
 }
 
 PyDoc_STRVAR(BPy_PointerProperty_doc,
-".. function:: PointerProperty(type=\"\", description=\"\", options={'ANIMATABLE'})\n"
+".. function:: PointerProperty(type=\"\", description=\"\", options={'ANIMATABLE'}, update=None)\n"
 "\n"
 "   Returns a new pointer property definition.\n"
 "\n"
@@ -1027,6 +1195,7 @@ BPY_PROPDEF_NAME_DOC
 BPY_PROPDEF_DESC_DOC
 "   :arg options: Enumerator in ['HIDDEN', 'ANIMATABLE'].\n"
 "   :type options: set\n"
+BPY_PROPDEF_UPDATE_DOC
 );
 static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
@@ -1035,7 +1204,7 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k
        BPY_PROPDEF_HEAD(PointerProperty)
 
        if(srna) {
-               static const char *kwlist[]= {"attr", "type", "name", "description", "options", NULL};
+               static const char *kwlist[]= {"attr", "type", "name", "description", "options", "update", NULL};
                const char *id=NULL, *name="", *description="";
                int id_len;
                PropertyRNA *prop;
@@ -1043,12 +1212,14 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k
                PyObject *type= Py_None;
                PyObject *pyopts= NULL;
                int opts=0;
+               PyObject *update_cb= NULL;
 
                if (!PyArg_ParseTupleAndKeywords(args, kw,
-                                                "s#O|ssO!:PointerProperty",
+                                                "s#O|ssO!O:PointerProperty",
                                                 (char **)kwlist, &id, &id_len,
                                                 &type, &name, &description,
-                                                &PySet_Type, &pyopts))
+                                                &PySet_Type, &pyopts,
+                                                &update_cb))
                {
                        return NULL;
                }
@@ -1059,11 +1230,16 @@ static PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *k
                if(!ptype)
                        return NULL;
 
+               if (bpy_prop_callback_check(update_cb, 2) == -1) {
+                       return NULL;
+               }
+
                prop= RNA_def_pointer_runtime(srna, id, ptype, name, description);
                if(pyopts) {
                        if(opts & PROP_HIDDEN) RNA_def_property_flag(prop, PROP_HIDDEN);
                        if((opts & PROP_ANIMATABLE)==0) RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
                }
+               bpy_prop_callback_assign(prop, update_cb);
                RNA_def_property_duplicate_pointers(srna, prop);
        }
        Py_RETURN_NONE;