2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * Contributor(s): Joseph Eagar, Campbell Barton
21 * ***** END GPL LICENSE BLOCK *****
24 /** \file blender/python/generic/idprop_py_api.c
30 #include "MEM_guardedalloc.h"
32 #include "BLI_utildefines.h"
34 #include "idprop_py_api.h"
36 #include "BKE_idprop.h"
38 #define USE_STRING_COERCE
40 #ifdef USE_STRING_COERCE
41 #include "py_capi_utils.h"
44 #include "python_utildefines.h"
47 /*********************** ID Property Main Wrapper Stuff ***************/
49 /* ----------------------------------------------------------------------------
50 * static conversion functions to avoid duplicate code, no type checking.
53 static PyObject *idprop_py_from_idp_string(const IDProperty *prop)
55 if (prop->subtype == IDP_STRING_SUB_BYTE) {
56 return PyBytes_FromStringAndSize(IDP_String(prop), prop->len);
59 #ifdef USE_STRING_COERCE
60 return PyC_UnicodeFromByteAndSize(IDP_Array(prop), prop->len - 1);
62 return PyUnicode_FromStringAndSize(IDP_String(prop), prop->len - 1);
67 static PyObject *idprop_py_from_idp_int(const IDProperty *prop)
69 return PyLong_FromLong((long)IDP_Int(prop));
72 static PyObject *idprop_py_from_idp_float(const IDProperty *prop)
74 return PyFloat_FromDouble((double)IDP_Float(prop));
77 static PyObject *idprop_py_from_idp_double(const IDProperty *prop)
79 return PyFloat_FromDouble(IDP_Double(prop));
82 static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *parent)
84 BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type);
87 group->parent = parent; /* can be NULL */
88 return (PyObject *)group;
91 static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop)
93 BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type);
96 return (PyObject *)array;
99 static PyObject *idprop_py_from_idp_idparray(ID *id, IDProperty *prop)
101 PyObject *seq = PyList_New(prop->len), *wrap;
102 IDProperty *array = IDP_IDPArray(prop);
106 PyErr_Format(PyExc_RuntimeError,
107 "%s: IDP_IDPARRAY: PyList_New(%d) failed",
108 __func__, prop->len);
112 for (i = 0; i < prop->len; i++) {
113 wrap = BPy_IDGroup_WrapData(id, array++, prop);
115 if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */
118 PyList_SET_ITEM(seq, i, wrap);
124 /* -------------------------------------------------------------------------- */
126 /* use for both array and group */
127 static Py_hash_t BPy_IDGroup_hash(BPy_IDProperty *self)
129 return _Py_HashPointer(self->prop);
132 static PyObject *BPy_IDGroup_repr(BPy_IDProperty *self)
134 return PyUnicode_FromFormat("<bpy id prop: owner=\"%s\", name=\"%s\", address=%p>",
135 self->id ? self->id->name : "<NONE>", self->prop->name, self->prop);
138 PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent)
140 switch (prop->type) {
141 case IDP_STRING: return idprop_py_from_idp_string(prop);
142 case IDP_INT: return idprop_py_from_idp_int(prop);
143 case IDP_FLOAT: return idprop_py_from_idp_float(prop);
144 case IDP_DOUBLE: return idprop_py_from_idp_double(prop);
145 case IDP_GROUP: return idprop_py_from_idp_group(id, prop, parent);
146 case IDP_ARRAY: return idprop_py_from_idp_array(id, prop);
147 case IDP_IDPARRAY: return idprop_py_from_idp_idparray(id, prop); /* this could be better a internal type */
148 default: Py_RETURN_NONE;
152 #if 0 /* UNUSED, currently assignment overwrites into new properties, rather than setting in-place */
153 static int BPy_IDGroup_SetData(BPy_IDProperty *self, IDProperty *prop, PyObject *value)
155 switch (prop->type) {
159 if (!PyUnicode_Check(value)) {
160 PyErr_SetString(PyExc_TypeError, "expected a string!");
163 /* NOTE: if this code is enabled, bytes support needs to be added */
164 #ifdef USE_STRING_COERCE
167 PyObject *value_coerce = NULL;
169 st = (char *)PyC_UnicodeAsByte(value, &value_coerce);
170 alloc_len = strlen(st) + 1;
172 st = _PyUnicode_AsString(value);
173 IDP_ResizeArray(prop, alloc_len);
174 memcpy(IDP_Array(prop), st, alloc_len);
175 Py_XDECREF(value_coerce);
178 st = _PyUnicode_AsString(value);
179 IDP_ResizeArray(prop, strlen(st) + 1);
180 strcpy(IDP_Array(prop), st);
188 int ivalue = PyLong_AsSsize_t(value);
189 if (ivalue == -1 && PyErr_Occurred()) {
190 PyErr_SetString(PyExc_TypeError, "expected an int type");
193 IDP_Int(prop) = ivalue;
198 float fvalue = (float)PyFloat_AsDouble(value);
199 if (fvalue == -1 && PyErr_Occurred()) {
200 PyErr_SetString(PyExc_TypeError, "expected a float");
203 IDP_Float(self->prop) = fvalue;
208 double dvalue = PyFloat_AsDouble(value);
209 if (dvalue == -1 && PyErr_Occurred()) {
210 PyErr_SetString(PyExc_TypeError, "expected a float");
213 IDP_Double(self->prop) = dvalue;
217 PyErr_SetString(PyExc_AttributeError, "attempt to set read-only attribute!");
224 static PyObject *BPy_IDGroup_GetName(BPy_IDProperty *self, void *UNUSED(closure))
226 return PyUnicode_FromString(self->prop->name);
229 static int BPy_IDGroup_SetName(BPy_IDProperty *self, PyObject *value, void *UNUSED(closure))
232 Py_ssize_t name_size;
234 if (!PyUnicode_Check(value)) {
235 PyErr_SetString(PyExc_TypeError, "expected a string!");
239 name = _PyUnicode_AsStringAndSize(value, &name_size);
241 if (name_size > MAX_IDPROP_NAME) {
242 PyErr_SetString(PyExc_TypeError, "string length cannot exceed 63 characters!");
246 memcpy(self->prop->name, name, name_size);
251 static PyObject *BPy_IDGroup_GetType(BPy_IDProperty *self)
253 return PyLong_FromLong(self->prop->type);
257 static PyGetSetDef BPy_IDGroup_getseters[] = {
258 {(char *)"name", (getter)BPy_IDGroup_GetName, (setter)BPy_IDGroup_SetName, (char *)"The name of this Group.", NULL},
259 {NULL, NULL, NULL, NULL, NULL}
262 static Py_ssize_t BPy_IDGroup_Map_Len(BPy_IDProperty *self)
264 if (self->prop->type != IDP_GROUP) {
265 PyErr_SetString(PyExc_TypeError, "len() of unsized object");
269 return self->prop->len;
272 static PyObject *BPy_IDGroup_Map_GetItem(BPy_IDProperty *self, PyObject *item)
277 if (self->prop->type != IDP_GROUP) {
278 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
282 name = _PyUnicode_AsString(item);
285 PyErr_SetString(PyExc_TypeError, "only strings are allowed as keys of ID properties");
289 idprop = IDP_GetPropertyFromGroup(self->prop, name);
291 if (idprop == NULL) {
292 PyErr_SetString(PyExc_KeyError, "key not in subgroup dict");
296 return BPy_IDGroup_WrapData(self->id, idprop, self->prop);
299 /* returns NULL on success, error string on failure */
300 static char idp_sequence_type(PyObject *seq_fast)
302 PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
306 Py_ssize_t i, len = PySequence_Fast_GET_SIZE(seq_fast);
308 for (i = 0; i < len; i++) {
309 item = seq_fast_items[i];
310 if (PyFloat_Check(item)) {
311 if (type == IDP_IDPARRAY) { /* mixed dict/int */
316 else if (PyLong_Check(item)) {
317 if (type == IDP_IDPARRAY) { /* mixed dict/int */
321 else if (PyMapping_Check(item)) {
322 if (i != 0 && (type != IDP_IDPARRAY)) { /* mixed dict/int */
336 * \note group can be a pointer array or a group.
337 * assume we already checked key is a string.
341 bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob)
343 IDProperty *prop = NULL;
344 IDPropertyTemplate val = {0};
349 Py_ssize_t name_size;
350 name = _PyUnicode_AsStringAndSize(name_obj, &name_size);
353 PyErr_Format(PyExc_KeyError,
354 "invalid id-property key, expected a string, not a %.200s",
355 Py_TYPE(name_obj)->tp_name);
359 if (name_size > MAX_IDPROP_NAME) {
360 PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters");
368 if (PyFloat_Check(ob)) {
369 val.d = PyFloat_AsDouble(ob);
370 prop = IDP_New(IDP_DOUBLE, &val, name);
372 else if (PyLong_Check(ob)) {
373 val.i = _PyLong_AsInt(ob);
374 if (val.i == -1 && PyErr_Occurred()) {
377 prop = IDP_New(IDP_INT, &val, name);
379 else if (PyUnicode_Check(ob)) {
380 #ifdef USE_STRING_COERCE
381 PyObject *value_coerce = NULL;
382 val.string.str = PyC_UnicodeAsByte(ob, &value_coerce);
383 val.string.subtype = IDP_STRING_SUB_UTF8;
384 prop = IDP_New(IDP_STRING, &val, name);
385 Py_XDECREF(value_coerce);
387 val.str = _PyUnicode_AsString(ob);
388 prop = IDP_New(IDP_STRING, val, name);
391 else if (PyBytes_Check(ob)) {
392 val.string.str = PyBytes_AS_STRING(ob);
393 val.string.len = PyBytes_GET_SIZE(ob);
394 val.string.subtype = IDP_STRING_SUB_BYTE;
396 prop = IDP_New(IDP_STRING, &val, name);
397 //prop = IDP_NewString(PyBytes_AS_STRING(ob), name, PyBytes_GET_SIZE(ob));
398 //prop->subtype = IDP_STRING_SUB_BYTE;
400 else if (PySequence_Check(ob)) {
401 PyObject *ob_seq_fast;
402 PyObject **ob_seq_fast_items;
406 if (!(ob_seq_fast = PySequence_Fast(ob, "py -> idprop"))) {
410 ob_seq_fast_items = PySequence_Fast_ITEMS(ob_seq_fast);
412 if ((val.array.type = idp_sequence_type(ob_seq_fast)) == (char)-1) {
413 Py_DECREF(ob_seq_fast);
414 PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays");
418 /* validate sequence and derive type.
419 * we assume IDP_INT unless we hit a float
420 * number; then we assume it's */
422 val.array.len = PySequence_Fast_GET_SIZE(ob_seq_fast);
424 switch (val.array.type) {
429 prop = IDP_New(IDP_ARRAY, &val, name);
430 prop_data = IDP_Array(prop);
431 for (i = 0; i < val.array.len; i++) {
432 item = ob_seq_fast_items[i];
433 if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) {
434 Py_DECREF(ob_seq_fast);
443 prop = IDP_New(IDP_ARRAY, &val, name);
444 prop_data = IDP_Array(prop);
445 for (i = 0; i < val.array.len; i++) {
446 item = ob_seq_fast_items[i];
447 if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) {
448 Py_DECREF(ob_seq_fast);
456 prop = IDP_NewIDPArray(name);
457 for (i = 0; i < val.array.len; i++) {
458 item = ob_seq_fast_items[i];
460 if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) {
461 Py_DECREF(ob_seq_fast);
468 /* should never happen */
469 Py_DECREF(ob_seq_fast);
470 PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
474 Py_DECREF(ob_seq_fast);
476 else if (PyMapping_Check(ob)) {
477 PyObject *keys, *vals, *key, *pval;
479 /*yay! we get into recursive stuff now!*/
480 keys = PyMapping_Keys(ob);
481 vals = PyMapping_Values(ob);
483 /* we allocate the group first; if we hit any invalid data,
484 * we can delete it easily enough.*/
485 prop = IDP_New(IDP_GROUP, &val, name);
486 len = PyMapping_Length(ob);
487 for (i = 0; i < len; i++) {
488 key = PySequence_GetItem(keys, i);
489 pval = PySequence_GetItem(vals, i);
490 if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) {
491 IDP_FreeProperty(prop);
497 /* error is already set */
507 PyErr_Format(PyExc_TypeError,
508 "invalid id-property type %.200s not supported",
509 Py_TYPE(ob)->tp_name);
513 if (group->type == IDP_IDPARRAY) {
514 IDP_AppendArray(group, prop);
515 // IDP_FreeProperty(item); /* IDP_AppendArray does a shallow copy (memcpy), only free memory */
519 IDProperty *prop_exist;
521 /* avoid freeing when types match in case they are referenced by the UI, see: T37073
522 * obviously this isn't a complete solution, but helps for common cases. */
523 prop_exist = IDP_GetPropertyFromGroup(group, prop->name);
524 if ((prop_exist != NULL) &&
525 (prop_exist->type == prop->type) &&
526 (prop_exist->subtype == prop->subtype))
528 /* Preserve prev/next links!!! See T42593. */
529 prop->prev = prop_exist->prev;
530 prop->next = prop_exist->next;
532 IDP_FreeProperty(prop_exist);
537 IDP_ReplaceInGroup_ex(group, prop, prop_exist);
544 int BPy_Wrap_SetMapItem(IDProperty *prop, PyObject *key, PyObject *val)
546 if (prop->type != IDP_GROUP) {
547 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
551 if (val == NULL) { /* del idprop[key] */
553 const char *name = _PyUnicode_AsString(key);
556 PyErr_Format(PyExc_KeyError,
557 "expected a string, not %.200s",
558 Py_TYPE(key)->tp_name);
562 pkey = IDP_GetPropertyFromGroup(prop, name);
564 IDP_FreeFromGroup(prop, pkey);
568 PyErr_SetString(PyExc_KeyError, "property not found in group");
575 ok = BPy_IDProperty_Map_ValidateAndCreate(key, prop, val);
584 static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val)
586 return BPy_Wrap_SetMapItem(self->prop, key, val);
589 static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self)
591 BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
593 iter->mode = IDPROP_ITER_KEYS;
594 iter->cur = self->prop->data.group.first;
596 return (PyObject *)iter;
599 /* for simple, non nested types this is the same as BPy_IDGroup_WrapData */
600 static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
602 switch (prop->type) {
604 return idprop_py_from_idp_string(prop);
606 return idprop_py_from_idp_int(prop);
608 return idprop_py_from_idp_float(prop);
610 return idprop_py_from_idp_double(prop);
613 PyObject *seq = PyList_New(prop->len);
617 PyErr_Format(PyExc_RuntimeError,
618 "%s: IDP_ARRAY: PyList_New(%d) failed",
619 __func__, prop->len);
623 switch (prop->subtype) {
626 const float *array = (float *)IDP_Array(prop);
627 for (i = 0; i < prop->len; i++) {
628 PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i]));
634 const double *array = (double *)IDP_Array(prop);
635 for (i = 0; i < prop->len; i++) {
636 PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i]));
642 const int *array = (int *)IDP_Array(prop);
643 for (i = 0; i < prop->len; i++) {
644 PyList_SET_ITEM(seq, i, PyLong_FromLong(array[i]));
649 PyErr_Format(PyExc_RuntimeError,
650 "%s: invalid/corrupt array type '%d'!",
651 __func__, prop->subtype);
660 PyObject *seq = PyList_New(prop->len), *wrap;
661 IDProperty *array = IDP_IDPArray(prop);
665 PyErr_Format(PyExc_RuntimeError,
666 "%s: IDP_IDPARRAY: PyList_New(%d) failed",
667 __func__, prop->len);
671 for (i = 0; i < prop->len; i++) {
672 wrap = BPy_IDGroup_MapDataToPy(array++);
674 if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */
677 PyList_SET_ITEM(seq, i, wrap);
683 PyObject *dict = PyDict_New(), *wrap;
686 for (loop = prop->data.group.first; loop; loop = loop->next) {
687 wrap = BPy_IDGroup_MapDataToPy(loop);
689 if (!wrap) /* BPy_IDGroup_MapDataToPy sets the error */
692 PyDict_SetItemString(dict, loop->name, wrap);
699 PyErr_Format(PyExc_RuntimeError,
700 "%s ERROR: '%s' property exists with a bad type code '%d'!",
701 __func__, prop->name, prop->type);
705 PyDoc_STRVAR(BPy_IDGroup_pop_doc,
706 ".. method:: pop(key)\n"
708 " Remove an item from the group, returning a Python representation.\n"
710 " :raises KeyError: When the item doesn't exist.\n"
712 " :arg key: Name of item to remove.\n"
713 " :type key: string\n"
715 static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *value)
719 const char *name = _PyUnicode_AsString(value);
722 PyErr_Format(PyExc_TypeError,
723 "pop expected at least a string argument, not %.200s",
724 Py_TYPE(value)->tp_name);
728 idprop = IDP_GetPropertyFromGroup(self->prop, name);
731 pyform = BPy_IDGroup_MapDataToPy(idprop);
734 /* ok something bad happened with the pyobject,
735 * so don't remove the prop from the group. if pyform is
736 * NULL, then it already should have raised an exception.*/
740 IDP_RemoveFromGroup(self->prop, idprop);
744 PyErr_SetString(PyExc_KeyError, "item not in group");
748 PyDoc_STRVAR(BPy_IDGroup_iter_items_doc,
749 ".. method:: iteritems()\n"
751 " Iterate through the items in the dict; behaves like dictionary method iteritems.\n"
753 static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self)
755 BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
757 iter->mode = IDPROP_ITER_ITEMS;
758 iter->cur = self->prop->data.group.first;
760 return (PyObject *)iter;
763 /* utility function */
764 static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
768 printf("%s: ID Property Error found and corrected!\n", func);
770 /* fill rest of list with valid references to None */
771 for (j = len; j < prop->len; j++) {
772 PyList_SET_ITEM(seq, j, Py_INCREF_RET(Py_None));
775 /*set correct group length*/
779 PyObject *BPy_Wrap_GetKeys(IDProperty *prop)
781 PyObject *list = PyList_New(prop->len);
785 for (i = 0, loop = prop->data.group.first; loop && (i < prop->len); loop = loop->next, i++)
786 PyList_SET_ITEM(list, i, PyUnicode_FromString(loop->name));
788 /* if the id prop is corrupt, count the remaining */
789 for ( ; loop; loop = loop->next, i++) {
793 if (i != prop->len) { /* if the loop didnt finish, we know the length is wrong */
794 BPy_IDGroup_CorrectListLen(prop, list, i, __func__);
795 Py_DECREF(list); /*free the list*/
797 return BPy_Wrap_GetKeys(prop);
803 PyObject *BPy_Wrap_GetValues(ID *id, IDProperty *prop)
805 PyObject *list = PyList_New(prop->len);
809 for (i = 0, loop = prop->data.group.first; loop; loop = loop->next, i++) {
810 PyList_SET_ITEM(list, i, BPy_IDGroup_WrapData(id, loop, prop));
813 if (i != prop->len) {
814 BPy_IDGroup_CorrectListLen(prop, list, i, __func__);
815 Py_DECREF(list); /*free the list*/
817 return BPy_Wrap_GetValues(id, prop);
823 PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
825 PyObject *seq = PyList_New(prop->len);
829 for (i = 0, loop = prop->data.group.first; loop; loop = loop->next, i++) {
830 PyObject *item = PyTuple_New(2);
831 PyTuple_SET_ITEMS(item,
832 PyUnicode_FromString(loop->name),
833 BPy_IDGroup_WrapData(id, loop, prop));
834 PyList_SET_ITEM(seq, i, item);
837 if (i != prop->len) {
838 BPy_IDGroup_CorrectListLen(prop, seq, i, __func__);
839 Py_DECREF(seq); /*free the list*/
841 return BPy_Wrap_GetItems(id, prop);
847 PyDoc_STRVAR(BPy_IDGroup_keys_doc,
848 ".. method:: keys()\n"
850 " Return the keys associated with this group as a list of strings.\n"
852 static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
854 return BPy_Wrap_GetKeys(self->prop);
857 PyDoc_STRVAR(BPy_IDGroup_values_doc,
858 ".. method:: values()\n"
860 " Return the values associated with this group.\n"
862 static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
864 return BPy_Wrap_GetValues(self->id, self->prop);
867 PyDoc_STRVAR(BPy_IDGroup_items_doc,
868 ".. method:: items()\n"
870 " Return the items associated with this group.\n"
872 static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
874 return BPy_Wrap_GetItems(self->id, self->prop);
877 static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
879 const char *name = _PyUnicode_AsString(value);
882 PyErr_Format(PyExc_TypeError,
883 "expected a string, not a %.200s",
884 Py_TYPE(value)->tp_name);
888 return IDP_GetPropertyFromGroup(self->prop, name) ? 1 : 0;
891 PyDoc_STRVAR(BPy_IDGroup_update_doc,
892 ".. method:: update(other)\n"
894 " Update key, values.\n"
896 " :arg other: Updates the values in the group with this.\n"
897 " :type other: :class:`IDPropertyGroup` or dict\n"
899 static PyObject *BPy_IDGroup_update(BPy_IDProperty *self, PyObject *value)
901 PyObject *pkey, *pval;
904 if (BPy_IDGroup_Check(value)) {
905 BPy_IDProperty *other = (BPy_IDProperty *)value;
906 if (UNLIKELY(self->prop == other->prop)) {
910 /* XXX, possible one is inside the other */
911 IDP_MergeGroup(self->prop, other->prop, true);
913 else if (PyDict_Check(value)) {
914 while (PyDict_Next(value, &i, &pkey, &pval)) {
915 BPy_IDGroup_Map_SetItem(self, pkey, pval);
916 if (PyErr_Occurred()) return NULL;
920 PyErr_Format(PyExc_TypeError,
921 "expected a dict or an IDPropertyGroup type, not a %.200s",
922 Py_TYPE(value)->tp_name);
930 PyDoc_STRVAR(BPy_IDGroup_to_dict_doc,
931 ".. method:: to_dict()\n"
933 " Return a purely python version of the group.\n"
935 static PyObject *BPy_IDGroup_to_dict(BPy_IDProperty *self)
937 return BPy_IDGroup_MapDataToPy(self->prop);
940 PyDoc_STRVAR(BPy_IDGroup_clear_doc,
941 ".. method:: clear()\n"
943 " Clear all members from this group.\n"
945 static PyObject *BPy_IDGroup_clear(BPy_IDProperty *self)
947 IDP_ClearProperty(self->prop);
951 PyDoc_STRVAR(BPy_IDGroup_get_doc,
952 ".. method:: get(key, default=None)\n"
954 " Return the value for key, if it exists, else default.\n"
956 static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
960 PyObject *def = Py_None;
962 if (!PyArg_ParseTuple(args, "s|O:get", &key, &def))
965 idprop = IDP_GetPropertyFromGroup(self->prop, key);
967 PyObject *pyobj = BPy_IDGroup_WrapData(self->id, idprop, self->prop);
976 static struct PyMethodDef BPy_IDGroup_methods[] = {
977 {"pop", (PyCFunction)BPy_IDGroup_pop, METH_O, BPy_IDGroup_pop_doc},
978 {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
979 {"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
980 {"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
981 {"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
982 {"update", (PyCFunction)BPy_IDGroup_update, METH_O, BPy_IDGroup_update_doc},
983 {"get", (PyCFunction)BPy_IDGroup_get, METH_VARARGS, BPy_IDGroup_get_doc},
984 {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS, BPy_IDGroup_to_dict_doc},
985 {"clear", (PyCFunction)BPy_IDGroup_clear, METH_NOARGS, BPy_IDGroup_clear_doc},
986 {NULL, NULL, 0, NULL}
989 static PySequenceMethods BPy_IDGroup_Seq = {
990 (lenfunc) BPy_IDGroup_Map_Len, /* lenfunc sq_length */
991 NULL, /* binaryfunc sq_concat */
992 NULL, /* ssizeargfunc sq_repeat */
993 NULL, /* ssizeargfunc sq_item */ /* TODO - setting this will allow PySequence_Check to return True */
994 NULL, /* intintargfunc ***was_sq_slice*** */
995 NULL, /* intobjargproc sq_ass_item */
996 NULL, /* ssizeobjargproc ***was_sq_ass_slice*** */
997 (objobjproc) BPy_IDGroup_Contains, /* objobjproc sq_contains */
998 NULL, /* binaryfunc sq_inplace_concat */
999 NULL, /* ssizeargfunc sq_inplace_repeat */
1002 static PyMappingMethods BPy_IDGroup_Mapping = {
1003 (lenfunc)BPy_IDGroup_Map_Len, /*inquiry mp_length */
1004 (binaryfunc)BPy_IDGroup_Map_GetItem, /*binaryfunc mp_subscript */
1005 (objobjargproc)BPy_IDGroup_Map_SetItem, /*objobjargproc mp_ass_subscript */
1008 PyTypeObject BPy_IDGroup_Type = {
1009 PyVarObject_HEAD_INIT(NULL, 0)
1010 /* For printing, in format "<module>.<name>" */
1011 "IDPropertyGroup", /* char *tp_name; */
1012 sizeof(BPy_IDProperty), /* int tp_basicsize; */
1013 0, /* tp_itemsize; For allocation */
1015 /* Methods to implement standard operations */
1017 NULL, /* destructor tp_dealloc; */
1018 NULL, /* printfunc tp_print; */
1019 NULL, /* getattrfunc tp_getattr; */
1020 NULL, /* setattrfunc tp_setattr; */
1021 NULL, /* cmpfunc tp_compare; */
1022 (reprfunc)BPy_IDGroup_repr, /* reprfunc tp_repr; */
1024 /* Method suites for standard classes */
1026 NULL, /* PyNumberMethods *tp_as_number; */
1027 &BPy_IDGroup_Seq, /* PySequenceMethods *tp_as_sequence; */
1028 &BPy_IDGroup_Mapping, /* PyMappingMethods *tp_as_mapping; */
1030 /* More standard operations (here for binary compatibility) */
1032 (hashfunc)BPy_IDGroup_hash, /* hashfunc tp_hash; */
1033 NULL, /* ternaryfunc tp_call; */
1034 NULL, /* reprfunc tp_str; */
1035 NULL, /* getattrofunc tp_getattro; */
1036 NULL, /* setattrofunc tp_setattro; */
1038 /* Functions to access object as input/output buffer */
1039 NULL, /* PyBufferProcs *tp_as_buffer; */
1041 /*** Flags to define presence of optional/expanded features ***/
1042 Py_TPFLAGS_DEFAULT, /* long tp_flags; */
1044 NULL, /* char *tp_doc; Documentation string */
1045 /*** Assigned meaning in release 2.0 ***/
1046 /* call function for all accessible objects */
1047 NULL, /* traverseproc tp_traverse; */
1049 /* delete references to contained objects */
1050 NULL, /* inquiry tp_clear; */
1052 /*** Assigned meaning in release 2.1 ***/
1053 /*** rich comparisons ***/
1054 NULL, /* richcmpfunc tp_richcompare; */
1056 /*** weak reference enabler ***/
1057 0, /* long tp_weaklistoffset; */
1059 /*** Added in release 2.2 ***/
1061 (getiterfunc)BPy_IDGroup_iter, /* getiterfunc tp_iter; */
1062 NULL, /* iternextfunc tp_iternext; */
1063 /*** Attribute descriptor and subclassing stuff ***/
1064 BPy_IDGroup_methods, /* struct PyMethodDef *tp_methods; */
1065 NULL, /* struct PyMemberDef *tp_members; */
1066 BPy_IDGroup_getseters, /* struct PyGetSetDef *tp_getset; */
1069 /********Array Wrapper********/
1071 static PyTypeObject *idp_array_py_type(BPy_IDArray *self, bool *r_is_double)
1073 switch (self->prop->subtype) {
1075 *r_is_double = false;
1076 return &PyFloat_Type;
1078 *r_is_double = true;
1079 return &PyFloat_Type;
1081 *r_is_double = false;
1082 return &PyLong_Type;
1084 *r_is_double = false;
1089 static PyObject *BPy_IDArray_repr(BPy_IDArray *self)
1091 return PyUnicode_FromFormat("<bpy id property array [%d]>", self->prop->len);
1094 PyDoc_STRVAR(BPy_IDArray_get_typecode_doc,
1095 "The type of the data in the array {'f': float, 'd': double, 'i': int}."
1097 static PyObject *BPy_IDArray_get_typecode(BPy_IDArray *self)
1099 switch (self->prop->subtype) {
1100 case IDP_FLOAT: return PyUnicode_FromString("f");
1101 case IDP_DOUBLE: return PyUnicode_FromString("d");
1102 case IDP_INT: return PyUnicode_FromString("i");
1105 PyErr_Format(PyExc_RuntimeError,
1106 "%s: invalid/corrupt array type '%d'!",
1107 __func__, self->prop->subtype);
1112 static PyGetSetDef BPy_IDArray_getseters[] = {
1113 /* matches pythons array.typecode */
1114 {(char *)"typecode", (getter)BPy_IDArray_get_typecode, (setter)NULL, BPy_IDArray_get_typecode_doc, NULL},
1115 {NULL, NULL, NULL, NULL, NULL},
1118 PyDoc_STRVAR(BPy_IDArray_to_list_doc,
1119 ".. method:: to_list()\n"
1121 " Return the array as a list.\n"
1123 static PyObject *BPy_IDArray_to_list(BPy_IDArray *self)
1125 return BPy_IDGroup_MapDataToPy(self->prop);
1128 static PyMethodDef BPy_IDArray_methods[] = {
1129 {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS, BPy_IDArray_to_list_doc},
1130 {NULL, NULL, 0, NULL}
1133 static int BPy_IDArray_Len(BPy_IDArray *self)
1135 return self->prop->len;
1138 static PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, int index)
1140 if (index < 0 || index >= self->prop->len) {
1141 PyErr_SetString(PyExc_IndexError, "index out of range!");
1145 switch (self->prop->subtype) {
1147 return PyFloat_FromDouble(((float *)IDP_Array(self->prop))[index]);
1149 return PyFloat_FromDouble(((double *)IDP_Array(self->prop))[index]);
1151 return PyLong_FromLong((long)((int *)IDP_Array(self->prop))[index]);
1154 PyErr_Format(PyExc_RuntimeError,
1155 "%s: invalid/corrupt array type '%d'!",
1156 __func__, self->prop->subtype);
1161 static int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *value)
1163 if (index < 0 || index >= self->prop->len) {
1164 PyErr_SetString(PyExc_RuntimeError, "index out of range!");
1168 switch (self->prop->subtype) {
1171 const float f = (float)PyFloat_AsDouble(value);
1172 if (f == -1 && PyErr_Occurred()) {
1175 ((float *)IDP_Array(self->prop))[index] = f;
1180 const double d = PyFloat_AsDouble(value);
1181 if (d == -1 && PyErr_Occurred()) {
1184 ((double *)IDP_Array(self->prop))[index] = d;
1189 const int i = _PyLong_AsInt(value);
1190 if (i == -1 && PyErr_Occurred()) {
1194 ((int *)IDP_Array(self->prop))[index] = i;
1201 static PySequenceMethods BPy_IDArray_Seq = {
1202 (lenfunc) BPy_IDArray_Len, /* inquiry sq_length */
1203 NULL, /* binaryfunc sq_concat */
1204 NULL, /* intargfunc sq_repeat */
1205 (ssizeargfunc)BPy_IDArray_GetItem, /* intargfunc sq_item */
1206 NULL, /* intintargfunc sq_slice */
1207 (ssizeobjargproc)BPy_IDArray_SetItem, /* intobjargproc sq_ass_item */
1208 NULL, /* intintobjargproc sq_ass_slice */
1209 NULL, /* objobjproc sq_contains */
1210 /* Added in release 2.0 */
1211 NULL, /* binaryfunc sq_inplace_concat */
1212 NULL, /* intargfunc sq_inplace_repeat */
1217 /* sequence slice (get): idparr[a:b] */
1218 static PyObject *BPy_IDArray_slice(BPy_IDArray *self, int begin, int end)
1220 IDProperty *prop = self->prop;
1224 CLAMP(begin, 0, prop->len);
1225 if (end < 0) end = prop->len + end + 1;
1226 CLAMP(end, 0, prop->len);
1227 begin = MIN2(begin, end);
1229 tuple = PyTuple_New(end - begin);
1231 switch (prop->subtype) {
1234 const float *array = (float *)IDP_Array(prop);
1235 for (count = begin; count < end; count++) {
1236 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count]));
1242 const double *array = (double *)IDP_Array(prop);
1243 for (count = begin; count < end; count++) {
1244 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count]));
1250 const int *array = (int *)IDP_Array(prop);
1251 for (count = begin; count < end; count++) {
1252 PyTuple_SET_ITEM(tuple, count - begin, PyLong_FromLong(array[count]));
1260 /* sequence slice (set): idparr[a:b] = value */
1261 static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject *seq)
1263 IDProperty *prop = self->prop;
1265 const PyTypeObject *py_type = idp_array_py_type(self, &is_double);
1266 const size_t elem_size = is_double ? sizeof(double) : sizeof(float);
1271 CLAMP(begin, 0, prop->len);
1272 CLAMP(end, 0, prop->len);
1273 begin = MIN2(begin, end);
1275 size = (end - begin);
1276 alloc_len = size * elem_size;
1278 vec = MEM_mallocN(alloc_len, "array assignment"); /* NOTE: we count on int/float being the same size here */
1279 if (PyC_AsArray(vec, seq, size, py_type, is_double, "slice assignment: ") == -1) {
1284 memcpy((void *)(((char *)IDP_Array(prop)) + (begin * elem_size)), vec, alloc_len);
1290 static PyObject *BPy_IDArray_subscript(BPy_IDArray *self, PyObject *item)
1292 if (PyIndex_Check(item)) {
1294 i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1295 if (i == -1 && PyErr_Occurred())
1298 i += self->prop->len;
1299 return BPy_IDArray_GetItem(self, i);
1301 else if (PySlice_Check(item)) {
1302 Py_ssize_t start, stop, step, slicelength;
1304 if (PySlice_GetIndicesEx(item, self->prop->len, &start, &stop, &step, &slicelength) < 0)
1307 if (slicelength <= 0) {
1308 return PyTuple_New(0);
1310 else if (step == 1) {
1311 return BPy_IDArray_slice(self, start, stop);
1314 PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors");
1319 PyErr_Format(PyExc_TypeError,
1320 "vector indices must be integers, not %.200s",
1321 __func__, Py_TYPE(item)->tp_name);
1326 static int BPy_IDArray_ass_subscript(BPy_IDArray *self, PyObject *item, PyObject *value)
1328 if (PyIndex_Check(item)) {
1329 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1330 if (i == -1 && PyErr_Occurred())
1333 i += self->prop->len;
1334 return BPy_IDArray_SetItem(self, i, value);
1336 else if (PySlice_Check(item)) {
1337 Py_ssize_t start, stop, step, slicelength;
1339 if (PySlice_GetIndicesEx(item, self->prop->len, &start, &stop, &step, &slicelength) < 0)
1343 return BPy_IDArray_ass_slice(self, start, stop, value);
1345 PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors");
1350 PyErr_Format(PyExc_TypeError,
1351 "vector indices must be integers, not %.200s",
1352 Py_TYPE(item)->tp_name);
1357 static PyMappingMethods BPy_IDArray_AsMapping = {
1358 (lenfunc)BPy_IDArray_Len,
1359 (binaryfunc)BPy_IDArray_subscript,
1360 (objobjargproc)BPy_IDArray_ass_subscript
1364 PyTypeObject BPy_IDArray_Type = {
1365 PyVarObject_HEAD_INIT(NULL, 0)
1366 /* For printing, in format "<module>.<name>" */
1367 "IDPropertyArray", /* char *tp_name; */
1368 sizeof(BPy_IDArray), /* int tp_basicsize; */
1369 0, /* tp_itemsize; For allocation */
1371 /* Methods to implement standard operations */
1373 NULL, /* destructor tp_dealloc; */
1374 NULL, /* printfunc tp_print; */
1375 NULL, /* getattrfunc tp_getattr; */
1376 NULL, /* setattrfunc tp_setattr; */
1377 NULL, /* cmpfunc tp_compare; */
1378 (reprfunc)BPy_IDArray_repr, /* reprfunc tp_repr; */
1380 /* Method suites for standard classes */
1382 NULL, /* PyNumberMethods *tp_as_number; */
1383 &BPy_IDArray_Seq, /* PySequenceMethods *tp_as_sequence; */
1384 &BPy_IDArray_AsMapping, /* PyMappingMethods *tp_as_mapping; */
1386 /* More standard operations (here for binary compatibility) */
1388 NULL, /* hashfunc tp_hash; */
1389 NULL, /* ternaryfunc tp_call; */
1390 NULL, /* reprfunc tp_str; */
1391 NULL, /* getattrofunc tp_getattro; */
1392 NULL, /* setattrofunc tp_setattro; */
1394 /* Functions to access object as input/output buffer */
1395 NULL, /* PyBufferProcs *tp_as_buffer; */
1397 /*** Flags to define presence of optional/expanded features ***/
1398 Py_TPFLAGS_DEFAULT, /* long tp_flags; */
1400 NULL, /* char *tp_doc; Documentation string */
1401 /*** Assigned meaning in release 2.0 ***/
1402 /* call function for all accessible objects */
1403 NULL, /* traverseproc tp_traverse; */
1405 /* delete references to contained objects */
1406 NULL, /* inquiry tp_clear; */
1408 /*** Assigned meaning in release 2.1 ***/
1409 /*** rich comparisons ***/
1410 NULL, /* richcmpfunc tp_richcompare; */
1412 /*** weak reference enabler ***/
1413 0, /* long tp_weaklistoffset; */
1415 /*** Added in release 2.2 ***/
1417 NULL, /* getiterfunc tp_iter; */
1418 NULL, /* iternextfunc tp_iternext; */
1420 /*** Attribute descriptor and subclassing stuff ***/
1421 BPy_IDArray_methods, /* struct PyMethodDef *tp_methods; */
1422 NULL, /* struct PyMemberDef *tp_members; */
1423 BPy_IDArray_getseters, /* struct PyGetSetDef *tp_getset; */
1424 NULL, /* struct _typeobject *tp_base; */
1425 NULL, /* PyObject *tp_dict; */
1426 NULL, /* descrgetfunc tp_descr_get; */
1427 NULL, /* descrsetfunc tp_descr_set; */
1428 0, /* long tp_dictoffset; */
1429 NULL, /* initproc tp_init; */
1430 NULL, /* allocfunc tp_alloc; */
1431 NULL, /* newfunc tp_new; */
1432 /* Low-level free-memory routine */
1433 NULL, /* freefunc tp_free; */
1434 /* For PyObject_IS_GC */
1435 NULL, /* inquiry tp_is_gc; */
1436 NULL, /* PyObject *tp_bases; */
1437 /* method resolution order */
1438 NULL, /* PyObject *tp_mro; */
1439 NULL, /* PyObject *tp_cache; */
1440 NULL, /* PyObject *tp_subclasses; */
1441 NULL, /* PyObject *tp_weaklist; */
1445 /*********** ID Property Group iterator ********/
1447 static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
1449 return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name);
1452 static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
1460 self->cur = self->cur->next;
1462 if (self->mode == IDPROP_ITER_ITEMS) {
1463 ret = PyTuple_New(2);
1464 PyTuple_SET_ITEMS(ret,
1465 PyUnicode_FromString(cur->name),
1466 BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
1470 return PyUnicode_FromString(cur->name);
1474 PyErr_SetNone(PyExc_StopIteration);
1479 PyTypeObject BPy_IDGroup_Iter_Type = {
1480 PyVarObject_HEAD_INIT(NULL, 0)
1481 /* For printing, in format "<module>.<name>" */
1482 "IDPropertyGroupIter", /* char *tp_name; */
1483 sizeof(BPy_IDGroup_Iter), /* int tp_basicsize; */
1484 0, /* tp_itemsize; For allocation */
1486 /* Methods to implement standard operations */
1488 NULL, /* destructor tp_dealloc; */
1489 NULL, /* printfunc tp_print; */
1490 NULL, /* getattrfunc tp_getattr; */
1491 NULL, /* setattrfunc tp_setattr; */
1492 NULL, /* cmpfunc tp_compare; */
1493 (reprfunc) IDGroup_Iter_repr, /* reprfunc tp_repr; */
1495 /* Method suites for standard classes */
1497 NULL, /* PyNumberMethods *tp_as_number; */
1498 NULL, /* PySequenceMethods *tp_as_sequence; */
1499 NULL, /* PyMappingMethods *tp_as_mapping; */
1501 /* More standard operations (here for binary compatibility) */
1503 NULL, /* hashfunc tp_hash; */
1504 NULL, /* ternaryfunc tp_call; */
1505 NULL, /* reprfunc tp_str; */
1506 NULL, /* getattrofunc tp_getattro; */
1507 NULL, /* setattrofunc tp_setattro; */
1509 /* Functions to access object as input/output buffer */
1510 NULL, /* PyBufferProcs *tp_as_buffer; */
1512 /*** Flags to define presence of optional/expanded features ***/
1513 Py_TPFLAGS_DEFAULT, /* long tp_flags; */
1515 NULL, /* char *tp_doc; Documentation string */
1516 /*** Assigned meaning in release 2.0 ***/
1517 /* call function for all accessible objects */
1518 NULL, /* traverseproc tp_traverse; */
1520 /* delete references to contained objects */
1521 NULL, /* inquiry tp_clear; */
1523 /*** Assigned meaning in release 2.1 ***/
1524 /*** rich comparisons ***/
1525 NULL, /* richcmpfunc tp_richcompare; */
1527 /*** weak reference enabler ***/
1528 0, /* long tp_weaklistoffset; */
1530 /*** Added in release 2.2 ***/
1532 PyObject_SelfIter, /* getiterfunc tp_iter; */
1533 (iternextfunc) BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
1536 void IDProp_Init_Types(void)
1538 PyType_Ready(&BPy_IDGroup_Type);
1539 PyType_Ready(&BPy_IDGroup_Iter_Type);
1540 PyType_Ready(&BPy_IDArray_Type);
1543 /*----------------------------MODULE INIT-------------------------*/
1547 static struct PyModuleDef IDProp_types_module_def = {
1548 PyModuleDef_HEAD_INIT,
1549 "idprop.types", /* m_name */
1552 NULL, /* m_methods */
1553 NULL, /* m_reload */
1554 NULL, /* m_traverse */
1559 static PyObject *BPyInit_idprop_types(void)
1561 PyObject *submodule;
1563 submodule = PyModule_Create(&IDProp_types_module_def);
1565 IDProp_Init_Types();
1567 #define MODULE_TYPE_ADD(s, t) \
1568 PyModule_AddObject(s, t.tp_name, (PyObject *)&t); Py_INCREF((PyObject *)&t)
1570 /* bmesh_py_types.c */
1571 MODULE_TYPE_ADD(submodule, BPy_IDGroup_Type);
1572 MODULE_TYPE_ADD(submodule, BPy_IDGroup_Iter_Type);
1573 MODULE_TYPE_ADD(submodule, BPy_IDArray_Type);
1575 #undef MODULE_TYPE_ADD
1582 static PyMethodDef IDProp_methods[] = {
1583 {NULL, NULL, 0, NULL}
1587 PyDoc_STRVAR(IDProp_module_doc,
1588 "This module provides access id property types (currently mainly for docs)."
1590 static struct PyModuleDef IDProp_module_def = {
1591 PyModuleDef_HEAD_INIT,
1592 "idprop", /* m_name */
1593 IDProp_module_doc, /* m_doc */
1595 IDProp_methods, /* m_methods */
1596 NULL, /* m_reload */
1597 NULL, /* m_traverse */
1602 PyObject *BPyInit_idprop(void)
1605 PyObject *submodule;
1606 PyObject *sys_modules = PyThreadState_GET()->interp->modules;
1608 mod = PyModule_Create(&IDProp_module_def);
1611 PyModule_AddObject(mod, "types", (submodule = BPyInit_idprop_types()));
1612 PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
1613 Py_INCREF(submodule);
1620 /* -------------------------------------------------------------------- */
1621 /* debug only function */
1623 void IDP_spit(IDProperty *prop)
1626 PyGILState_STATE gilstate;
1627 bool use_gil = true; /* !PyC_IsInterpreterActive(); */
1632 gilstate = PyGILState_Ensure();
1636 ret_dict = BPy_IDGroup_MapDataToPy(prop);
1637 ret_str = PyObject_Repr(ret_dict);
1638 Py_DECREF(ret_dict);
1640 printf("IDProperty(%p): %s\n", prop, _PyUnicode_AsString(ret_str));
1645 PyGILState_Release(gilstate);
1649 printf("IDProperty: <NIL>\n");