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