Blender  V2.93
bpy_rna_id_collection.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
23 #include <Python.h>
24 #include <stddef.h>
25 
26 #include "MEM_guardedalloc.h"
27 
28 #include "BLI_bitmap.h"
29 #include "BLI_utildefines.h"
30 
31 #include "BKE_global.h"
32 #include "BKE_lib_id.h"
33 #include "BKE_lib_query.h"
34 #include "BKE_main.h"
35 
36 #include "DNA_ID.h"
37 /* Those following are only to support hack of not listing some internal
38  * 'backward' pointers in generated user_map. */
39 #include "DNA_key_types.h"
40 #include "DNA_object_types.h"
41 
42 #include "WM_api.h"
43 #include "WM_types.h"
44 
45 #include "bpy_capi_utils.h"
46 #include "bpy_rna_id_collection.h"
47 
48 #include "../generic/py_capi_utils.h"
49 #include "../generic/python_utildefines.h"
50 
51 #include "RNA_access.h"
52 #include "RNA_enum_types.h"
53 #include "RNA_types.h"
54 
55 #include "bpy_rna.h"
56 
57 typedef struct IDUserMapData {
59  PyObject *py_id_curr;
61 
64 
66  PyObject *user_map;
68  bool is_subset;
70 
71 static int id_code_as_index(const short idcode)
72 {
73  return (int)*((ushort *)&idcode);
74 }
75 
76 static bool id_check_type(const ID *id, const BLI_bitmap *types_bitmap)
77 {
78  return BLI_BITMAP_TEST_BOOL(types_bitmap, id_code_as_index(GS(id->name)));
79 }
80 
82 {
83  ID **id_p = cb_data->id_pointer;
84 
85  if (*id_p) {
86  IDUserMapData *data = cb_data->user_data;
87  const int cb_flag = cb_data->cb_flag;
88 
89  if (data->types_bitmap) {
90  if (!id_check_type(*id_p, data->types_bitmap)) {
91  return IDWALK_RET_NOP;
92  }
93  }
94 
95  if (cb_flag & IDWALK_CB_LOOPBACK) {
96  /* We skip loop-back pointers like Object.proxy_from or Key.from here,
97  * since it's some internal pointer which is not relevant info for py/API level. */
98  return IDWALK_RET_NOP;
99  }
100 
101  if (cb_flag & IDWALK_CB_EMBEDDED) {
102  /* We skip private pointers themselves, like root node trees, we'll 'link' their own ID
103  * pointers to their 'ID owner' instead. */
104  return IDWALK_RET_NOP;
105  }
106 
107  PyObject *key = pyrna_id_CreatePyObject(*id_p);
108 
109  PyObject *set;
110  if ((set = PyDict_GetItem(data->user_map, key)) == NULL) {
111  /* limit to key's added already */
112  if (data->is_subset) {
113  return IDWALK_RET_NOP;
114  }
115 
116  set = PySet_New(NULL);
117  PyDict_SetItem(data->user_map, key, set);
118  Py_DECREF(set);
119  }
120  Py_DECREF(key);
121 
122  if (data->py_id_curr == NULL) {
123  data->py_id_curr = pyrna_id_CreatePyObject(data->id_curr);
124  }
125 
126  PySet_Add(set, data->py_id_curr);
127  }
128 
129  return IDWALK_RET_NOP;
130 }
131 
132 PyDoc_STRVAR(bpy_user_map_doc,
133  ".. method:: user_map([subset=(id1, id2, ...)], key_types={..}, value_types={..})\n"
134  "\n"
135  " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all "
136  "datablocks using them.\n"
137  "\n"
138  " For list of valid set members for key_types & value_types, see: "
139  ":class:`bpy.types.KeyingSetPath.id_type`.\n"
140  "\n"
141  " :arg subset: When passed, only these data-blocks and their users will be "
142  "included as keys/values in the map.\n"
143  " :type subset: sequence\n"
144  " :arg key_types: Filter the keys mapped by ID types.\n"
145  " :type key_types: set of strings\n"
146  " :arg value_types: Filter the values in the set by ID types.\n"
147  " :type value_types: set of strings\n"
148  " :return: dictionary of :class:`bpy.types.ID` instances, with sets of ID's as "
149  "their values.\n"
150  " :rtype: dict\n");
151 static PyObject *bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
152 {
153 #if 0 /* If someone knows how to get a proper 'self' in that case... */
154  BPy_StructRNA *pyrna = (BPy_StructRNA *)self;
155  Main *bmain = pyrna->ptr.data;
156 #else
157  Main *bmain = G_MAIN; /* XXX Ugly, but should work! */
158 #endif
159  ListBase *lb;
160  ID *id;
161 
162  PyObject *subset = NULL;
163 
164  PyObject *key_types = NULL;
165  PyObject *val_types = NULL;
166  BLI_bitmap *key_types_bitmap = NULL;
167  BLI_bitmap *val_types_bitmap = NULL;
168 
169  PyObject *ret = NULL;
170 
171  IDUserMapData data_cb = {NULL};
172 
173  static const char *_keywords[] = {"subset", "key_types", "value_types", NULL};
174  static _PyArg_Parser _parser = {"|O$O!O!:user_map", _keywords, 0};
175  if (!_PyArg_ParseTupleAndKeywordsFast(
176  args, kwds, &_parser, &subset, &PySet_Type, &key_types, &PySet_Type, &val_types)) {
177  return NULL;
178  }
179 
180  if (key_types) {
181  key_types_bitmap = pyrna_set_to_enum_bitmap(
182  rna_enum_id_type_items, key_types, sizeof(short), true, USHRT_MAX, "key types");
183  if (key_types_bitmap == NULL) {
184  goto error;
185  }
186  }
187 
188  if (val_types) {
189  val_types_bitmap = pyrna_set_to_enum_bitmap(
190  rna_enum_id_type_items, val_types, sizeof(short), true, USHRT_MAX, "value types");
191  if (val_types_bitmap == NULL) {
192  goto error;
193  }
194  }
195 
196  if (subset) {
197  PyObject *subset_fast = PySequence_Fast(subset, "user_map");
198  if (subset_fast == NULL) {
199  goto error;
200  }
201 
202  PyObject **subset_array = PySequence_Fast_ITEMS(subset_fast);
203  Py_ssize_t subset_len = PySequence_Fast_GET_SIZE(subset_fast);
204 
205  data_cb.user_map = _PyDict_NewPresized(subset_len);
206  data_cb.is_subset = true;
207  for (; subset_len; subset_array++, subset_len--) {
208  PyObject *set = PySet_New(NULL);
209  PyDict_SetItem(data_cb.user_map, *subset_array, set);
210  Py_DECREF(set);
211  }
212  Py_DECREF(subset_fast);
213  }
214  else {
215  data_cb.user_map = PyDict_New();
216  }
217 
218  data_cb.types_bitmap = key_types_bitmap;
219 
220  FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
222  /* We cannot skip here in case we have some filter on key types... */
223  if (key_types_bitmap == NULL && val_types_bitmap != NULL) {
224  if (!id_check_type(id, val_types_bitmap)) {
225  break;
226  }
227  }
228 
229  if (!data_cb.is_subset &&
230  /* We do not want to pre-add keys of flitered out types. */
231  (key_types_bitmap == NULL || id_check_type(id, key_types_bitmap)) &&
232  /* We do not want to pre-add keys when we have filter on value types,
233  * but not on key types. */
234  (val_types_bitmap == NULL || key_types_bitmap != NULL)) {
235  PyObject *key = pyrna_id_CreatePyObject(id);
236  PyObject *set;
237 
238  /* We have to insert the key now,
239  * otherwise ID unused would be missing from final dict... */
240  if ((set = PyDict_GetItem(data_cb.user_map, key)) == NULL) {
241  set = PySet_New(NULL);
242  PyDict_SetItem(data_cb.user_map, key, set);
243  Py_DECREF(set);
244  }
245  Py_DECREF(key);
246  }
247 
248  if (val_types_bitmap != NULL && !id_check_type(id, val_types_bitmap)) {
249  continue;
250  }
251 
252  data_cb.id_curr = id;
255 
256  if (data_cb.py_id_curr) {
257  Py_DECREF(data_cb.py_id_curr);
258  data_cb.py_id_curr = NULL;
259  }
260  }
262  }
264 
265  ret = data_cb.user_map;
266 
267 error:
268  if (key_types_bitmap != NULL) {
269  MEM_freeN(key_types_bitmap);
270  }
271 
272  if (val_types_bitmap != NULL) {
273  MEM_freeN(val_types_bitmap);
274  }
275 
276  return ret;
277 }
278 
279 PyDoc_STRVAR(bpy_batch_remove_doc,
280  ".. method:: batch_remove(ids=(id1, id2, ...))\n"
281  "\n"
282  " Remove (delete) several IDs at once.\n"
283  "\n"
284  " WARNING: Considered experimental feature currently.\n"
285  "\n"
286  " Note that this function is quicker than individual calls to :func:`remove()` "
287  "(from :class:`bpy.types.BlendData`\n"
288  " ID collections), but less safe/versatile (it can break Blender, e.g. by removing "
289  "all scenes...).\n"
290  "\n"
291  " :arg ids: Iterables of IDs (types can be mixed).\n"
292  " :type subset: sequence\n");
293 static PyObject *bpy_batch_remove(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
294 {
295 #if 0 /* If someone knows how to get a proper 'self' in that case... */
296  BPy_StructRNA *pyrna = (BPy_StructRNA *)self;
297  Main *bmain = pyrna->ptr.data;
298 #else
299  Main *bmain = G_MAIN; /* XXX Ugly, but should work! */
300 #endif
301 
302  PyObject *ids = NULL;
303 
304  PyObject *ret = NULL;
305 
306  static const char *_keywords[] = {"ids", NULL};
307  static _PyArg_Parser _parser = {"O:batch_remove", _keywords, 0};
308  if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &ids)) {
309  return ret;
310  }
311 
312  if (ids) {
313  BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
314 
315  PyObject *ids_fast = PySequence_Fast(ids, "batch_remove");
316  if (ids_fast == NULL) {
317  goto error;
318  }
319 
320  PyObject **ids_array = PySequence_Fast_ITEMS(ids_fast);
321  Py_ssize_t ids_len = PySequence_Fast_GET_SIZE(ids_fast);
322 
323  for (; ids_len; ids_array++, ids_len--) {
324  ID *id;
325  if (!pyrna_id_FromPyObject(*ids_array, &id)) {
326  PyErr_Format(
327  PyExc_TypeError, "Expected an ID type, not %.200s", Py_TYPE(*ids_array)->tp_name);
328  Py_DECREF(ids_fast);
329  goto error;
330  }
331 
332  id->tag |= LIB_TAG_DOIT;
333  }
334  Py_DECREF(ids_fast);
335 
337  /* Force full redraw, mandatory to avoid crashes when running this from UI... */
339  }
340  else {
341  goto error;
342  }
343 
344  Py_INCREF(Py_None);
345  ret = Py_None;
346 
347 error:
348  return ret;
349 }
350 
351 PyDoc_STRVAR(bpy_orphans_purge_doc,
352  ".. method:: orphans_purge()\n"
353  "\n"
354  " Remove (delete) all IDs with no user.\n"
355  "\n"
356  " :arg do_local_ids: Include unused local IDs in the deletion, defaults to True\n"
357  " :type do_local_ids: bool, optional\n"
358  " :arg do_linked_ids: Include unused linked IDs in the deletion, defaults to True\n"
359  " :type do_linked_ids: bool, optional\n"
360  " :arg do_recursive: Recursively check for unused IDs, ensuring no orphaned one "
361  "remain after a single run of that function, defaults to False\n"
362  " :type do_recursive: bool, optional\n"
363  " :return: The number of deleted IDs.\n");
364 static PyObject *bpy_orphans_purge(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
365 {
366 #if 0 /* If someone knows how to get a proper 'self' in that case... */
367  BPy_StructRNA *pyrna = (BPy_StructRNA *)self;
368  Main *bmain = pyrna->ptr.data;
369 #else
370  Main *bmain = G_MAIN; /* XXX Ugly, but should work! */
371 #endif
372 
373  int num_tagged[INDEX_ID_MAX] = {0};
374 
375  bool do_local_ids = true;
376  bool do_linked_ids = true;
377  bool do_recursive_cleanup = false;
378 
379  static const char *_keywords[] = {"do_local_ids", "do_linked_ids", "do_recursive", NULL};
380  static _PyArg_Parser _parser = {"|O&O&O&:orphans_purge", _keywords, 0};
381  if (!_PyArg_ParseTupleAndKeywordsFast(args,
382  kwds,
383  &_parser,
385  &do_local_ids,
387  &do_linked_ids,
389  &do_recursive_cleanup)) {
390  return NULL;
391  }
392 
393  /* Tag all IDs to delete. */
395  bmain, LIB_TAG_DOIT, do_local_ids, do_linked_ids, do_recursive_cleanup, num_tagged);
396 
397  if (num_tagged[INDEX_ID_NULL] == 0) {
398  return PyLong_FromSize_t(0);
399  }
400 
401  const size_t num_datablocks_deleted = BKE_id_multi_tagged_delete(bmain);
402  /* Force full redraw, mandatory to avoid crashes when running this from UI... */
404 
405  return PyLong_FromSize_t(num_datablocks_deleted);
406 }
407 
409  "user_map",
410  (PyCFunction)bpy_user_map,
411  METH_STATIC | METH_VARARGS | METH_KEYWORDS,
412  bpy_user_map_doc,
413 };
415  "batch_remove",
416  (PyCFunction)bpy_batch_remove,
417  METH_STATIC | METH_VARARGS | METH_KEYWORDS,
418  bpy_batch_remove_doc,
419 };
421  "orphans_purge",
422  (PyCFunction)bpy_orphans_purge,
423  METH_STATIC | METH_VARARGS | METH_KEYWORDS,
424  bpy_orphans_purge_doc,
425 };
#define G_MAIN
Definition: BKE_global.h:232
void BKE_main_id_tag_all(struct Main *mainvar, const int tag, const bool value)
Definition: lib_id.c:923
size_t BKE_id_multi_tagged_delete(struct Main *bmain) ATTR_NONNULL()
void BKE_library_foreach_ID_link(struct Main *bmain, struct ID *id, LibraryIDLinkCallback callback, void *user_data, int flag)
Definition: lib_query.c:322
@ IDWALK_RET_NOP
Definition: BKE_lib_query.h:97
void BKE_lib_query_unused_ids_tag(struct Main *bmain, const int tag, const bool do_local_ids, const bool do_linked_ids, const bool do_tag_recursive, int *r_num_tagged)
Definition: lib_query.c:726
@ IDWALK_CB_LOOPBACK
Definition: BKE_lib_query.h:68
@ IDWALK_CB_EMBEDDED
Definition: BKE_lib_query.h:62
@ IDWALK_CB_NOP
Definition: BKE_lib_query.h:47
#define FOREACH_MAIN_LISTBASE_ID_END
Definition: BKE_main.h:219
#define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id)
Definition: BKE_main.h:213
#define FOREACH_MAIN_LISTBASE_BEGIN(_bmain, _lb)
Definition: BKE_main.h:224
#define BLI_BITMAP_TEST_BOOL(_bitmap, _index)
Definition: BLI_bitmap.h:73
unsigned int BLI_bitmap
Definition: BLI_bitmap.h:32
unsigned short ushort
Definition: BLI_sys_types.h:84
#define UNUSED(x)
ID and Library types, which are fundamental for sdna.
@ LIB_TAG_DOIT
Definition: DNA_ID.h:554
@ INDEX_ID_NULL
Definition: DNA_ID.h:858
@ INDEX_ID_MAX
Definition: DNA_ID.h:859
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
#define NC_WINDOW
Definition: WM_types.h:277
BLI_bitmap * pyrna_set_to_enum_bitmap(const EnumPropertyItem *items, PyObject *value, int type_size, bool type_convert_sign, int bitmap_size, const char *error_prefix)
Definition: bpy_rna.c:1299
static bool id_check_type(const ID *id, const BLI_bitmap *types_bitmap)
static int id_code_as_index(const short idcode)
static PyObject * bpy_orphans_purge(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
PyMethodDef BPY_rna_id_collection_batch_remove_method_def
struct IDUserMapData IDUserMapData
static int foreach_libblock_id_user_map_callback(LibraryIDLinkCallbackData *cb_data)
PyDoc_STRVAR(bpy_user_map_doc, ".. method:: user_map([subset=(id1, id2, ...)], key_types={..}, value_types={..})\n" "\n" " Returns a mapping of all ID data-blocks in current ``bpy.data`` to a set of all " "datablocks 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\n" " :arg key_types: Filter the keys mapped by ID types.\n" " :type key_types: set of strings\n" " :arg value_types: Filter the values in the set by ID types.\n" " :type value_types: set of strings\n" " :return: dictionary of :class:`bpy.types.ID` instances, with sets of ID's as " "their values.\n" " :rtype: dict\n")
PyMethodDef BPY_rna_id_collection_orphans_purge_method_def
static PyObject * bpy_batch_remove(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
static PyObject * bpy_user_map(PyObject *UNUSED(self), PyObject *args, PyObject *kwds)
PyMethodDef BPY_rna_id_collection_user_map_method_def
PyObject * pyrna_id_CreatePyObject(ID *id)
Definition: bpy_rna.c:7611
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
Definition: bpy_rna.c:7622
#define GS(x)
Definition: iris.c:241
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
static void error(const char *str)
Definition: meshlaplacian.c:65
int PyC_ParseBool(PyObject *o, void *p)
return ret
const EnumPropertyItem rna_enum_id_type_items[]
Definition: rna_ID.c:46
PyObject_HEAD PointerRNA ptr
Definition: bpy_rna.h:125
BLI_bitmap * types_bitmap
Definition: DNA_ID.h:273
char name[66]
Definition: DNA_ID.h:283
Definition: BKE_main.h:116
void * data
Definition: RNA_types.h:52
void WM_main_add_notifier(unsigned int type, void *reference)