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.
This commit is contained in:
parent
03dbae07d3
commit
1679cd7f96
@ -146,7 +146,7 @@ typedef enum PropertySubType {
|
||||
} 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 @@ typedef enum PropertyFlag {
|
||||
/* 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),
|
||||
|
@ -6875,7 +6875,7 @@ int pyrna_deferred_register_class(StructRNA *srna, PyTypeObject *py_class)
|
||||
|
||||
/*-------------------- 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 @@ static int rna_function_arg_count(FunctionRNA *func)
|
||||
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 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
|
||||
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 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, StructRNA *srna, v
|
||||
/* 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 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
|
||||
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,7 +7219,15 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
|
||||
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 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user