diff --git a/source/blender/makesdna/DNA_defaults.h b/source/blender/makesdna/DNA_defaults.h index ef7f573e7a8..b2d38bc849c 100644 --- a/source/blender/makesdna/DNA_defaults.h +++ b/source/blender/makesdna/DNA_defaults.h @@ -40,6 +40,8 @@ uint8_t *_DNA_struct_default_alloc_impl(const uint8_t *data_src, size_t size, const char *alloc_str); +uint8_t *_DNA_struct_default_alloc_from_nr_impl(const int nr, const char *alloc_str); + /** * Wrap with macro that casts correctly. */ @@ -52,6 +54,9 @@ uint8_t *_DNA_struct_default_alloc_impl(const uint8_t *data_src, sizeof(struct_name), \ __func__) +#define DNA_struct_default_alloc_from_nr(nr) \ + (void *)_DNA_struct_default_alloc_from_nr_impl(nr, __func__) + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index fd23c5c618f..99cedaa22c4 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -85,6 +85,8 @@ #include "IMB_imbuf.h" #include "DNA_defaults.h" +#include "DNA_genfile.h" +#include "DNA_sdna_types.h" #include "DNA_armature_types.h" #include "DNA_asset_types.h" @@ -583,3 +585,20 @@ uint8_t *_DNA_struct_default_alloc_impl(const uint8_t *data_src, memcpy(data_dst, data_src, size); return data_dst; } + +uint8_t *_DNA_struct_default_alloc_from_nr_impl(const int nr, const char *alloc_str) +{ + const SDNA *sdna = DNA_sdna_current_get(); + BLI_assert((nr >= 0) && (nr < sdna->structs_len)); + const SDNA_Struct *struct_info = sdna->structs[nr]; + const size_t size = sdna->types_size[struct_info->type]; + uint8_t *data_dst = MEM_mallocN(size, alloc_str); + const uint8_t *data_src = DNA_default_table[nr]; + if (data_src) { + memcpy(data_dst, data_src, size); + } + else { + memset(data_dst, 0x0, size); + } + return data_dst; +} diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 47afa0f9a13..12bb125385b 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -830,6 +830,12 @@ void RNA_struct_py_type_set(StructRNA *srna, void *py_type); void *RNA_struct_blender_type_get(StructRNA *srna); void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type); +/** + * \return the DNA index compatible with #DNA_struct_find_nr + * or -1 when there is no associated struct. + */ +int RNA_struct_dna_index_get(const StructRNA *srna); + struct IDProperty **RNA_struct_idprops_p(PointerRNA *ptr); struct IDProperty *RNA_struct_idprops(PointerRNA *ptr, bool create); bool RNA_struct_idprops_check(StructRNA *srna); diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index e37eb9f7188..d9c2ef9752b 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -374,6 +374,11 @@ typedef enum ParameterFlag { * \note only for input parameters! */ PARM_PYFUNC_OPTIONAL = (1 << 3), + /** + * Support coercing dictionary into RNA structs (for Python). + * The struct must either be part of DNA or use ID-properties such as "OperatorProperties". + */ + PARM_RNAPTR_COERCE = (1 << 4), } ParameterFlag; struct CollectionPropertyIterator; diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 0fadbda5a18..50028aa89e6 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -4271,7 +4271,7 @@ static void rna_generate_struct(BlenderRNA *UNUSED(brna), StructRNA *srna, FILE fprintf(f, "\t"); rna_print_c_string(f, srna->identifier); fprintf(f, ", NULL, NULL"); /* PyType - Can't initialize here */ - fprintf(f, ", %d, NULL, ", srna->flag); + fprintf(f, ", %d, %d, NULL, ", srna->dna_index, srna->flag); rna_print_c_string(f, srna->name); fprintf(f, ",\n\t"); rna_print_c_string(f, srna->description); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 63eb016b5de..243584915c3 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -914,6 +914,11 @@ void RNA_struct_blender_type_set(StructRNA *srna, void *blender_type) srna->blender_type = blender_type; } +int RNA_struct_dna_index_get(const StructRNA *srna) +{ + return srna->dna_index; +} + char *RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len) { PropertyRNA *nameprop; diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index b17ca7d8d59..68a421007f2 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -1084,6 +1084,9 @@ void RNA_def_struct_sdna(StructRNA *srna, const char *structname) #endif ds->dnaname = structname; + + /* May by -1, which is fine. */ + srna->dna_index = DNA_struct_find_nr_wrapper(DefRNA.sdna, structname); } void RNA_def_struct_sdna_from(StructRNA *srna, const char *structname, const char *propname) @@ -1535,6 +1538,10 @@ void RNA_def_parameter_flags(PropertyRNA *prop, { prop->flag |= flag_property; prop->flag_parameter |= flag_parameter; + + if (flag_parameter & PARM_RNAPTR_COERCE) { + BLI_assert(prop->type == PROP_POINTER); + } } void RNA_def_parameter_clear_flags(PropertyRNA *prop, diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index af13baad5a2..745b12f1e1f 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -709,17 +709,17 @@ static void rna_def_imageuser(BlenderRNA *brna) prop = RNA_def_property(srna, "multilayer_layer", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "layer"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ RNA_def_property_ui_text(prop, "Layer", "Layer in multilayer image"); prop = RNA_def_property(srna, "multilayer_pass", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "pass"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ RNA_def_property_ui_text(prop, "Pass", "Pass in multilayer image"); prop = RNA_def_property(srna, "multilayer_view", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "view"); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ + // RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ RNA_def_property_ui_text(prop, "View", "View in multilayer image"); prop = RNA_def_property(srna, "tile", PROP_INT, PROP_UNSIGNED); diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 1c04805be8b..364a50f6b11 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -61,8 +61,12 @@ static void rna_ImagePackedFile_save(ImagePackedFile *imapf, Main *bmain, Report } } -static void rna_Image_save_render( - Image *image, bContext *C, ReportList *reports, const char *path, Scene *scene) +static void rna_Image_save_render(Image *image, + bContext *C, + ReportList *reports, + const char *path, + Scene *scene, + ImageUser *iuser) { ImBuf *ibuf; @@ -71,12 +75,16 @@ static void rna_Image_save_render( } if (scene) { - ImageUser iuser = {NULL}; + ImageUser iuser_copy = *iuser; void *lock; - iuser.scene = scene; + if (iuser_copy.scene == NULL) { + iuser_copy.scene = scene; + } + + printf("iuser_copy: %d %d %d\n", iuser_copy.view, iuser_copy.layer, iuser_copy.pass); - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + ibuf = BKE_image_acquire_ibuf(image, &iuser_copy, &lock); if (ibuf == NULL) { BKE_report(reports, RPT_ERROR, "Could not acquire buffer from image"); @@ -291,6 +299,8 @@ void RNA_api_image(StructRNA *srna) parm = RNA_def_string_file_path(func, "filepath", NULL, 0, "", "Save path"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); RNA_def_pointer(func, "scene", "Scene", "", "Scene to take image parameters from"); + parm = RNA_def_pointer(func, "image_user", "ImageUser", "", "Image user for saving"); + RNA_def_parameter_flags(parm, 0, PARM_RNAPTR_COERCE); func = RNA_def_function(srna, "save", "rna_Image_save"); RNA_def_function_ui_description(func, "Save image to its source path"); diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 723ae384fdf..a744d5b6684 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -513,6 +513,9 @@ struct StructRNA { void *py_type; void *blender_type; + /** Index in (-1 when not DNA). */ + int dna_index; + /* various options */ int flag; /* Each StructRNA type can define own tags which properties can set diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 2be2105d327..3c000921667 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -35,6 +35,9 @@ set(INC ../../../../intern/guardedalloc ../../../../intern/mantaflow/extern ../../../../intern/opencolorio + + # dna_type_offsets.h + ${CMAKE_CURRENT_BINARY_DIR}/../../makesdna/intern ) set(INC_SYS diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index a79a0ed32bf..efd7a4d8ade 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -31,6 +31,7 @@ #include "RNA_types.h" +#include "BLI_alloca.h" #include "BLI_bitmap.h" #include "BLI_dynstr.h" #include "BLI_listbase.h" @@ -56,6 +57,8 @@ #include "RNA_define.h" /* RNA_def_property_free_identifier */ #include "RNA_enum_types.h" +#include "DNA_defaults.h" + #include "CLG_log.h" #include "MEM_guardedalloc.h" @@ -1501,6 +1504,14 @@ int pyrna_pydict_to_props(PointerRNA *ptr, error_val = -1; /* pyrna_py_to_prop sets the error. */ break; } + if (RNA_property_flag(prop) & PROP_NEVER_NULL) { + PyErr_Format(PyExc_TypeError, + "%.200s: keyword \"%.200s\" isn't set but is marked NEVER NULL", + error_prefix, + arg_name ? arg_name : ""); + error_val = -1; /* pyrna_py_to_prop sets the error. */ + break; + } } else { if (pyrna_py_to_prop(ptr, prop, NULL, item, error_prefix)) { @@ -1534,6 +1545,24 @@ int pyrna_pydict_to_props(PointerRNA *ptr, return error_val; } +static PyObject *pyrna_pydict_to_ptr_alloc(PointerRNA *ptr, + PropertyRNA *prop, + PyObject *value, + const char *error_prefix) +{ + StructRNA *ptr_type = RNA_property_pointer_type(ptr, prop); + const int nr = RNA_struct_dna_index_get(ptr_type); + BLI_assert(nr != -1); + uint8_t *data = DNA_struct_default_alloc_from_nr(nr); + PointerRNA value_ptr; + RNA_pointer_create(NULL, ptr_type, data, &value_ptr); + if (pyrna_pydict_to_props(&value_ptr, value, false, error_prefix) == -1) { + MEM_freeN(data); + return NULL; + } + return pyrna_struct_CreatePyObject(&value_ptr); +} + static PyObject *pyrna_func_to_py(const PointerRNA *ptr, FunctionRNA *func) { BPy_FunctionRNA *pyfunc = (BPy_FunctionRNA *)PyObject_NEW(BPy_FunctionRNA, &pyrna_func_Type); @@ -6031,6 +6060,36 @@ static PyObject *small_dict_get_item_string(PyObject *dict, const char *key_look return NULL; } +/** + * \param parm_index: The argument index or -1 for keyword arguments. + */ +static void pyrna_func_error_prefix(BPy_FunctionRNA *self, + PropertyRNA *parm, + const int parm_index, + char *error, + const size_t error_size) +{ + PointerRNA *self_ptr = &self->ptr; + FunctionRNA *self_func = self->func; + if (parm_index == -1) { + BLI_snprintf(error, + error_size, + "%.200s.%.200s(): error with keyword argument \"%.200s\" - ", + RNA_struct_identifier(self_ptr->type), + RNA_function_identifier(self_func), + RNA_property_identifier(parm)); + } + else { + BLI_snprintf(error, + error_size, + "%.200s.%.200s(): error with argument %d, \"%.200s\" - ", + RNA_struct_identifier(self_ptr->type), + RNA_function_identifier(self_func), + parm_index, + RNA_property_identifier(parm)); + } +} + static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject *kw) { /* NOTE: both BPy_StructRNA and BPy_PropertyRNA can be used here. */ @@ -6047,6 +6106,8 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject PropertyRNA *pret_single = NULL; void *retdata_single = NULL; + BPy_StructRNA **items_alloc = NULL; + int items_alloc_count = 0; /* enable this so all strings are copied and freed after calling. * this exposes bugs where the pointer to the string is held and re-used */ @@ -6180,32 +6241,34 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject } } #endif - err = pyrna_py_to_prop(&funcptr, parm, iter.data, item, ""); - - if (err != 0) { - /* the error generated isn't that useful, so generate it again with a useful prefix - * could also write a function to prepend to error messages */ - char error_prefix[512]; - PyErr_Clear(); /* Re-raise. */ - if (kw_arg == true) { - BLI_snprintf(error_prefix, - sizeof(error_prefix), - "%.200s.%.200s(): error with keyword argument \"%.200s\" - ", - RNA_struct_identifier(self_ptr->type), - RNA_function_identifier(self_func), - RNA_property_identifier(parm)); + /* The error generated isn't that useful, so generate it again with a useful prefix + * could also write a function to prepend to error messages. */ + char error_prefix[512]; + + if ((flag_parameter & PARM_RNAPTR_COERCE) && PyDict_CheckExact(item)) { + const PropertyType type = RNA_property_type(parm); + BLI_assert(type == PROP_POINTER); + item = pyrna_pydict_to_ptr_alloc(&funcptr, parm, item, ""); + if (item == NULL) { + PyErr_Clear(); /* Re-raise. */ + pyrna_func_error_prefix(self, parm, kw_arg ? -1 : i, error_prefix, sizeof(error_prefix)); + pyrna_pydict_to_ptr_alloc(&funcptr, parm, item, error_prefix); + err = -1; + break; } - else { - BLI_snprintf(error_prefix, - sizeof(error_prefix), - "%.200s.%.200s(): error with argument %d, \"%.200s\" - ", - RNA_struct_identifier(self_ptr->type), - RNA_function_identifier(self_func), - i, - RNA_property_identifier(parm)); + /* Allocate once (normally `alloca` is avoided in a loop). */ + if (items_alloc == NULL) { + items_alloc = BLI_array_alloca(items_alloc, parms_len); } + items_alloc[items_alloc_count++] = (BPy_StructRNA *)item; + } + err = pyrna_py_to_prop(&funcptr, parm, iter.data, item, ""); + + if (err != 0) { + PyErr_Clear(); /* Re-raise. */ + pyrna_func_error_prefix(self, parm, kw_arg ? -1 : i, error_prefix, sizeof(error_prefix)); pyrna_py_to_prop(&funcptr, parm, iter.data, item, error_prefix); break; @@ -6351,6 +6414,15 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject RNA_parameter_list_end(&iter); RNA_parameter_list_free(&parms); + /* Cleanup any coerced arguments. */ + while (UNLIKELY(--items_alloc_count >= 0)) { + item = (PyObject *)items_alloc[items_alloc_count]; + BLI_assert(item->ob_refcnt == 1); + MEM_freeN(((BPy_StructRNA *)item)->ptr.data); + ((BPy_StructRNA *)item)->ptr.data = NULL; + Py_DECREF((PyObject *)item); + } + if (ret) { return ret; }