Blender V4.5
bpy_rna_id_collection.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <Python.h>
12#include <cstddef>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_bitmap.h"
17
18#include "BKE_bpath.hh"
19#include "BKE_global.hh"
20#include "BKE_lib_id.hh"
21#include "BKE_lib_query.hh"
22#include "BKE_main.hh"
23
24#include "DNA_ID.h"
25/* Those following are only to support hack of not listing some internal
26 * 'backward' pointers in generated user_map. */
27
28#include "WM_api.hh"
29#include "WM_types.hh"
30
32
35#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
36
37#include "RNA_enum_types.hh"
38#include "RNA_prototypes.hh"
39
40#include "bpy_rna.hh"
41
42static Main *pyrna_bmain_FromPyObject(PyObject *obj)
43{
44 if (!BPy_StructRNA_Check(obj)) {
45 PyErr_Format(PyExc_TypeError,
46 "Expected a StructRNA of type BlendData, not %.200s",
47 Py_TYPE(obj)->tp_name);
48 return nullptr;
49 }
50 BPy_StructRNA *pyrna = reinterpret_cast<BPy_StructRNA *>(obj);
52 if (!(pyrna->ptr && pyrna->ptr->type == &RNA_BlendData && pyrna->ptr->data)) {
53 PyErr_Format(PyExc_TypeError,
54 "Expected a StructRNA of type BlendData, not %.200s",
55 Py_TYPE(pyrna)->tp_name);
56 return nullptr;
57 }
58 return static_cast<Main *>(pyrna->ptr->data);
59}
60
63 PyObject *py_id_curr;
65
68
70 PyObject *user_map;
73};
74
75static int id_code_as_index(const short idcode)
76{
77 return int(*((ushort *)&idcode));
78}
79
80static bool id_check_type(const ID *id, const BLI_bitmap *types_bitmap)
81{
82 return BLI_BITMAP_TEST_BOOL(types_bitmap, id_code_as_index(GS(id->name)));
83}
84
86{
87 ID **id_p = cb_data->id_pointer;
88
89 if (*id_p) {
90 IDUserMapData *data = static_cast<IDUserMapData *>(cb_data->user_data);
91 const LibraryForeachIDCallbackFlag cb_flag = cb_data->cb_flag;
92
93 if (data->types_bitmap) {
94 if (!id_check_type(*id_p, data->types_bitmap)) {
95 return IDWALK_RET_NOP;
96 }
97 }
98
99 if (cb_flag & IDWALK_CB_LOOPBACK) {
100 /* We skip loop-back pointers like Key.from here,
101 * since it's some internal pointer which is not relevant info for py/API level. */
102 return IDWALK_RET_NOP;
103 }
104
106 /* We skip private pointers themselves, like root node trees, we'll 'link' their own ID
107 * pointers to their 'ID owner' instead. */
108 return IDWALK_RET_NOP;
109 }
110
111 PyObject *key = pyrna_id_CreatePyObject(*id_p);
112
113 PyObject *set;
114 if ((set = PyDict_GetItem(data->user_map, key)) == nullptr) {
115 /* limit to key's added already */
116 if (data->is_subset) {
117 return IDWALK_RET_NOP;
118 }
119
120 set = PySet_New(nullptr);
121 PyDict_SetItem(data->user_map, key, set);
122 Py_DECREF(set);
123 }
124 Py_DECREF(key);
125
126 if (data->py_id_curr == nullptr) {
127 data->py_id_curr = pyrna_id_CreatePyObject(data->id_curr);
128 }
129
130 PySet_Add(set, data->py_id_curr);
131 }
132
133 return IDWALK_RET_NOP;
134}
135
137 /* Wrap. */
138 bpy_user_map_doc,
139 /* NOTE: These documented default values (None) are here just to signal that these parameters
140 * are optional. Explicitly passing None is not valid, and will raise a TypeError. */
141 ".. method:: user_map(*, subset=None, key_types=None, value_types=None)\n"
142 "\n"
143 " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all "
144 "data-blocks using them.\n"
145 "\n"
146 " For list of valid set members for key_types & value_types, see: "
147 ":class:`bpy.types.KeyingSetPath.id_type`.\n"
148 "\n"
149 " :arg subset: When passed, only these data-blocks and their users will be "
150 "included as keys/values in the map.\n"
151 " :type subset: Sequence[:class:`bpy.types.ID`]\n"
152 " :arg key_types: Filter the keys mapped by ID types.\n"
153 " :type key_types: set[str]\n"
154 " :arg value_types: Filter the values in the set by ID types.\n"
155 " :type value_types: set[str]\n"
156 " :return: dictionary that maps data-blocks ID's to their users.\n"
157 " :rtype: dict[:class:`bpy.types.ID`, set[:class:`bpy.types.ID`]]\n");
158static PyObject *bpy_user_map(PyObject *self, PyObject *args, PyObject *kwds)
159{
161 if (!bmain) {
162 return nullptr;
163 }
164
165 ListBase *lb;
166 ID *id;
167
168 PyObject *subset = nullptr;
169
170 PyObject *key_types = nullptr;
171 PyObject *val_types = nullptr;
172 BLI_bitmap *key_types_bitmap = nullptr;
173 BLI_bitmap *val_types_bitmap = nullptr;
174
175 PyObject *ret = nullptr;
176
177 IDUserMapData data_cb = {nullptr};
178
179 static const char *_keywords[] = {"subset", "key_types", "value_types", nullptr};
180 static _PyArg_Parser _parser = {
182 "|$" /* Optional keyword only arguments. */
183 "O" /* `subset` */
184 "O!" /* `key_types` */
185 "O!" /* `value_types` */
186 ":user_map",
187 _keywords,
188 nullptr,
189 };
190 if (!_PyArg_ParseTupleAndKeywordsFast(
191 args, kwds, &_parser, &subset, &PySet_Type, &key_types, &PySet_Type, &val_types))
192 {
193 return nullptr;
194 }
195
196 if (key_types) {
197 key_types_bitmap = pyrna_enum_bitmap_from_set(
198 rna_enum_id_type_items, key_types, sizeof(short), true, USHRT_MAX, "key types");
199 if (key_types_bitmap == nullptr) {
200 goto error;
201 }
202 }
203
204 if (val_types) {
205 val_types_bitmap = pyrna_enum_bitmap_from_set(
206 rna_enum_id_type_items, val_types, sizeof(short), true, USHRT_MAX, "value types");
207 if (val_types_bitmap == nullptr) {
208 goto error;
209 }
210 }
211
212 if (subset) {
213 PyObject *subset_fast = PySequence_Fast(subset, "user_map");
214 if (subset_fast == nullptr) {
215 goto error;
216 }
217
218 PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast);
219 Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast);
220
221 data_cb.user_map = _PyDict_NewPresized(subset_len);
222 data_cb.is_subset = true;
223 for (; subset_len; subset_array++, subset_len--) {
224 ID *id;
225 if (!pyrna_id_FromPyObject(*subset_array, &id)) {
226 PyErr_Format(PyExc_TypeError,
227 "Expected an ID type in `subset` iterable, not %.200s",
228 Py_TYPE(*subset_array)->tp_name);
229 Py_DECREF(subset_fast);
230 Py_DECREF(data_cb.user_map);
231 goto error;
232 }
233
234 if (!PyDict_Contains(data_cb.user_map, *subset_array)) {
235 PyObject *set = PySet_New(nullptr);
236 PyDict_SetItem(data_cb.user_map, *subset_array, set);
237 Py_DECREF(set);
238 }
239 }
240 Py_DECREF(subset_fast);
241 }
242 else {
243 data_cb.user_map = PyDict_New();
244 }
245
246 data_cb.types_bitmap = key_types_bitmap;
247
248 FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
250 /* We cannot skip here in case we have some filter on key types... */
251 if (key_types_bitmap == nullptr && val_types_bitmap != nullptr) {
252 if (!id_check_type(id, val_types_bitmap)) {
253 break;
254 }
255 }
256
257 if (!data_cb.is_subset &&
258 /* We do not want to pre-add keys of filtered out types. */
259 (key_types_bitmap == nullptr || id_check_type(id, key_types_bitmap)) &&
260 /* We do not want to pre-add keys when we have filter on value types,
261 * but not on key types. */
262 (val_types_bitmap == nullptr || key_types_bitmap != nullptr))
263 {
264 PyObject *key = pyrna_id_CreatePyObject(id);
265 PyObject *set;
266
267 /* We have to insert the key now,
268 * otherwise ID unused would be missing from final dict... */
269 if ((set = PyDict_GetItem(data_cb.user_map, key)) == nullptr) {
270 set = PySet_New(nullptr);
271 PyDict_SetItem(data_cb.user_map, key, set);
272 Py_DECREF(set);
273 }
274 Py_DECREF(key);
275 }
276
277 if (val_types_bitmap != nullptr && !id_check_type(id, val_types_bitmap)) {
278 continue;
279 }
280
281 data_cb.id_curr = id;
283 nullptr, id, foreach_libblock_id_user_map_callback, &data_cb, IDWALK_NOP);
284
285 if (data_cb.py_id_curr) {
286 Py_DECREF(data_cb.py_id_curr);
287 data_cb.py_id_curr = nullptr;
288 }
289 }
291 }
293
294 ret = data_cb.user_map;
295
296error:
297 if (key_types_bitmap != nullptr) {
298 MEM_freeN(key_types_bitmap);
299 }
300 if (val_types_bitmap != nullptr) {
301 MEM_freeN(val_types_bitmap);
302 }
303
304 return ret;
305}
306
308 /* Data unchanged for the whole process. */
309
311 PyObject *file_path_map;
312
315
316 /* Data modified for each processed ID. */
317
322};
323
325 char * /*path_dst*/,
326 size_t /*path_dst_maxncpy*/,
327 const char *path_src)
328{
329 IDFilePathMapData &data = *static_cast<IDFilePathMapData *>(bpath_data->user_data);
330 PyObject *id_file_path_set = data.id_file_path_set;
331
332 BLI_assert(data.id == bpath_data->owner_id);
333
334 if (path_src && *path_src) {
335 PyObject *path = PyC_UnicodeFromBytes(path_src);
336 PySet_Add(id_file_path_set, path);
337 Py_DECREF(path);
338 }
339 return false;
340}
341
343{
344 IDFilePathMapData &data = *static_cast<IDFilePathMapData *>(bpath_data.user_data);
345 ID *id = data.id;
346 PyObject *id_file_path_set = data.id_file_path_set;
347
348 if (data.include_libraries && ID_IS_LINKED(id)) {
349 PyObject *path = PyC_UnicodeFromBytes(id->lib->filepath);
350 PySet_Add(id_file_path_set, path);
351 Py_DECREF(path);
352 }
353
354 BKE_bpath_foreach_path_id(&bpath_data, id);
355}
356
358 /* Wrap. */
359 bpy_file_path_map_doc,
360 ".. method:: file_path_map(subset=None, key_types=None, include_libraries=False)\n"
361 "\n"
362 " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all "
363 "file paths used by them.\n"
364 "\n"
365 " For list of valid set members for key_types, see: "
366 ":class:`bpy.types.KeyingSetPath.id_type`.\n"
367 "\n"
368 " :arg subset: When given, only these data-blocks and their used file paths "
369 "will be included as keys/values in the map.\n"
370 " :type subset: sequence\n"
371 " :arg key_types: When given, filter the keys mapped by ID types. Ignored if ``subset`` is "
372 "also given.\n"
373 " :type key_types: set of strings\n"
374 " :arg include_libraries: Include library file paths of linked data. False by default.\n"
375 " :type include_libraries: bool\n"
376 " :return: dictionary of :class:`bpy.types.ID` instances, with sets of file path "
377 "strings as their values.\n"
378 " :rtype: dict\n");
379static PyObject *bpy_file_path_map(PyObject *self, PyObject *args, PyObject *kwds)
380{
382 if (!bmain) {
383 return nullptr;
384 }
385
386 PyObject *subset = nullptr;
387
388 PyObject *key_types = nullptr;
389 PyObject *include_libraries = nullptr;
390 BLI_bitmap *key_types_bitmap = nullptr;
391
392 PyObject *ret = nullptr;
393
394 IDFilePathMapData filepathmap_data{};
395 BPathForeachPathData bpath_data{};
396
397 static const char *_keywords[] = {"subset", "key_types", "include_libraries", nullptr};
398 static _PyArg_Parser _parser = {
400 "|$" /* Optional keyword only arguments. */
401 "O" /* `subset` */
402 "O!" /* `key_types` */
403 "O!" /* `include_libraries` */
404 ":file_path_map",
405 _keywords,
406 nullptr,
407 };
408 if (!_PyArg_ParseTupleAndKeywordsFast(args,
409 kwds,
410 &_parser,
411 &subset,
412 &PySet_Type,
413 &key_types,
414 &PyBool_Type,
415 &include_libraries))
416 {
417 return nullptr;
418 }
419
420 if (key_types) {
421 key_types_bitmap = pyrna_enum_bitmap_from_set(
422 rna_enum_id_type_items, key_types, sizeof(short), true, USHRT_MAX, "key types");
423 if (key_types_bitmap == nullptr) {
424 goto error;
425 }
426 }
427
428 bpath_data.bmain = bmain;
430 /* TODO: needs to be controllable from caller (add more options to the API). */
432 bpath_data.user_data = &filepathmap_data;
433
434 filepathmap_data.include_libraries = (include_libraries == Py_True);
435
436 if (subset) {
437 PyObject *subset_fast = PySequence_Fast(subset, "user_map");
438 if (subset_fast == nullptr) {
439 goto error;
440 }
441
442 PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast);
443 Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast);
444
445 filepathmap_data.file_path_map = _PyDict_NewPresized(subset_len);
446 for (; subset_len; subset_array++, subset_len--) {
447 if (PyDict_Contains(filepathmap_data.file_path_map, *subset_array)) {
448 continue;
449 }
450
451 ID *id;
452 if (!pyrna_id_FromPyObject(*subset_array, &id)) {
453 PyErr_Format(PyExc_TypeError,
454 "Expected an ID type in `subset` iterable, not %.200s",
455 Py_TYPE(*subset_array)->tp_name);
456 Py_DECREF(subset_fast);
457 Py_DECREF(filepathmap_data.file_path_map);
458 goto error;
459 }
460
461 filepathmap_data.id_file_path_set = PySet_New(nullptr);
462 PyDict_SetItem(
463 filepathmap_data.file_path_map, *subset_array, filepathmap_data.id_file_path_set);
464 Py_DECREF(filepathmap_data.id_file_path_set);
465
466 filepathmap_data.id = id;
467 foreach_id_file_path_map(bpath_data);
468 }
469 Py_DECREF(subset_fast);
470 }
471 else {
472 ListBase *lb;
473 ID *id;
474 filepathmap_data.file_path_map = PyDict_New();
475
476 FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
478 /* We can skip here in case we have some filter on key types. */
479 if (key_types_bitmap && !id_check_type(id, key_types_bitmap)) {
480 break;
481 }
482
483 PyObject *key = pyrna_id_CreatePyObject(id);
484 filepathmap_data.id_file_path_set = PySet_New(nullptr);
485 PyDict_SetItem(filepathmap_data.file_path_map, key, filepathmap_data.id_file_path_set);
486 Py_DECREF(filepathmap_data.id_file_path_set);
487 Py_DECREF(key);
488
489 filepathmap_data.id = id;
490 foreach_id_file_path_map(bpath_data);
491 }
493 }
495 }
496
497 ret = filepathmap_data.file_path_map;
498
499error:
500 if (key_types_bitmap != nullptr) {
501 MEM_freeN(key_types_bitmap);
502 }
503
504 return ret;
505}
506
508 /* Wrap. */
509 bpy_batch_remove_doc,
510 ".. method:: batch_remove(ids)\n"
511 "\n"
512 " Remove (delete) several IDs at once.\n"
513 "\n"
514 " Note that this function is quicker than individual calls to :func:`remove()` "
515 "(from :class:`bpy.types.BlendData`\n"
516 " ID collections), but less safe/versatile (it can break Blender, e.g. by removing "
517 "all scenes...).\n"
518 "\n"
519 " :arg ids: Sequence of IDs (types can be mixed).\n"
520 " :type ids: Sequence[:class:`bpy.types.ID`]\n");
521static PyObject *bpy_batch_remove(PyObject *self, PyObject *args, PyObject *kwds)
522{
524 if (!bmain) {
525 return nullptr;
526 }
527
528 PyObject *ids = nullptr;
529
530 static const char *_keywords[] = {"ids", nullptr};
531 static _PyArg_Parser _parser = {
533 "O" /* `ids` */
534 ":batch_remove",
535 _keywords,
536 nullptr,
537 };
538 if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &ids)) {
539 return nullptr;
540 }
541
542 if (!ids) {
543 return nullptr;
544 }
545
546 PyObject *ids_fast = PySequence_Fast(ids, "batch_remove");
547 if (ids_fast == nullptr) {
548 return nullptr;
549 }
550
551 PyObject **ids_array = PySequence_Fast_ITEMS(ids_fast);
552 Py_ssize_t ids_len = PySequence_Fast_GET_SIZE(ids_fast);
553 blender::Set<ID *> ids_to_delete;
554 for (; ids_len; ids_array++, ids_len--) {
555 ID *id;
556 if (!pyrna_id_FromPyObject(*ids_array, &id)) {
557 PyErr_Format(
558 PyExc_TypeError, "Expected an ID type, not %.200s", Py_TYPE(*ids_array)->tp_name);
559 Py_DECREF(ids_fast);
560 return nullptr;
561 }
562
563 ids_to_delete.add(id);
564 }
565 Py_DECREF(ids_fast);
566
567 BKE_id_multi_delete(bmain, ids_to_delete);
568 /* Force full redraw, mandatory to avoid crashes when running this from UI... */
570
571 Py_RETURN_NONE;
572}
573
575 /* Wrap. */
576 bpy_orphans_purge_doc,
577 ".. method:: orphans_purge()\n"
578 "\n"
579 " Remove (delete) all IDs with no user.\n"
580 "\n"
581 " :arg do_local_ids: Include unused local IDs in the deletion, defaults to True\n"
582 " :type do_local_ids: bool, optional\n"
583 " :arg do_linked_ids: Include unused linked IDs in the deletion, defaults to True\n"
584 " :type do_linked_ids: bool, optional\n"
585 " :arg do_recursive: Recursively check for unused IDs, ensuring no orphaned one "
586 "remain after a single run of that function, defaults to False\n"
587 " :type do_recursive: bool, optional\n"
588 " :return: The number of deleted IDs.\n");
589static PyObject *bpy_orphans_purge(PyObject *self, PyObject *args, PyObject *kwds)
590{
592 if (!bmain) {
593 return nullptr;
594 }
595
596 LibQueryUnusedIDsData unused_ids_data;
597 unused_ids_data.do_local_ids = true;
598 unused_ids_data.do_linked_ids = true;
599 unused_ids_data.do_recursive = false;
600
601 static const char *_keywords[] = {"do_local_ids", "do_linked_ids", "do_recursive", nullptr};
602 static _PyArg_Parser _parser = {
604 "|" /* Optional arguments. */
605 "O&" /* `do_local_ids` */
606 "O&" /* `do_linked_ids` */
607 "O&" /* `do_recursive` */
608 ":orphans_purge",
609 _keywords,
610 nullptr,
611 };
612 if (!_PyArg_ParseTupleAndKeywordsFast(args,
613 kwds,
614 &_parser,
616 &unused_ids_data.do_local_ids,
618 &unused_ids_data.do_linked_ids,
620 &unused_ids_data.do_recursive))
621 {
622 return nullptr;
623 }
624
625 /* Tag all IDs to delete. */
626 BKE_lib_query_unused_ids_tag(bmain, ID_TAG_DOIT, unused_ids_data);
627
628 if (unused_ids_data.num_total[INDEX_ID_NULL] == 0) {
629 return PyLong_FromSize_t(0);
630 }
631
632 const size_t num_datablocks_deleted = BKE_id_multi_tagged_delete(bmain);
633 /* Force full redraw, mandatory to avoid crashes when running this from UI... */
635
636 return PyLong_FromSize_t(num_datablocks_deleted);
637}
638
639#ifdef __GNUC__
640# ifdef __clang__
641# pragma clang diagnostic push
642# pragma clang diagnostic ignored "-Wcast-function-type"
643# else
644# pragma GCC diagnostic push
645# pragma GCC diagnostic ignored "-Wcast-function-type"
646# endif
647#endif
648
650 "user_map",
651 (PyCFunction)bpy_user_map,
652 METH_VARARGS | METH_KEYWORDS,
653 bpy_user_map_doc,
654};
656 "file_path_map",
657 (PyCFunction)bpy_file_path_map,
658 METH_VARARGS | METH_KEYWORDS,
659 bpy_file_path_map_doc,
660};
662 "batch_remove",
663 (PyCFunction)bpy_batch_remove,
664 METH_VARARGS | METH_KEYWORDS,
665 bpy_batch_remove_doc,
666};
668 "orphans_purge",
669 (PyCFunction)bpy_orphans_purge,
670 METH_VARARGS | METH_KEYWORDS,
671 bpy_orphans_purge_doc,
672};
673
674#ifdef __GNUC__
675# ifdef __clang__
676# pragma clang diagnostic pop
677# else
678# pragma GCC diagnostic pop
679# endif
680#endif
void BKE_bpath_foreach_path_id(BPathForeachPathData *bpath_data, ID *id)
Definition bpath.cc:75
@ BKE_BPATH_TRAVERSE_SKIP_WEAK_REFERENCES
Definition BKE_bpath.hh:47
@ BKE_BPATH_FOREACH_PATH_SKIP_PACKED
Definition BKE_bpath.hh:37
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)
LibraryForeachIDCallbackFlag
@ IDWALK_CB_LOOPBACK
@ IDWALK_CB_EMBEDDED_NOT_OWNING
@ IDWALK_CB_EMBEDDED
void BKE_library_foreach_ID_link(Main *bmain, ID *id, blender::FunctionRef< LibraryIDLinkCallback > callback, void *user_data, LibraryForeachIDFlag flag)
Definition lib_query.cc:431
@ IDWALK_RET_NOP
@ IDWALK_NOP
#define FOREACH_MAIN_LISTBASE_ID_END
Definition BKE_main.hh:532
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
Definition BKE_main.hh:526
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb)
Definition BKE_main.hh:537
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_BITMAP_TEST_BOOL(_bitmap, _index)
Definition BLI_bitmap.h:71
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
unsigned short ushort
ID and Library types, which are fundamental for SDNA.
@ INDEX_ID_NULL
Definition DNA_ID.h:1266
@ ID_TAG_DOIT
Definition DNA_ID.h:944
Read Guarded memory(de)allocation.
#define NC_WINDOW
Definition WM_types.hh:372
BMesh const char void * data
PyObject * self
#define PYRNA_STRUCT_CHECK_OBJ(obj)
Definition bpy_rna.hh:78
#define BPy_StructRNA_Check(v)
Definition bpy_rna.hh:73
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)
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")
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_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
bool add(const Key &key)
Definition BLI_set.hh:248
#define ID_IS_LINKED(_id)
#define GS(a)
PyObject * pyrna_id_CreatePyObject(ID *id)
Definition bpy_rna.cc:8489
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
Definition bpy_rna.cc:8499
void BKE_lib_query_unused_ids_tag(Main *bmain, const int tag, LibQueryUnusedIDsData &parameters)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static void error(const char *str)
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)
PyObject * PyC_UnicodeFromBytes(const char *str)
int PyC_ParseBool(PyObject *o, void *p)
header-only compatibility defines.
#define PY_ARG_PARSER_HEAD_COMPAT()
return ret
const EnumPropertyItem rna_enum_id_type_items[]
Definition rna_ID.cc:29
eBPathForeachFlag flag
Definition BKE_bpath.hh:94
BPathForeachPathFunctionCallback callback_function
Definition BKE_bpath.hh:93
PyObject_HEAD std::optional< PointerRNA > ptr
Definition bpy_rna.hh:130
BLI_bitmap * types_bitmap
Definition DNA_ID.h:404
struct Library * lib
Definition DNA_ID.h:410
char name[66]
Definition DNA_ID.h:415
std::array< int, INDEX_ID_MAX > num_total
LibraryForeachIDCallbackFlag cb_flag
char filepath[1024]
Definition DNA_ID.h:507
void WM_main_add_notifier(uint type, void *reference)