40#include "RNA_prototypes.hh"
47 PyErr_Format(PyExc_TypeError,
48 "Expected a StructRNA of type BlendData, not %.200s",
49 Py_TYPE(obj)->tp_name);
54 if (!(pyrna->
ptr && pyrna->
ptr->type == &RNA_BlendData && pyrna->
ptr->data)) {
55 PyErr_Format(PyExc_TypeError,
56 "Expected a StructRNA of type BlendData, not %.200s",
57 Py_TYPE(pyrna)->tp_name);
60 return static_cast<Main *
>(pyrna->
ptr->data);
79 return int(*((
ushort *)&idcode));
95 if (
data->types_bitmap) {
116 if ((set = PyDict_GetItem(
data->user_map, key)) ==
nullptr) {
118 if (
data->is_subset) {
122 set = PySet_New(
nullptr);
123 PyDict_SetItem(
data->user_map, key, set);
128 if (
data->py_id_curr ==
nullptr) {
132 PySet_Add(set,
data->py_id_curr);
143 ".. method:: user_map(*, subset=None, key_types=None, value_types=None)\n"
145 " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all "
146 "data-blocks using them.\n"
148 " For list of valid set members for key_types & value_types, see: "
149 ":class:`bpy.types.KeyingSetPath.id_type`.\n"
151 " :arg subset: When passed, only these data-blocks and their users will be "
152 "included as keys/values in the map.\n"
153 " :type subset: Sequence[:class:`bpy.types.ID`]\n"
154 " :arg key_types: Filter the keys mapped by ID types.\n"
155 " :type key_types: set[str]\n"
156 " :arg value_types: Filter the values in the set by ID types.\n"
157 " :type value_types: set[str]\n"
158 " :return: dictionary that maps data-blocks ID's to their users.\n"
159 " :rtype: dict[:class:`bpy.types.ID`, set[:class:`bpy.types.ID`]]\n");
170 PyObject *subset =
nullptr;
172 PyObject *key_types =
nullptr;
173 PyObject *val_types =
nullptr;
177 PyObject *
ret =
nullptr;
181 static const char *_keywords[] = {
"subset",
"key_types",
"value_types",
nullptr};
182 static _PyArg_Parser _parser = {
192 if (!_PyArg_ParseTupleAndKeywordsFast(
193 args, kwds, &_parser, &subset, &PySet_Type, &key_types, &PySet_Type, &val_types))
201 if (key_types_bitmap ==
nullptr) {
209 if (val_types_bitmap ==
nullptr) {
215 PyObject *subset_fast = PySequence_Fast(subset,
"user_map");
216 if (subset_fast ==
nullptr) {
220 PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast);
221 Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast);
223 data_cb.
user_map = _PyDict_NewPresized(subset_len);
225 for (; subset_len; subset_array++, subset_len--) {
228 PyErr_Format(PyExc_TypeError,
229 "Expected an ID type in `subset` iterable, not %.200s",
230 Py_TYPE(*subset_array)->tp_name);
236 if (!PyDict_Contains(data_cb.
user_map, *subset_array)) {
237 PyObject *set = PySet_New(
nullptr);
238 PyDict_SetItem(data_cb.
user_map, *subset_array, set);
253 if (key_types_bitmap ==
nullptr && val_types_bitmap !=
nullptr) {
261 (key_types_bitmap ==
nullptr ||
id_check_type(
id, key_types_bitmap)) &&
264 (val_types_bitmap ==
nullptr || key_types_bitmap !=
nullptr))
271 if ((set = PyDict_GetItem(data_cb.
user_map, key)) ==
nullptr) {
272 set = PySet_New(
nullptr);
273 PyDict_SetItem(data_cb.
user_map, key, set);
279 if (val_types_bitmap !=
nullptr && !
id_check_type(
id, val_types_bitmap)) {
299 if (key_types_bitmap !=
nullptr) {
302 if (val_types_bitmap !=
nullptr) {
329 const char *path_src)
332 PyObject *id_file_path_set =
data.id_file_path_set;
336 if (path_src && *path_src) {
338 PySet_Add(id_file_path_set, path);
348 PyObject *id_file_path_set =
data.id_file_path_set;
352 PySet_Add(id_file_path_set, path);
361 bpy_file_path_map_doc,
362 ".. method:: file_path_map(*, subset=None, key_types=None, include_libraries=False)\n"
364 " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all "
365 "file paths used by them.\n"
367 " For list of valid set members for key_types, see: "
368 ":class:`bpy.types.KeyingSetPath.id_type`.\n"
370 " :arg subset: When given, only these data-blocks and their used file paths "
371 "will be included as keys/values in the map.\n"
372 " :type subset: sequence\n"
373 " :arg key_types: When given, filter the keys mapped by ID types. Ignored if ``subset`` is "
375 " :type key_types: set[str]\n"
376 " :arg include_libraries: Include library file paths of linked data. False by default.\n"
377 " :type include_libraries: bool\n"
378 " :return: dictionary of :class:`bpy.types.ID` instances, with sets of file path "
379 "strings as their values.\n"
388 PyObject *subset =
nullptr;
390 PyObject *key_types =
nullptr;
391 PyObject *include_libraries =
nullptr;
394 PyObject *
ret =
nullptr;
399 static const char *_keywords[] = {
"subset",
"key_types",
"include_libraries",
nullptr};
400 static _PyArg_Parser _parser = {
410 if (!_PyArg_ParseTupleAndKeywordsFast(args,
425 if (key_types_bitmap ==
nullptr) {
430 bpath_data.
bmain = bmain;
434 bpath_data.
user_data = &filepathmap_data;
439 PyObject *subset_fast = PySequence_Fast(subset,
"subset");
440 if (subset_fast ==
nullptr) {
444 PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast);
445 Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast);
447 filepathmap_data.
file_path_map = _PyDict_NewPresized(subset_len);
448 for (; subset_len; subset_array++, subset_len--) {
449 if (PyDict_Contains(filepathmap_data.
file_path_map, *subset_array)) {
455 PyErr_Format(PyExc_TypeError,
456 "Expected an ID type in `subset` iterable, not %.200s",
457 Py_TYPE(*subset_array)->tp_name);
468 filepathmap_data.
id = id;
481 if (key_types_bitmap && !
id_check_type(
id, key_types_bitmap)) {
491 filepathmap_data.
id = id;
502 if (key_types_bitmap !=
nullptr) {
541 "Skip paths of linked IDs"},
546 "Skip paths when their matching data is packed"},
551 "Resolve tokens within a virtual filepath to a single, concrete, filepath. Currently only "
552 "used for UDIM tiles"},
554 "SKIP_WEAK_REFERENCES",
556 "Skip Weak References",
557 "Skip weak reference paths. Those paths are typically 'nice to have' extra information, but "
558 "are not used as actual source of data by the current .blend file"},
563 "Skip paths where a single dir is used with an array of files, eg. sequence strip images or "
564 "point-caches. In this case only the first file path is processed. This is needed for "
565 "directory manipulation callbacks which might otherwise modify the same directory multiple "
571 "Reload data when the path is edited"},
572 {0,
nullptr, 0,
nullptr,
nullptr},
577 const size_t path_dst_maxncpy,
578 const char *path_src)
582 if (
data.seen_error) {
587 if (!path_src || !path_src[0]) {
594 PyObject *args = PyTuple_New(3);
598 PyObject *py_path_src = PyUnicode_FromString(path_src);
606 PyObject *py_path_meta = Py_NewRef(Py_None);
610 PyObject *
result = PyObject_CallObject(
data.visit_path_fn, args);
617 data.seen_error =
true;
627 if (!PyUnicode_Check(
result)) {
628 PyErr_Format(PyExc_TypeError,
629 "visit_path_fn() should return a string or None, but returned %s for "
630 "owner_id=\"%s\" and file_path=\"%s\"",
634 data.seen_error =
true;
640 Py_ssize_t replacement_path_length = 0;
641 PyObject *value_coerce =
nullptr;
643 result, &replacement_path_length, &value_coerce);
648 path_dst, replacement_path, std::min(path_dst_maxncpy,
size_t(replacement_path_length + 1)));
650 Py_XDECREF(value_coerce);
657 bpy_file_path_foreach_doc,
658 ".. method:: file_path_foreach(visit_path_fn, *, subset=None, visit_types=None, "
659 "flags={'SKIP_PACKED', 'SKIP_WEAK_REFERENCES'})\n"
661 " Call ``visit_path_fn`` for the file paths used by all ID data-blocks in current "
664 " For list of valid set members for visit_types, see: "
665 ":class:`bpy.types.KeyingSetPath.id_type`.\n"
667 " :arg visit_path_fn: function that takes three parameters: the data-block, a file path, "
668 "and a placeholder for future use. The function should return either ``None`` or a ``str``. "
669 "In the latter case, the visited file path will be replaced with the returned string.\n"
670 " :type visit_path_fn: Callable[[:class:`bpy.types.ID`, str, Any], str|None]\n"
671 " :arg subset: When given, only these data-blocks and their used file paths "
673 " :type subset: set[str]\n"
674 " :arg visit_types: When given, only visit data-blocks of these types. Ignored if "
675 "``subset`` is also given.\n"
676 " :type visit_types: set[str]\n"
677 " :type flags: set[str]\n"
678 " :arg flags: Set of flags that influence which data-blocks are visited. See "
679 ":ref:`rna_enum_file_path_foreach_flag_items`.\n");
687 PyObject *visit_path_fn =
nullptr;
688 PyObject *subset =
nullptr;
689 PyObject *visit_types =
nullptr;
690 std::unique_ptr<BLI_bitmap, MEM_freeN_smart_ptr_deleter> visit_types_bitmap;
691 PyObject *py_flags =
nullptr;
696 static const char *_keywords[] = {
"visit_path_fn",
"subset",
"visit_types",
"flags",
nullptr};
697 static _PyArg_Parser _parser = {
704 ":file_path_foreach",
708 if (!_PyArg_ParseTupleAndKeywordsFast(args,
725 if (visit_types_bitmap_rawptr ==
nullptr) {
728 visit_types_bitmap.reset(visit_types_bitmap_rawptr);
736 reinterpret_cast<int *
>(&bpath_data.
flag),
743 bpath_data.
bmain = bmain;
745 bpath_data.
user_data = &filepathforeach_data;
752 PyObject *subset_fast = PySequence_Fast(subset,
"subset");
757 PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast);
758 const Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast);
759 for (Py_ssize_t index = 0; index < subset_len; index++) {
760 PyObject *subset_item = subset_array[index];
764 PyErr_Format(PyExc_TypeError,
765 "Expected an ID type in `subset` iterable, not %.200s",
766 Py_TYPE(subset_item)->tp_name);
787 if (visit_types_bitmap && !
id_check_type(
id, visit_types_bitmap.get())) {
808 bpy_batch_remove_doc,
809 ".. method:: batch_remove(ids)\n"
811 " Remove (delete) several IDs at once.\n"
813 " Note that this function is quicker than individual calls to :func:`remove()` "
814 "(from :class:`bpy.types.BlendData`\n"
815 " ID collections), but less safe/versatile (it can break Blender, e.g. by removing "
818 " :arg ids: Sequence of IDs (types can be mixed).\n"
819 " :type ids: Sequence[:class:`bpy.types.ID`]\n");
827 PyObject *ids =
nullptr;
829 static const char *_keywords[] = {
"ids",
nullptr};
830 static _PyArg_Parser _parser = {
837 if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &ids)) {
845 PyObject *ids_fast = PySequence_Fast(ids,
"batch_remove");
846 if (ids_fast ==
nullptr) {
850 PyObject **ids_array = PySequence_Fast_ITEMS(ids_fast);
851 Py_ssize_t ids_len = PySequence_Fast_GET_SIZE(ids_fast);
853 for (; ids_len; ids_array++, ids_len--) {
857 PyExc_TypeError,
"Expected an ID type, not %.200s", Py_TYPE(*ids_array)->tp_name);
862 ids_to_delete.
add(
id);
875 bpy_orphans_purge_doc,
876 ".. method:: orphans_purge()\n"
878 " Remove (delete) all IDs with no user.\n"
880 " :arg do_local_ids: Include unused local IDs in the deletion, defaults to True\n"
881 " :type do_local_ids: bool, optional\n"
882 " :arg do_linked_ids: Include unused linked IDs in the deletion, defaults to True\n"
883 " :type do_linked_ids: bool, optional\n"
884 " :arg do_recursive: Recursively check for unused IDs, ensuring no orphaned one "
885 "remain after a single run of that function, defaults to False\n"
886 " :type do_recursive: bool, optional\n"
887 " :return: The number of deleted IDs.\n");
900 static const char *_keywords[] = {
"do_local_ids",
"do_linked_ids",
"do_recursive",
nullptr};
901 static _PyArg_Parser _parser = {
911 if (!_PyArg_ParseTupleAndKeywordsFast(args,
928 return PyLong_FromSize_t(0);
935 return PyLong_FromSize_t(num_datablocks_deleted);
940# pragma clang diagnostic push
941# pragma clang diagnostic ignored "-Wcast-function-type"
943# pragma GCC diagnostic push
944# pragma GCC diagnostic ignored "-Wcast-function-type"
951 METH_VARARGS | METH_KEYWORDS,
957 METH_VARARGS | METH_KEYWORDS,
958 bpy_file_path_map_doc,
963 METH_VARARGS | METH_KEYWORDS,
964 bpy_file_path_foreach_doc,
969 METH_VARARGS | METH_KEYWORDS,
970 bpy_batch_remove_doc,
975 METH_VARARGS | METH_KEYWORDS,
976 bpy_orphans_purge_doc,
981# pragma clang diagnostic pop
983# pragma GCC diagnostic pop
void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id)
@ BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES
@ BKE_BPATH_FOREACH_PATH_SKIP_LINKED
@ BKE_BPATH_FOREACH_PATH_SKIP_PACKED
@ BKE_BPATH_FOREACH_PATH_SKIP_MULTIFILE
@ BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN
@ BKE_BPATH_FOREACH_PATH_RELOAD_EDITED
void size_t BKE_id_multi_tagged_delete(Main *bmain) ATTR_NONNULL()
size_t BKE_id_multi_delete(Main *bmain, blender::Set< ID * > &ids_to_delete)
void BKE_lib_query_unused_ids_tag(Main *bmain, int tag, LibQueryUnusedIDsData ¶meters)
LibraryForeachIDCallbackFlag
@ IDWALK_CB_EMBEDDED_NOT_OWNING
void BKE_library_foreach_ID_link(Main *bmain, ID *id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, LibraryForeachIDFlag flag)
#define FOREACH_MAIN_LISTBASE_ID_END
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
#define FOREACH_MAIN_LISTBASE_END
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb)
#define BLI_BITMAP_TEST_BOOL(_bitmap, _index)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
ID and Library types, which are fundamental for SDNA.
#define ID_IS_LINKED(_id)
Read Guarded memory(de)allocation.
BMesh const char void * data
PyObject * pyrna_struct_CreatePyObject(PointerRNA *ptr)
#define PYRNA_STRUCT_CHECK_OBJ(obj)
#define BPy_StructRNA_Check(v)
static bool id_check_type(const ID *id, const BLI_bitmap *types_bitmap)
static PyObject * bpy_orphans_purge(PyObject *self, PyObject *args, PyObject *kwds)
static int id_code_as_index(const short idcode)
static void foreach_id_file_path_map(BPathForeachPathData &bpath_data)
const EnumPropertyItem rna_enum_file_path_foreach_flag_items[]
static bool foreach_id_file_path_foreach_callback(BPathForeachPathData *bpath_data, char *path_dst, const size_t path_dst_maxncpy, const char *path_src)
PyDoc_STRVAR(bpy_user_map_doc, ".. method:: user_map(*, subset=None, key_types=None, value_types=None)\n" "\n" " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all " "data-blocks using them.\n" "\n" " For list of valid set members for key_types & value_types, see: " ":class:`bpy.types.KeyingSetPath.id_type`.\n" "\n" " :arg subset: When passed, only these data-blocks and their users will be " "included as keys/values in the map.\n" " :type subset: Sequence[:class:`bpy.types.ID`]\n" " :arg key_types: Filter the keys mapped by ID types.\n" " :type key_types: set[str]\n" " :arg value_types: Filter the values in the set by ID types.\n" " :type value_types: set[str]\n" " :return: dictionary that maps data-blocks ID's to their users.\n" " :rtype: dict[:class:`bpy.types.ID`, set[:class:`bpy.types.ID`]]\n")
PyMethodDef BPY_rna_id_collection_file_path_foreach_method_def
static bool foreach_id_file_path_map_callback(BPathForeachPathData *bpath_data, char *, size_t, const char *path_src)
static PyObject * bpy_file_path_map(PyObject *self, PyObject *args, PyObject *kwds)
PyMethodDef BPY_rna_id_collection_batch_remove_method_def
static int foreach_libblock_id_user_map_callback(LibraryIDLinkCallbackData *cb_data)
static Main * pyrna_bmain_FromPyObject(PyObject *obj)
static PyObject * bpy_file_path_foreach(PyObject *self, PyObject *args, PyObject *kwds)
static PyObject * bpy_user_map(PyObject *self, PyObject *args, PyObject *kwds)
PyMethodDef BPY_rna_id_collection_orphans_purge_method_def
PyMethodDef BPY_rna_id_collection_file_path_map_method_def
static PyObject * bpy_batch_remove(PyObject *self, PyObject *args, PyObject *kwds)
PyMethodDef BPY_rna_id_collection_user_map_method_def
PyObject * pyrna_id_CreatePyObject(ID *id)
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
void MEM_freeN(void *vmemh)
static void error(const char *str)
int pyrna_enum_bitfield_from_set(const EnumPropertyItem *items, PyObject *value, int *r_value, const char *error_prefix)
BLI_bitmap * pyrna_enum_bitmap_from_set(const EnumPropertyItem *items, PyObject *value, int type_size, bool type_convert_sign, int bitmap_size, const char *error_prefix)
const char * PyC_UnicodeAsBytesAndSize(PyObject *py_str, Py_ssize_t *r_size, PyObject **r_coerce)
PyObject * PyC_UnicodeFromBytes(const char *str)
int PyC_ParseBool(PyObject *o, void *p)
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
#define PyTuple_SET_ITEMS(op_arg,...)
const EnumPropertyItem rna_enum_id_type_items[]
PointerRNA RNA_id_pointer_create(ID *id)
BPathForeachPathFunctionCallback callback_function
PyObject_HEAD std::optional< PointerRNA > ptr
PyObject * id_file_path_set
BLI_bitmap * types_bitmap
std::array< int, INDEX_ID_MAX > num_total
LibraryForeachIDCallbackFlag cb_flag
void WM_main_add_notifier(uint type, void *reference)