Revision: 59536
          
http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=59536
Author:   mont29
Date:     2013-08-26 21:11:52 +0000 (Mon, 26 Aug 2013)
Log Message:
-----------
This commit adds optional parameters for pyfunc implementations of RNA API 
(i.e. callbacks, e.g. draw functions of panels, uiLists, or exec/poll/etc. of 
operators). Thanks to Brecht for he review!

Any parameter after the first flagged with PROP_PYFUNC_OPTIONAL will be 
considered as optional, hence the python code does not have to define/use it. 
This will ease API evolutions by avoiding to break existing code when adding 
non-essential new parameters.

Note: this will need some update to API doc generation, patch is ready, will 
have it reviewed by Campbell asap.

Modified Paths:
--------------
    trunk/blender/source/blender/makesrna/RNA_types.h
    trunk/blender/source/blender/python/intern/bpy_rna.c

Modified: trunk/blender/source/blender/makesrna/RNA_types.h
===================================================================
--- trunk/blender/source/blender/makesrna/RNA_types.h   2013-08-26 20:29:36 UTC 
(rev 59535)
+++ trunk/blender/source/blender/makesrna/RNA_types.h   2013-08-26 21:11:52 UTC 
(rev 59536)
@@ -146,7 +146,7 @@
 } PropertySubType;
 
 /* Make sure enums are updated with thses */
-/* HIGHEST FLAG IN USE: 1 << 29 */
+/* HIGHEST FLAG IN USE: 1 << 30 */
 typedef enum PropertyFlag {
        /* editable means the property is editable in the user
         * interface, properties are editable by default except
@@ -172,10 +172,16 @@
        /* do not write in presets */
        PROP_SKIP_SAVE = (1 << 28),
 
-       /* function paramater flags */
+       /* function parameter flags */
        PROP_REQUIRED = (1 << 2),
        PROP_OUTPUT = (1 << 3),
        PROP_RNAPTR = (1 << 11),
+       /* This allows for non-breaking API updates, when adding non-critical 
new parameter to a callback function.
+        * This way, old py code defining funcs without that parameter would 
still work.
+        * WARNING: any parameter after the first PYFUNC_OPTIONAL one will be 
considered as optional!
+        * NOTE: only for input parameters!
+        */
+       PROP_PYFUNC_OPTIONAL = (1 << 30),
        /* registering */
        PROP_REGISTER = (1 << 4),
        PROP_REGISTER_OPTIONAL = (1 << 4) | (1 << 5),

Modified: trunk/blender/source/blender/python/intern/bpy_rna.c
===================================================================
--- trunk/blender/source/blender/python/intern/bpy_rna.c        2013-08-26 
20:29:36 UTC (rev 59535)
+++ trunk/blender/source/blender/python/intern/bpy_rna.c        2013-08-26 
21:11:52 UTC (rev 59536)
@@ -6875,7 +6875,7 @@
 
 /*-------------------- Type Registration ------------------------*/
 
-static int rna_function_arg_count(FunctionRNA *func)
+static int rna_function_arg_count(FunctionRNA *func, int *min_count)
 {
        const ListBase *lb = RNA_function_defined_parameters(func);
        PropertyRNA *parm;
@@ -6883,13 +6883,23 @@
        int flag = RNA_function_flag(func);
        int is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & 
FUNC_USE_SELF_TYPE);
        int count = is_staticmethod ? 0 : 1;
+       bool done_min_count = false;
 
        for (link = lb->first; link; link = link->next) {
                parm = (PropertyRNA *)link;
-               if (!(RNA_property_flag(parm) & PROP_OUTPUT))
+               if (!(RNA_property_flag(parm) & PROP_OUTPUT)) {
+                       if (!done_min_count && (RNA_property_flag(parm) & 
PROP_PYFUNC_OPTIONAL)) {
+                               /* From now on, following parameters are 
optional in py func */
+                               if (min_count)
+                                       *min_count = count;
+                               done_min_count = true;
+                       }
                        count++;
+               }
        }
 
+       if (!done_min_count && min_count)
+               *min_count = count;
        return count;
 }
 
@@ -6904,7 +6914,7 @@
        PyObject *py_class = (PyObject *)py_data;
        PyObject *base_class = RNA_struct_py_type_get(srna);
        PyObject *item;
