Changeset View
Changeset View
Standalone View
Standalone View
source/blender/python/intern/bpy_library_load.c
| Show All 28 Lines | |||||
| #include <stddef.h> | #include <stddef.h> | ||||
| #include "BLI_ghash.h" | #include "BLI_ghash.h" | ||||
| #include "BLI_linklist.h" | #include "BLI_linklist.h" | ||||
| #include "BLI_path_util.h" | #include "BLI_path_util.h" | ||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_utildefines.h" | #include "BLI_utildefines.h" | ||||
| #include "BKE_blendfile_link_append.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_idtype.h" | #include "BKE_idtype.h" | ||||
| #include "BKE_lib_id.h" | #include "BKE_lib_id.h" | ||||
| #include "BKE_main.h" | #include "BKE_main.h" | ||||
| #include "BKE_report.h" | #include "BKE_report.h" | ||||
| #include "DNA_space_types.h" /* FILE_LINK, FILE_RELPATH */ | #include "DNA_space_types.h" /* FILE_LINK, FILE_RELPATH */ | ||||
| ▲ Show 20 Lines • Show All 259 Lines • ▼ Show 20 Lines | static PyObject *bpy_lib_enter(BPy_Library *self) | ||||
| PyTuple_SET_ITEMS(ret, (PyObject *)self_from, (PyObject *)self); | PyTuple_SET_ITEMS(ret, (PyObject *)self_from, (PyObject *)self); | ||||
| Py_INCREF(self); | Py_INCREF(self); | ||||
| BKE_reports_clear(reports); | BKE_reports_clear(reports); | ||||
| return ret; | return ret; | ||||
| } | } | ||||
| static void bpy_lib_exit_warn_idname(BPy_Library *self, | static void UNUSED_FUNCTION(bpy_lib_exit_warn_idname)(BPy_Library *self, | ||||
| const char *name_plural, | const char *name_plural, | ||||
| const char *idname) | const char *idname) | ||||
| { | { | ||||
| PyObject *exc, *val, *tb; | PyObject *exc, *val, *tb; | ||||
| PyErr_Fetch(&exc, &val, &tb); | PyErr_Fetch(&exc, &val, &tb); | ||||
| if (PyErr_WarnFormat(PyExc_UserWarning, | if (PyErr_WarnFormat(PyExc_UserWarning, | ||||
| 1, | 1, | ||||
| "load: '%s' does not contain %s[\"%s\"]", | "load: '%s' does not contain %s[\"%s\"]", | ||||
| self->abspath, | self->abspath, | ||||
| name_plural, | name_plural, | ||||
| Show All 18 Lines | if (PyErr_WarnFormat(PyExc_UserWarning, | ||||
| /* Spurious errors can appear at shutdown */ | /* Spurious errors can appear at shutdown */ | ||||
| if (PyErr_ExceptionMatches(PyExc_Warning)) { | if (PyErr_ExceptionMatches(PyExc_Warning)) { | ||||
| PyErr_WriteUnraisable((PyObject *)self); | PyErr_WriteUnraisable((PyObject *)self); | ||||
| } | } | ||||
| } | } | ||||
| PyErr_Restore(exc, val, tb); | PyErr_Restore(exc, val, tb); | ||||
| } | } | ||||
| struct LibExitLappContextItemsIterData { | |||||
| short idcode; | |||||
| PyObject *py_list; | |||||
| Py_ssize_t py_list_size; | |||||
| }; | |||||
| static bool bpy_lib_exit_lapp_context_items_cb(BlendfileLinkAppendContext *lapp_context, | |||||
| BlendfileLinkAppendContextItem *item, | |||||
| void *userdata) | |||||
| { | |||||
| struct LibExitLappContextItemsIterData *data = userdata; | |||||
| if (BKE_blendfile_link_append_context_item_idcode_get(lapp_context, item) != data->idcode) { | |||||
| return true; | |||||
| } | |||||
campbellbarton: There should be a comment explaining why the ID-codes might not match.
This reads as if it… | |||||
mont29AuthorUnsubmitted Done Inline ActionsSince the py code loops over ID types and then items in each ID type, the whole list of items in lapp_context is looped over for each ID type, so there will be a lot of items that do not match the current id_code, therefore the early return. Will add a comment about this. And unless bpy_lib_exit or BKE_blendfile_ code is buggy, there should be no way to get an unset item in py_list_index - worst case it would just keep the id name set by user in python code, right? But even this should not happen. mont29: Since the py code loops over ID types and then items in each ID type, the whole list of items… | |||||
Done Inline Actionstypo being campbellbarton: typo `being` | |||||
| const int py_list_index = POINTER_AS_INT( | |||||
| BKE_blendfile_link_append_context_item_userdata_get(lapp_context, item)); | |||||
| ID *new_id = BKE_blendfile_link_append_context_item_newid_get(lapp_context, item); | |||||
| BLI_assert(py_list_index < data->py_list_size); | |||||
| PyObject *py_item; | |||||
| if (new_id != NULL) { | |||||
| PointerRNA newid_ptr; | |||||
| RNA_id_pointer_create(new_id, &newid_ptr); | |||||
| py_item = pyrna_struct_CreatePyObject(&newid_ptr); | |||||
| } | |||||
| else { | |||||
| py_item = Py_INCREF_RET(Py_None); | |||||
| } | |||||
| PyList_SET_ITEM(data->py_list, py_list_index, py_item); | |||||
| return true; | |||||
| } | |||||
| static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) | static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) | ||||
| { | { | ||||
| Main *bmain = self->bmain; | Main *bmain = self->bmain; | ||||
| Main *mainl = NULL; | |||||
| const int err = 0; | |||||
| const bool do_append = ((self->flag & FILE_LINK) == 0); | const bool do_append = ((self->flag & FILE_LINK) == 0); | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); | ||||
| /* here appending/linking starts */ | /* here appending/linking starts */ | ||||
| const int id_tag_extra = self->bmain_is_temp ? LIB_TAG_TEMP_MAIN : 0; | const int id_tag_extra = self->bmain_is_temp ? LIB_TAG_TEMP_MAIN : 0; | ||||
| struct LibraryLink_Params liblink_params; | struct LibraryLink_Params liblink_params; | ||||
| BLO_library_link_params_init(&liblink_params, bmain, self->flag, id_tag_extra); | BLO_library_link_params_init(&liblink_params, bmain, self->flag, id_tag_extra); | ||||
| mainl = BLO_library_link_begin(&(self->blo_handle), self->relpath, &liblink_params); | BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new( | ||||
| &liblink_params); | |||||
| BKE_blendfile_link_append_context_library_add(lapp_context, self->abspath, self->blo_handle); | |||||
| { | int idcode_step = 0; | ||||
| int idcode_step = 0, idcode; | short idcode; | ||||
| while ((idcode = BKE_idtype_idcode_iter_step(&idcode_step))) { | while ((idcode = BKE_idtype_idcode_iter_step(&idcode_step))) { | ||||
| if (BKE_idtype_idcode_is_linkable(idcode) && (idcode != ID_WS || do_append)) { | if (!BKE_idtype_idcode_is_linkable(idcode) || (idcode == ID_WS && !do_append)) { | ||||
| continue; | |||||
| } | |||||
| const char *name_plural = BKE_idtype_idcode_to_name_plural(idcode); | const char *name_plural = BKE_idtype_idcode_to_name_plural(idcode); | ||||
| PyObject *ls = PyDict_GetItemString(self->dict, name_plural); | PyObject *ls = PyDict_GetItemString(self->dict, name_plural); | ||||
| // printf("lib: %s\n", name_plural); | // printf("lib: %s\n", name_plural); | ||||
| if (ls && PyList_Check(ls)) { | if (ls == NULL || !PyList_Check(ls)) { | ||||
| /* loop */ | continue; | ||||
| } | |||||
| const Py_ssize_t size = PyList_GET_SIZE(ls); | const Py_ssize_t size = PyList_GET_SIZE(ls); | ||||
| Py_ssize_t i; | if (size == 0) { | ||||
| continue; | |||||
| } | |||||
| for (i = 0; i < size; i++) { | /* loop */ | ||||
| for (Py_ssize_t i = 0; i < size; i++) { | |||||
| PyObject *item_src = PyList_GET_ITEM(ls, i); | PyObject *item_src = PyList_GET_ITEM(ls, i); | ||||
| PyObject *item_dst; /* must be set below */ | |||||
| const char *item_idname = PyUnicode_AsUTF8(item_src); | const char *item_idname = PyUnicode_AsUTF8(item_src); | ||||
| // printf(" %s\n", item_idname); | // printf(" %s\n", item_idname); | ||||
| if (item_idname) { | /* NOTE: index of item in py list is stored in userdata pointer, so that it can be found | ||||
| ID *id = BLO_library_link_named_part( | * later on to replace the ID name by the actual ID pointer. */ | ||||
| mainl, &(self->blo_handle), idcode, item_idname, &liblink_params); | if (item_idname != NULL) { | ||||
| if (id) { | BlendfileLinkAppendContextItem *item = BKE_blendfile_link_append_context_item_add( | ||||
| lapp_context, item_idname, idcode, POINTER_FROM_INT(i)); | |||||
| if (self->bmain_is_temp) { | BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, item, 0); | ||||
| /* If this fails, #LibraryLink_Params.id_tag_extra is not being applied. */ | |||||
| BLI_assert(id->tag & LIB_TAG_TEMP_MAIN); | |||||
| } | |||||
| #ifdef USE_RNA_DATABLOCKS | |||||
| /* swap name for pointer to the id */ | |||||
| item_dst = PyCapsule_New((void *)id, NULL, NULL); | |||||
| #else | |||||
| /* leave as is */ | |||||
| continue; | |||||
| #endif | |||||
| } | |||||
| else { | |||||
| bpy_lib_exit_warn_idname(self, name_plural, item_idname); | |||||
| /* just warn for now */ | |||||
| /* err = -1; */ | |||||
| item_dst = Py_INCREF_RET(Py_None); | |||||
| } | |||||
| /* ID or None */ | |||||
| } | } | ||||
| else { | else { | ||||
| /* XXX, could complain about this */ | /* XXX, could complain about this */ | ||||
| bpy_lib_exit_warn_type(self, item_src); | bpy_lib_exit_warn_type(self, item_src); | ||||
| PyErr_Clear(); | PyErr_Clear(); | ||||
| item_dst = Py_INCREF_RET(Py_None); | |||||
| #ifdef USE_RNA_DATABLOCKS | |||||
| /* We can replace the item immediately with `None`. */ | |||||
| PyObject *py_item = Py_INCREF_RET(Py_None); | |||||
| PyList_SET_ITEM(ls, i, py_item); | |||||
| #endif | |||||
| } | } | ||||
| /* item_dst must be new or already incref'd */ | |||||
| Py_DECREF(item_src); | Py_DECREF(item_src); | ||||
| PyList_SET_ITEM(ls, i, item_dst); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| if (err == -1) { | BKE_blendfile_link(lapp_context, NULL); | ||||
| /* exception raised above, XXX, this leaks some memory */ | |||||
| BLO_blendhandle_close(self->blo_handle); | |||||
| self->blo_handle = NULL; | |||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | |||||
| return NULL; | |||||
| } | |||||
| Library *lib = mainl->curlib; /* newly added lib, assign before append end */ | |||||
| BLO_library_link_end(mainl, &(self->blo_handle), &liblink_params); | |||||
| BLO_blendhandle_close(self->blo_handle); | |||||
| self->blo_handle = NULL; | |||||
| GHash *old_to_new_ids = BLI_ghash_ptr_new(__func__); | |||||
| /* copied from wm_operator.c */ | |||||
| { | |||||
| /* mark all library linked objects to be updated */ | |||||
| BKE_main_lib_objects_recalc_all(bmain); | |||||
| /* append, rather than linking */ | |||||
| if (do_append) { | if (do_append) { | ||||
| BKE_library_make_local(bmain, lib, old_to_new_ids, true, false); | BKE_blendfile_append(lapp_context, NULL); | ||||
| } | |||||
| } | } | ||||
| BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | /* If enabled, replace named items in given lists by the final matching new ID pointer. */ | ||||
| /* finally swap the capsules for real bpy objects | |||||
| * important since BLO_library_append_end initializes NodeTree types used by srna->refine */ | |||||
| #ifdef USE_RNA_DATABLOCKS | #ifdef USE_RNA_DATABLOCKS | ||||
| { | idcode_step = 0; | ||||
| int idcode_step = 0, idcode; | |||||
| while ((idcode = BKE_idtype_idcode_iter_step(&idcode_step))) { | while ((idcode = BKE_idtype_idcode_iter_step(&idcode_step))) { | ||||
| if (BKE_idtype_idcode_is_linkable(idcode) && (idcode != ID_WS || do_append)) { | if (!BKE_idtype_idcode_is_linkable(idcode) || (idcode == ID_WS && !do_append)) { | ||||
| continue; | |||||
| } | |||||
| const char *name_plural = BKE_idtype_idcode_to_name_plural(idcode); | const char *name_plural = BKE_idtype_idcode_to_name_plural(idcode); | ||||
| PyObject *ls = PyDict_GetItemString(self->dict, name_plural); | PyObject *ls = PyDict_GetItemString(self->dict, name_plural); | ||||
| if (ls && PyList_Check(ls)) { | // printf("lib: %s\n", name_plural); | ||||
| if (ls == NULL || !PyList_Check(ls)) { | |||||
| continue; | |||||
| } | |||||
| const Py_ssize_t size = PyList_GET_SIZE(ls); | const Py_ssize_t size = PyList_GET_SIZE(ls); | ||||
| Py_ssize_t i; | if (size == 0) { | ||||
| PyObject *item; | continue; | ||||
| } | |||||
| for (i = 0; i < size; i++) { | /* Loop over linked items in `lapp_context` to find matching python one in the list, and | ||||
| item = PyList_GET_ITEM(ls, i); | * replace them with proper ID pointer. */ | ||||
| if (PyCapsule_CheckExact(item)) { | struct LibExitLappContextItemsIterData iter_data = { | ||||
| PointerRNA id_ptr; | .idcode = idcode, .py_list = ls, .py_list_size = size}; | ||||
| ID *id; | BKE_blendfile_link_append_context_item_foreach( | ||||
| lapp_context, | |||||
| bpy_lib_exit_lapp_context_items_cb, | |||||
| BKE_BLENDFILE_LINK_APPEND_FOREACH_ITEM_FLAG_DO_DIRECT, | |||||
| &iter_data); | |||||
| } | |||||
| #endif // USE_RNA_DATABLOCKS | |||||
| id = PyCapsule_GetPointer(item, NULL); | BLO_blendhandle_close(self->blo_handle); | ||||
| id = BLI_ghash_lookup_default(old_to_new_ids, id, id); | self->blo_handle = NULL; | ||||
| Py_DECREF(item); | |||||
| RNA_id_pointer_create(id, &id_ptr); | BKE_blendfile_link_append_context_free(lapp_context); | ||||
| item = pyrna_struct_CreatePyObject(&id_ptr); | BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); | ||||
| PyList_SET_ITEM(ls, i, item); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| #endif /* USE_RNA_DATABLOCKS */ | |||||
| BLI_ghash_free(old_to_new_ids, NULL, NULL); | |||||
| Py_RETURN_NONE; | Py_RETURN_NONE; | ||||
| } | } | ||||
| static PyObject *bpy_lib_dir(BPy_Library *self) | static PyObject *bpy_lib_dir(BPy_Library *self) | ||||
| { | { | ||||
| return PyDict_Keys(self->dict); | return PyDict_Keys(self->dict); | ||||
| } | } | ||||
| Show All 15 Lines | |||||
There should be a comment explaining why the ID-codes might not match.
This reads as if it might leave the list index unset which creates a bad/corrupt list which will likely crash on access).
If the list item at py_list_index is guaranteed to be set they should be a comment explaining the conditions under which this would happen.