-       int i, flag, is_staticmethod, arg_count, func_arg_count;
+       int i, flag, is_staticmethod, arg_count, func_arg_count, 
func_arg_min_count = 0;
        const char *py_class_name = ((PyTypeObject *)py_class)->tp_name;  /* 
__name__ */
 
        if (srna_base) {
@@ -6968,7 +6978,7 @@
                                }
                        }
 
-                       func_arg_count = rna_function_arg_count(func);
+                       func_arg_count = rna_function_arg_count(func, 
&func_arg_min_count);
 
                        if (func_arg_count >= 0) { /* -1 if we don't care*/
                                arg_count = ((PyCodeObject 
*)PyFunction_GET_CODE(item))->co_argcount;
@@ -6976,14 +6986,25 @@
                                /* note, the number of args we check for and 
the number of args we give to
                                 * @staticmethods are different (quirk of 
python),
                                 * this is why rna_function_arg_count() doesn't 
return the value -1*/
-                               if (is_staticmethod)
+                               if (is_staticmethod) {
                                        func_arg_count++;
+                                       func_arg_min_count++;
+                               }
 
-                               if (arg_count != func_arg_count) {
-                                       PyErr_Format(PyExc_ValueError,
-                                                    "expected %.200s, %.200s 
class \"%.200s\" function to have %d args, found %d",
-                                                    class_type, py_class_name, 
RNA_function_identifier(func),
-                                                    func_arg_count, arg_count);
+                               if (arg_count < func_arg_min_count || arg_count 
> func_arg_count) {
+                                       if (func_arg_min_count != 
func_arg_count) {
+                                               PyErr_Format(PyExc_ValueError,
+                                                            "expected %.200s, 
%.200s class \"%.200s\" function to have between %d and %d "
+                                                            "args, found %d",
+                                                            class_type, 
py_class_name, RNA_function_identifier(func),
+                                                            func_arg_count, 
func_arg_min_count, arg_count);
+                                       }
+                                       else {
+                                               PyErr_Format(PyExc_ValueError,
+                                                            "expected %.200s, 
%.200s class \"%.200s\" function to have %d args, found %d",
+                                                            class_type, 
py_class_name, RNA_function_identifier(func),
+                                                            func_arg_count, 
arg_count);
+                                       }
                                        return -1;
                                }
                        }
@@ -7063,7 +7084,7 @@
        PropertyRNA *parm;
        ParameterIterator iter;
        PointerRNA funcptr;
-       int err = 0, i, ret_len = 0;
+       int err = 0, i, ret_len = 0, arg_count;
        int flag = RNA_function_flag(func);
        const char is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & 
FUNC_USE_SELF_TYPE);
        const char is_classmethod = (flag & FUNC_NO_SELF) && (flag & 
FUNC_USE_SELF_TYPE);
@@ -7198,8 +7219,16 @@
                if (item) {
                        RNA_pointer_create(NULL, &RNA_Function, func, &funcptr);
 
-                       args = PyTuple_New(rna_function_arg_count(func)); /* 
first arg is included in 'item' */
+                       if (is_staticmethod) {
+                               arg_count = ((PyCodeObject 
*)PyFunction_GET_CODE(((PyMethodObject *)item)->im_func))->co_argcount - 1;
+                       }
+                       else {
+                               arg_count = ((PyCodeObject 
*)PyFunction_GET_CODE(item))->co_argcount;
+                       }
+//                     args = PyTuple_New(rna_function_arg_count(func)); /* 
first arg is included in 'item' */
+                       args = PyTuple_New(arg_count); /* first arg is included 
in 'item' */
 
+
                        if (is_staticmethod) {
                                i = 0;
                        }
@@ -7230,9 +7259,11 @@
                                        continue;
                                }
 
-                               parmitem = pyrna_param_to_py(&funcptr, parm, 
iter.data);
-                               PyTuple_SET_ITEM(args, i, parmitem);
-                               i++;
+                               if (i < arg_count) {
+                                       parmitem = pyrna_param_to_py(&funcptr, 
parm, iter.data);
+                                       PyTuple_SET_ITEM(args, i, parmitem);
+                                       i++;
+                               }
                        }
 
 #ifdef USE_PEDANTIC_WRITE

_______________________________________________
Bf-blender-cvs mailing list
Bf-blender-cvs@blender.org
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to