Datablock ID Properties
[blender.git] / source / blender / python / generic / idprop_py_api.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
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.
8  *
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.
13  *
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.
17  *
18  *
19  * Contributor(s): Joseph Eagar, Campbell Barton
20  *
21  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 /** \file blender/python/generic/idprop_py_api.c
25  *  \ingroup pygen
26  */
27
28 #include <Python.h>
29
30 #include "MEM_guardedalloc.h"
31
32 #include "BLI_utildefines.h"
33
34 #include "idprop_py_api.h"
35
36 #include "BKE_idprop.h"
37
38 #define USE_STRING_COERCE
39
40 #ifdef USE_STRING_COERCE
41 #include "py_capi_utils.h"
42 #endif
43
44 #include "python_utildefines.h"
45
46 extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
47 extern PyObject *pyrna_id_CreatePyObject(ID *id);
48 extern bool pyrna_id_CheckPyObject(PyObject *obj);
49
50 /*********************** ID Property Main Wrapper Stuff ***************/
51
52 /* ----------------------------------------------------------------------------
53  * static conversion functions to avoid duplicate code, no type checking.
54  */
55
56 static PyObject *idprop_py_from_idp_string(const IDProperty *prop)
57 {
58         if (prop->subtype == IDP_STRING_SUB_BYTE) {
59                 return PyBytes_FromStringAndSize(IDP_String(prop), prop->len);
60         }
61         else {
62 #ifdef USE_STRING_COERCE
63                 return PyC_UnicodeFromByteAndSize(IDP_Array(prop), prop->len - 1);
64 #else
65                 return PyUnicode_FromStringAndSize(IDP_String(prop), prop->len - 1);
66 #endif
67         }
68 }
69
70 static PyObject *idprop_py_from_idp_int(const IDProperty *prop)
71 {
72         return PyLong_FromLong((long)IDP_Int(prop));
73 }
74
75 static PyObject *idprop_py_from_idp_float(const IDProperty *prop)
76 {
77         return PyFloat_FromDouble((double)IDP_Float(prop));
78 }
79
80 static PyObject *idprop_py_from_idp_double(const IDProperty *prop)
81 {
82         return PyFloat_FromDouble(IDP_Double(prop));
83 }
84
85 static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *parent)
86 {
87         BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type);
88         group->id = id;
89         group->prop = prop;
90         group->parent = parent; /* can be NULL */
91         return (PyObject *)group;
92 }
93
94 static PyObject *idprop_py_from_idp_id(IDProperty *prop)
95 {
96         return pyrna_id_CreatePyObject(prop->data.pointer);
97 }
98
99 static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop)
100 {
101         BPy_IDProperty *array = PyObject_New(BPy_IDProperty, &BPy_IDArray_Type);
102         array->id = id;
103         array->prop = prop;
104         return (PyObject *)array;
105 }
106
107 static PyObject *idprop_py_from_idp_idparray(ID *id, IDProperty *prop)
108 {
109         PyObject *seq = PyList_New(prop->len);
110         IDProperty *array = IDP_IDPArray(prop);
111         int i;
112
113         if (!seq) {
114                 PyErr_Format(PyExc_RuntimeError,
115                              "%s: IDP_IDPARRAY: PyList_New(%d) failed",
116                              __func__, prop->len);
117                 return NULL;
118         }
119
120         for (i = 0; i < prop->len; i++) {
121                 PyObject *wrap = BPy_IDGroup_WrapData(id, array++, prop);
122
123                 /* BPy_IDGroup_MapDataToPy sets the error */
124                 if (UNLIKELY(wrap == NULL)) {
125                         Py_DECREF(seq);
126                         return NULL;
127                 }
128
129                 PyList_SET_ITEM(seq, i, wrap);
130         }
131
132         return seq;
133 }
134
135 /* -------------------------------------------------------------------------- */
136
137 /* use for both array and group */
138 static Py_hash_t BPy_IDGroup_hash(BPy_IDProperty *self)
139 {
140         return _Py_HashPointer(self->prop);
141 }
142
143 static PyObject *BPy_IDGroup_repr(BPy_IDProperty *self)
144 {
145         return PyUnicode_FromFormat("<bpy id prop: owner=\"%s\", name=\"%s\", address=%p>",
146                                     self->id ? self->id->name : "<NONE>", self->prop->name, self->prop);
147 }
148
149 PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent)
150 {
151         switch (prop->type) {
152                 case IDP_STRING:   return idprop_py_from_idp_string(prop);
153                 case IDP_INT:      return idprop_py_from_idp_int(prop);
154                 case IDP_FLOAT:    return idprop_py_from_idp_float(prop);
155                 case IDP_DOUBLE:   return idprop_py_from_idp_double(prop);
156                 case IDP_GROUP:    return idprop_py_from_idp_group(id, prop, parent);
157                 case IDP_ARRAY:    return idprop_py_from_idp_array(id, prop);
158                 case IDP_IDPARRAY: return idprop_py_from_idp_idparray(id, prop); /* this could be better a internal type */
159                 case IDP_ID:       return idprop_py_from_idp_id(prop);
160                 default: Py_RETURN_NONE;
161         }
162 }
163
164 #if 0 /* UNUSED, currently assignment overwrites into new properties, rather than setting in-place */
165 static int BPy_IDGroup_SetData(BPy_IDProperty *self, IDProperty *prop, PyObject *value)
166 {
167         switch (prop->type) {
168                 case IDP_STRING:
169                 {
170                         char *st;
171                         if (!PyUnicode_Check(value)) {
172                                 PyErr_SetString(PyExc_TypeError, "expected a string!");
173                                 return -1;
174                         }
175                         /* NOTE: if this code is enabled, bytes support needs to be added */
176 #ifdef USE_STRING_COERCE
177                         {
178                                 int alloc_len;
179                                 PyObject *value_coerce = NULL;
180
181                                 st = (char *)PyC_UnicodeAsByte(value, &value_coerce);
182                                 alloc_len = strlen(st) + 1;
183
184                                 st = _PyUnicode_AsString(value);
185                                 IDP_ResizeArray(prop, alloc_len);
186                                 memcpy(IDP_Array(prop), st, alloc_len);
187                                 Py_XDECREF(value_coerce);
188                         }
189 #else
190                         st = _PyUnicode_AsString(value);
191                         IDP_ResizeArray(prop, strlen(st) + 1);
192                         strcpy(IDP_Array(prop), st);
193 #endif
194
195                         return 0;
196                 }
197
198                 case IDP_INT:
199                 {
200                         int ivalue = PyLong_AsSsize_t(value);
201                         if (ivalue == -1 && PyErr_Occurred()) {
202                                 PyErr_SetString(PyExc_TypeError, "expected an int type");
203                                 return -1;
204                         }
205                         IDP_Int(prop) = ivalue;
206                         break;
207                 }
208                 case IDP_FLOAT:
209                 {
210                         float fvalue = (float)PyFloat_AsDouble(value);
211                         if (fvalue == -1 && PyErr_Occurred()) {
212                                 PyErr_SetString(PyExc_TypeError, "expected a float");
213                                 return -1;
214                         }
215                         IDP_Float(self->prop) = fvalue;
216                         break;
217                 }
218                 case IDP_DOUBLE:
219                 {
220                         double dvalue = PyFloat_AsDouble(value);
221                         if (dvalue == -1 && PyErr_Occurred()) {
222                                 PyErr_SetString(PyExc_TypeError, "expected a float");
223                                 return -1;
224                         }
225                         IDP_Double(self->prop) = dvalue;
226                         break;
227                 }
228                 default:
229                         PyErr_SetString(PyExc_AttributeError, "attempt to set read-only attribute!");
230                         return -1;
231         }
232         return 0;
233 }
234 #endif
235
236 static PyObject *BPy_IDGroup_GetName(BPy_IDProperty *self, void *UNUSED(closure))
237 {
238         return PyUnicode_FromString(self->prop->name);
239 }
240
241 static int BPy_IDGroup_SetName(BPy_IDProperty *self, PyObject *value, void *UNUSED(closure))
242 {
243         const char *name;
244         Py_ssize_t name_size;
245
246         if (!PyUnicode_Check(value)) {
247                 PyErr_SetString(PyExc_TypeError, "expected a string!");
248                 return -1;
249         }
250
251         name = _PyUnicode_AsStringAndSize(value, &name_size);
252
253         if (name_size > MAX_IDPROP_NAME) {
254                 PyErr_SetString(PyExc_TypeError, "string length cannot exceed 63 characters!");
255                 return -1;
256         }
257
258         memcpy(self->prop->name, name, name_size);
259         return 0;
260 }
261
262 #if 0
263 static PyObject *BPy_IDGroup_GetType(BPy_IDProperty *self)
264 {
265         return PyLong_FromLong(self->prop->type);
266 }
267 #endif
268
269 static PyGetSetDef BPy_IDGroup_getseters[] = {
270         {(char *)"name", (getter)BPy_IDGroup_GetName, (setter)BPy_IDGroup_SetName, (char *)"The name of this Group.", NULL},
271         {NULL, NULL, NULL, NULL, NULL}
272 };
273
274 static Py_ssize_t BPy_IDGroup_Map_Len(BPy_IDProperty *self)
275 {
276         if (self->prop->type != IDP_GROUP) {
277                 PyErr_SetString(PyExc_TypeError, "len() of unsized object");
278                 return -1;
279         }
280
281         return self->prop->len;
282 }
283
284 static PyObject *BPy_IDGroup_Map_GetItem(BPy_IDProperty *self, PyObject *item)
285 {
286         IDProperty *idprop;
287         const char *name;
288
289         if (self->prop->type != IDP_GROUP) {
290                 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
291                 return NULL;
292         }
293
294         name = _PyUnicode_AsString(item);
295
296         if (name == NULL) {
297                 PyErr_SetString(PyExc_TypeError, "only strings are allowed as keys of ID properties");
298                 return NULL;
299         }
300
301         idprop = IDP_GetPropertyFromGroup(self->prop, name);
302
303         if (idprop == NULL) {
304                 PyErr_SetString(PyExc_KeyError, "key not in subgroup dict");
305                 return NULL;
306         }
307
308         return BPy_IDGroup_WrapData(self->id, idprop, self->prop);
309 }
310
311 /* returns NULL on success, error string on failure */
312 static char idp_sequence_type(PyObject *seq_fast)
313 {
314         PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
315         PyObject *item;
316         char type = IDP_INT;
317
318         Py_ssize_t i, len = PySequence_Fast_GET_SIZE(seq_fast);
319
320         for (i = 0; i < len; i++) {
321                 item = seq_fast_items[i];
322                 if (PyFloat_Check(item)) {
323                         if (type == IDP_IDPARRAY) { /* mixed dict/int */
324                                 return -1;
325                         }
326                         type = IDP_DOUBLE;
327                 }
328                 else if (PyLong_Check(item)) {
329                         if (type == IDP_IDPARRAY) { /* mixed dict/int */
330                                 return -1;
331                         }
332                 }
333                 else if (PyMapping_Check(item)) {
334                         if (i != 0 && (type != IDP_IDPARRAY)) { /* mixed dict/int */
335                                 return -1;
336                         }
337                         type = IDP_IDPARRAY;
338                 }
339                 else {
340                         return -1;
341                 }
342         }
343
344         return type;
345 }
346
347 static const char *idp_try_read_name(PyObject *name_obj)
348 {
349         const char *name = NULL;
350         if (name_obj) {
351                 Py_ssize_t name_size;
352                 name = _PyUnicode_AsStringAndSize(name_obj, &name_size);
353
354                 if (name == NULL) {
355                         PyErr_Format(PyExc_KeyError,
356                                      "invalid id-property key, expected a string, not a %.200s",
357                                      Py_TYPE(name_obj)->tp_name);
358                         return NULL;
359                 }
360
361                 if (name_size > MAX_IDPROP_NAME) {
362                         PyErr_SetString(PyExc_KeyError, "the length of IDProperty names is limited to 63 characters");
363                         return NULL;
364                 }
365         }
366         else {
367                 name = "";
368         }
369         return name;
370 }
371
372 /* -------------------------------------------------------------------------- */
373
374 /**
375  * The 'idp_from_Py*' functions expect that the input type has been checked before
376  * and return NULL if the IDProperty can't be created.
377  */
378
379 static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob)
380 {
381         IDPropertyTemplate val = {0};
382         val.d = PyFloat_AsDouble(ob);
383         return IDP_New(IDP_DOUBLE, &val, name);
384 }
385
386 static IDProperty *idp_from_PyLong(const char *name, PyObject *ob)
387 {
388         IDPropertyTemplate val = {0};
389         val.i = _PyLong_AsInt(ob);
390         if (val.i == -1 && PyErr_Occurred()) {
391                 return NULL;
392         }
393         return IDP_New(IDP_INT, &val, name);
394 }
395
396 static IDProperty *idp_from_PyUnicode(const char *name, PyObject *ob)
397 {
398         IDProperty *prop;
399         IDPropertyTemplate val = {0};
400 #ifdef USE_STRING_COERCE
401         Py_ssize_t value_size;
402         PyObject *value_coerce = NULL;
403         val.string.str = PyC_UnicodeAsByteAndSize(ob, &value_size, &value_coerce);
404         val.string.len = (int)value_size + 1;
405         val.string.subtype = IDP_STRING_SUB_UTF8;
406         prop = IDP_New(IDP_STRING, &val, name);
407         Py_XDECREF(value_coerce);
408 #else
409         val.str = _PyUnicode_AsString(ob);
410         prop = IDP_New(IDP_STRING, val, name);
411 #endif
412         return prop;
413 }
414
415 static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob)
416 {
417         IDPropertyTemplate val = {0};
418         val.string.str = PyBytes_AS_STRING(ob);
419         val.string.len = PyBytes_GET_SIZE(ob);
420         val.string.subtype = IDP_STRING_SUB_BYTE;
421         return IDP_New(IDP_STRING, &val, name);
422 }
423
424 static int idp_array_type_from_format_char(char format)
425 {
426         if (format == 'i') return IDP_INT;
427         if (format == 'f') return IDP_FLOAT;
428         if (format == 'd') return IDP_DOUBLE;
429         return -1;
430 }
431
432 static const char *idp_format_from_array_type(int type)
433 {
434         if (type == IDP_INT) return "i";
435         if (type == IDP_FLOAT) return "f";
436         if (type == IDP_DOUBLE) return "d";
437         return NULL;
438 }
439
440 static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffer)
441 {
442         IDProperty *prop;
443         IDPropertyTemplate val = {0};
444
445         int format = idp_array_type_from_format_char(*buffer->format);
446         if (format == -1) {
447                 /* should never happen as the type has been checked before */
448                 return NULL;
449         }
450         else {
451                 val.array.type = format;
452                 val.array.len = buffer->len / buffer->itemsize;
453         }
454         prop = IDP_New(IDP_ARRAY, &val, name);
455         memcpy(IDP_Array(prop), buffer->buf, buffer->len);
456         return prop;
457 }
458
459 static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob)
460 {
461         IDProperty *prop;
462         IDPropertyTemplate val = {0};
463
464         PyObject **ob_seq_fast_items;
465         PyObject *item;
466         int i;
467
468         ob_seq_fast_items = PySequence_Fast_ITEMS(ob);
469
470         if ((val.array.type = idp_sequence_type(ob)) == (char)-1) {
471                 PyErr_SetString(PyExc_TypeError, "only floats, ints and dicts are allowed in ID property arrays");
472                 return NULL;
473         }
474
475         /* validate sequence and derive type.
476          * we assume IDP_INT unless we hit a float
477          * number; then we assume it's */
478
479         val.array.len = PySequence_Fast_GET_SIZE(ob);
480
481         switch (val.array.type) {
482                 case IDP_DOUBLE:
483                 {
484                         double *prop_data;
485                         prop = IDP_New(IDP_ARRAY, &val, name);
486                         prop_data = IDP_Array(prop);
487                         for (i = 0; i < val.array.len; i++) {
488                                 item = ob_seq_fast_items[i];
489                                 if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) {
490                                         return NULL;
491                                 }
492                         }
493                         break;
494                 }
495                 case IDP_INT:
496                 {
497                         int *prop_data;
498                         prop = IDP_New(IDP_ARRAY, &val, name);
499                         prop_data = IDP_Array(prop);
500                         for (i = 0; i < val.array.len; i++) {
501                                 item = ob_seq_fast_items[i];
502                                 if (((prop_data[i] = _PyLong_AsInt(item)) == -1) && PyErr_Occurred()) {
503                                         return NULL;
504                                 }
505                         }
506                         break;
507                 }
508                 case IDP_IDPARRAY:
509                 {
510                         prop = IDP_NewIDPArray(name);
511                         for (i = 0; i < val.array.len; i++) {
512                                 item = ob_seq_fast_items[i];
513                                 if (BPy_IDProperty_Map_ValidateAndCreate(NULL, prop, item) == false) {
514                                         return NULL;
515                                 }
516                         }
517                         break;
518                 }
519                 default:
520                         /* should never happen */
521                         PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
522                         return NULL;
523         }
524         return prop;
525 }
526
527
528 static IDProperty *idp_from_PySequence(const char *name, PyObject *ob)
529 {
530         Py_buffer buffer;
531         bool use_buffer = false;
532
533         if (PyObject_CheckBuffer(ob)) {
534                 PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT);
535                 char format = *buffer.format;
536                 if (ELEM(format, 'i', 'f', 'd')) {
537                         use_buffer = true;
538                 }
539                 else {
540                         PyBuffer_Release(&buffer);
541                 }
542         }
543
544         if (use_buffer) {
545                 IDProperty *prop = idp_from_PySequence_Buffer(name, &buffer);
546                 PyBuffer_Release(&buffer);
547                 return prop;
548         }
549         else {
550                 PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop");
551                 if (ob_seq_fast != NULL) {
552                         IDProperty *prop = idp_from_PySequence_Fast(name, ob_seq_fast);
553                         Py_DECREF(ob_seq_fast);
554                         return prop;
555                 }
556                 else {
557                         return NULL;
558                 }
559         }
560 }
561
562 static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob)
563 {
564         IDProperty *prop;
565         IDPropertyTemplate val = {0};
566
567         PyObject *keys, *vals, *key, *pval;
568         int i, len;
569         /* yay! we get into recursive stuff now! */
570         keys = PyMapping_Keys(ob);
571         vals = PyMapping_Values(ob);
572
573         /* we allocate the group first; if we hit any invalid data,
574          * we can delete it easily enough.*/
575         prop = IDP_New(IDP_GROUP, &val, name);
576         len = PyMapping_Length(ob);
577         for (i = 0; i < len; i++) {
578                 key = PySequence_GetItem(keys, i);
579                 pval = PySequence_GetItem(vals, i);
580                 if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) {
581                         IDP_FreeProperty(prop);
582                         MEM_freeN(prop);
583                         Py_XDECREF(keys);
584                         Py_XDECREF(vals);
585                         Py_XDECREF(key);
586                         Py_XDECREF(pval);
587                         /* error is already set */
588                         return NULL;
589                 }
590                 Py_XDECREF(key);
591                 Py_XDECREF(pval);
592         }
593         Py_XDECREF(keys);
594         Py_XDECREF(vals);
595         return prop;
596 }
597
598 static IDProperty *idp_from_DatablockPointer(const char *name, PyObject *ob, IDPropertyTemplate *val)
599 {
600         pyrna_id_FromPyObject(ob, &val->id);
601         return IDP_New(IDP_ID, val, name);
602 }
603
604 static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob)
605 {
606         IDPropertyTemplate val = {0};
607         const char *name = idp_try_read_name(name_obj);
608         if (name == NULL) {
609                 return NULL;
610         }
611
612         if (PyFloat_Check(ob)) {
613                 return idp_from_PyFloat(name, ob);
614         }
615         else if (PyLong_Check(ob)) {
616                 return idp_from_PyLong(name, ob);
617         }
618         else if (PyUnicode_Check(ob)) {
619                 return idp_from_PyUnicode(name, ob);
620         }
621         else if (PyBytes_Check(ob)) {
622                 return idp_from_PyBytes(name, ob);
623         }
624         else if (PySequence_Check(ob)) {
625                 return idp_from_PySequence(name, ob);
626         }
627         else if (ob == Py_None || pyrna_id_CheckPyObject(ob)) {
628                 return idp_from_DatablockPointer(name, ob, &val);
629         }
630         else if (PyMapping_Check(ob)) {
631                 return idp_from_PyMapping(name, ob);
632         }
633         else {
634                 PyErr_Format(PyExc_TypeError,
635                              "invalid id-property type %.200s not supported",
636                              Py_TYPE(ob)->tp_name);
637                 return NULL;
638         }
639 }
640
641 /* -------------------------------------------------------------------------- */
642
643 /**
644  * \note group can be a pointer array or a group.
645  * assume we already checked key is a string.
646  *
647  * \return success.
648  */
649 bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob)
650 {
651         IDProperty *prop = idp_from_PyObject(name_obj, ob);
652         if (prop == NULL) {
653                 return false;
654         }
655
656         if (group->type == IDP_IDPARRAY) {
657                 IDP_AppendArray(group, prop);
658                 /* IDP_AppendArray does a shallow copy (memcpy), only free memory */
659                 MEM_freeN(prop);
660         }
661         else {
662                 IDProperty *prop_exist;
663
664                 /* avoid freeing when types match in case they are referenced by the UI, see: T37073
665                  * obviously this isn't a complete solution, but helps for common cases. */
666                 prop_exist = IDP_GetPropertyFromGroup(group, prop->name);
667                 if ((prop_exist != NULL) &&
668                     (prop_exist->type == prop->type) &&
669                     (prop_exist->subtype == prop->subtype))
670                 {
671                         /* Preserve prev/next links!!! See T42593. */
672                         prop->prev = prop_exist->prev;
673                         prop->next = prop_exist->next;
674
675                         IDP_FreeProperty(prop_exist);
676                         *prop_exist = *prop;
677                         MEM_freeN(prop);
678                 }
679                 else {
680                         IDP_ReplaceInGroup_ex(group, prop, prop_exist);
681                 }
682         }
683
684         return true;
685 }
686
687 int BPy_Wrap_SetMapItem(IDProperty *prop, PyObject *key, PyObject *val)
688 {
689         if (prop->type != IDP_GROUP) {
690                 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
691                 return -1;
692         }
693
694         if (val == NULL) { /* del idprop[key] */
695                 IDProperty *pkey;
696                 const char *name = _PyUnicode_AsString(key);
697
698                 if (name == NULL) {
699                         PyErr_Format(PyExc_KeyError,
700                                      "expected a string, not %.200s",
701                                      Py_TYPE(key)->tp_name);
702                         return -1;
703                 }
704
705                 pkey = IDP_GetPropertyFromGroup(prop, name);
706                 if (pkey) {
707                         IDP_FreeFromGroup(prop, pkey);
708                         return 0;
709                 }
710                 else {
711                         PyErr_SetString(PyExc_KeyError, "property not found in group");
712                         return -1;
713                 }
714         }
715         else {
716                 bool ok;
717
718                 ok = BPy_IDProperty_Map_ValidateAndCreate(key, prop, val);
719                 if (ok == false) {
720                         return -1;
721                 }
722
723                 return 0;
724         }
725 }
726
727 static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val)
728 {
729         return BPy_Wrap_SetMapItem(self->prop, key, val);
730 }
731
732 static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self)
733 {
734         BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
735         iter->group = self;
736         iter->mode = IDPROP_ITER_KEYS;
737         iter->cur = self->prop->data.group.first;
738         Py_XINCREF(iter);
739         return (PyObject *)iter;
740 }
741
742 /* for simple, non nested types this is the same as BPy_IDGroup_WrapData */
743 static PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
744 {
745         switch (prop->type) {
746                 case IDP_STRING:
747                         return idprop_py_from_idp_string(prop);
748                 case IDP_INT:
749                         return idprop_py_from_idp_int(prop);
750                 case IDP_FLOAT:
751                         return idprop_py_from_idp_float(prop);
752                 case IDP_DOUBLE:
753                         return idprop_py_from_idp_double(prop);
754                 case IDP_ID:
755                         return idprop_py_from_idp_id(prop);
756                 case IDP_ARRAY:
757                 {
758                         PyObject *seq = PyList_New(prop->len);
759                         int i;
760
761                         if (!seq) {
762                                 PyErr_Format(PyExc_RuntimeError,
763                                              "%s: IDP_ARRAY: PyList_New(%d) failed",
764                                              __func__, prop->len);
765                                 return NULL;
766                         }
767
768                         switch (prop->subtype) {
769                                 case IDP_FLOAT:
770                                 {
771                                         const float *array = (float *)IDP_Array(prop);
772                                         for (i = 0; i < prop->len; i++) {
773                                                 PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i]));
774                                         }
775                                         break;
776                                 }
777                                 case IDP_DOUBLE:
778                                 {
779                                         const double *array = (double *)IDP_Array(prop);
780                                         for (i = 0; i < prop->len; i++) {
781                                                 PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i]));
782                                         }
783                                         break;
784                                 }
785                                 case IDP_INT:
786                                 {
787                                         const int *array = (int *)IDP_Array(prop);
788                                         for (i = 0; i < prop->len; i++) {
789                                                 PyList_SET_ITEM(seq, i, PyLong_FromLong(array[i]));
790                                         }
791                                         break;
792                                 }
793                                 default:
794                                         PyErr_Format(PyExc_RuntimeError,
795                                                      "%s: invalid/corrupt array type '%d'!",
796                                                      __func__, prop->subtype);
797                                         Py_DECREF(seq);
798                                         return NULL;
799                         }
800
801                         return seq;
802                 }
803                 case IDP_IDPARRAY:
804                 {
805                         PyObject *seq = PyList_New(prop->len);
806                         IDProperty *array = IDP_IDPArray(prop);
807                         int i;
808
809                         if (!seq) {
810                                 PyErr_Format(PyExc_RuntimeError,
811                                              "%s: IDP_IDPARRAY: PyList_New(%d) failed",
812                                              __func__, prop->len);
813                                 return NULL;
814                         }
815
816                         for (i = 0; i < prop->len; i++) {
817                                 PyObject *wrap = BPy_IDGroup_MapDataToPy(array++);
818
819                                 /* BPy_IDGroup_MapDataToPy sets the error */
820                                 if (UNLIKELY(wrap == NULL)) {
821                                         Py_DECREF(seq);
822                                         return NULL;
823                                 }
824
825                                 PyList_SET_ITEM(seq, i, wrap);
826                         }
827                         return seq;
828                 }
829                 case IDP_GROUP:
830                 {
831                         PyObject *dict = _PyDict_NewPresized(prop->len);
832                         IDProperty *loop;
833
834                         for (loop = prop->data.group.first; loop; loop = loop->next) {
835                                 PyObject *wrap = BPy_IDGroup_MapDataToPy(loop);
836
837                                 /* BPy_IDGroup_MapDataToPy sets the error */
838                                 if (UNLIKELY(wrap == NULL)) {
839                                         Py_DECREF(dict);
840                                         return NULL;
841                                 }
842
843                                 PyDict_SetItemString(dict, loop->name, wrap);
844                                 Py_DECREF(wrap);
845                         }
846                         return dict;
847                 }
848         }
849
850         PyErr_Format(PyExc_RuntimeError,
851                      "%s ERROR: '%s' property exists with a bad type code '%d'!",
852                      __func__, prop->name, prop->type);
853         return NULL;
854 }
855
856 PyDoc_STRVAR(BPy_IDGroup_pop_doc,
857 ".. method:: pop(key)\n"
858 "\n"
859 "   Remove an item from the group, returning a Python representation.\n"
860 "\n"
861 "   :raises KeyError: When the item doesn't exist.\n"
862 "\n"
863 "   :arg key: Name of item to remove.\n"
864 "   :type key: string\n"
865 );
866 static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *value)
867 {
868         IDProperty *idprop;
869         PyObject *pyform;
870         const char *name = _PyUnicode_AsString(value);
871
872         if (!name) {
873                 PyErr_Format(PyExc_TypeError,
874                              "pop expected at least a string argument, not %.200s",
875                              Py_TYPE(value)->tp_name);
876                 return NULL;
877         }
878
879         idprop = IDP_GetPropertyFromGroup(self->prop, name);
880
881         if (idprop) {
882                 pyform = BPy_IDGroup_MapDataToPy(idprop);
883
884                 if (!pyform) {
885                         /* ok something bad happened with the pyobject,
886                          * so don't remove the prop from the group.  if pyform is
887                          * NULL, then it already should have raised an exception.*/
888                         return NULL;
889                 }
890
891                 IDP_RemoveFromGroup(self->prop, idprop);
892                 return pyform;
893         }
894
895         PyErr_SetString(PyExc_KeyError, "item not in group");
896         return NULL;
897 }
898
899 PyDoc_STRVAR(BPy_IDGroup_iter_items_doc,
900 ".. method:: iteritems()\n"
901 "\n"
902 "   Iterate through the items in the dict; behaves like dictionary method iteritems.\n"
903 );
904 static PyObject *BPy_IDGroup_iter_items(BPy_IDProperty *self)
905 {
906         BPy_IDGroup_Iter *iter = PyObject_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
907         iter->group = self;
908         iter->mode = IDPROP_ITER_ITEMS;
909         iter->cur = self->prop->data.group.first;
910         Py_XINCREF(iter);
911         return (PyObject *)iter;
912 }
913
914 /* utility function */
915 static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
916 {
917         int j;
918
919         printf("%s: ID Property Error found and corrected!\n", func);
920
921         /* fill rest of list with valid references to None */
922         for (j = len; j < prop->len; j++) {
923                 PyList_SET_ITEM(seq, j, Py_INCREF_RET(Py_None));
924         }
925
926         /*set correct group length*/
927         prop->len = len;
928 }
929
930 PyObject *BPy_Wrap_GetKeys(IDProperty *prop)
931 {
932         PyObject *list = PyList_New(prop->len);
933         IDProperty *loop;
934         int i;
935
936         for (i = 0, loop = prop->data.group.first; loop && (i < prop->len); loop = loop->next, i++)
937                 PyList_SET_ITEM(list, i, PyUnicode_FromString(loop->name));
938
939         /* if the id prop is corrupt, count the remaining */
940         for ( ; loop; loop = loop->next, i++) {
941                 /* pass */
942         }
943
944         if (i != prop->len) { /* if the loop didnt finish, we know the length is wrong */
945                 BPy_IDGroup_CorrectListLen(prop, list, i, __func__);
946                 Py_DECREF(list); /*free the list*/
947                 /*call self again*/
948                 return BPy_Wrap_GetKeys(prop);
949         }
950
951         return list;
952 }
953
954 PyObject *BPy_Wrap_GetValues(ID *id, IDProperty *prop)
955 {
956         PyObject *list = PyList_New(prop->len);
957         IDProperty *loop;
958         int i;
959
960         for (i = 0, loop = prop->data.group.first; loop; loop = loop->next, i++) {
961                 PyList_SET_ITEM(list, i, BPy_IDGroup_WrapData(id, loop, prop));
962         }
963
964         if (i != prop->len) {
965                 BPy_IDGroup_CorrectListLen(prop, list, i, __func__);
966                 Py_DECREF(list); /*free the list*/
967                 /*call self again*/
968                 return BPy_Wrap_GetValues(id, prop);
969         }
970
971         return list;
972 }
973
974 PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
975 {
976         PyObject *seq = PyList_New(prop->len);
977         IDProperty *loop;
978         int i;
979
980         for (i = 0, loop = prop->data.group.first; loop; loop = loop->next, i++) {
981                 PyObject *item = PyTuple_New(2);
982                 PyTuple_SET_ITEMS(item,
983                         PyUnicode_FromString(loop->name),
984                         BPy_IDGroup_WrapData(id, loop, prop));
985                 PyList_SET_ITEM(seq, i, item);
986         }
987
988         if (i != prop->len) {
989                 BPy_IDGroup_CorrectListLen(prop, seq, i, __func__);
990                 Py_DECREF(seq); /*free the list*/
991                 /*call self again*/
992                 return BPy_Wrap_GetItems(id, prop);
993         }
994
995         return seq;
996 }
997
998 PyDoc_STRVAR(BPy_IDGroup_keys_doc,
999 ".. method:: keys()\n"
1000 "\n"
1001 "   Return the keys associated with this group as a list of strings.\n"
1002 );
1003 static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
1004 {
1005         return BPy_Wrap_GetKeys(self->prop);
1006 }
1007
1008 PyDoc_STRVAR(BPy_IDGroup_values_doc,
1009 ".. method:: values()\n"
1010 "\n"
1011 "   Return the values associated with this group.\n"
1012 );
1013 static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
1014 {
1015         return BPy_Wrap_GetValues(self->id, self->prop);
1016 }
1017
1018 PyDoc_STRVAR(BPy_IDGroup_items_doc,
1019 ".. method:: items()\n"
1020 "\n"
1021 "   Return the items associated with this group.\n"
1022 );
1023 static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
1024 {
1025         return BPy_Wrap_GetItems(self->id, self->prop);
1026 }
1027
1028 static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
1029 {
1030         const char *name = _PyUnicode_AsString(value);
1031
1032         if (!name) {
1033                 PyErr_Format(PyExc_TypeError,
1034                              "expected a string, not a %.200s",
1035                              Py_TYPE(value)->tp_name);
1036                 return -1;
1037         }
1038
1039         return IDP_GetPropertyFromGroup(self->prop, name) ? 1 : 0;
1040 }
1041
1042 PyDoc_STRVAR(BPy_IDGroup_update_doc,
1043 ".. method:: update(other)\n"
1044 "\n"
1045 "   Update key, values.\n"
1046 "\n"
1047 "   :arg other: Updates the values in the group with this.\n"
1048 "   :type other: :class:`IDPropertyGroup` or dict\n"
1049 );
1050 static PyObject *BPy_IDGroup_update(BPy_IDProperty *self, PyObject *value)
1051 {
1052         PyObject *pkey, *pval;
1053         Py_ssize_t i = 0;
1054
1055         if (BPy_IDGroup_Check(value)) {
1056                 BPy_IDProperty *other = (BPy_IDProperty *)value;
1057                 if (UNLIKELY(self->prop == other->prop)) {
1058                         Py_RETURN_NONE;
1059                 }
1060
1061                 /* XXX, possible one is inside the other */
1062                 IDP_MergeGroup(self->prop, other->prop, true);
1063         }
1064         else if (PyDict_Check(value)) {
1065                 while (PyDict_Next(value, &i, &pkey, &pval)) {
1066                         BPy_IDGroup_Map_SetItem(self, pkey, pval);
1067                         if (PyErr_Occurred()) return NULL;
1068                 }
1069         }
1070         else {
1071                 PyErr_Format(PyExc_TypeError,
1072                              "expected a dict or an IDPropertyGroup type, not a %.200s",
1073                              Py_TYPE(value)->tp_name);
1074                 return NULL;
1075         }
1076
1077
1078         Py_RETURN_NONE;
1079 }
1080
1081 PyDoc_STRVAR(BPy_IDGroup_to_dict_doc,
1082 ".. method:: to_dict()\n"
1083 "\n"
1084 "   Return a purely python version of the group.\n"
1085 );
1086 static PyObject *BPy_IDGroup_to_dict(BPy_IDProperty *self)
1087 {
1088         return BPy_IDGroup_MapDataToPy(self->prop);
1089 }
1090
1091 PyDoc_STRVAR(BPy_IDGroup_clear_doc,
1092 ".. method:: clear()\n"
1093 "\n"
1094 "   Clear all members from this group.\n"
1095 );
1096 static PyObject *BPy_IDGroup_clear(BPy_IDProperty *self)
1097 {
1098         IDP_ClearProperty(self->prop);
1099         Py_RETURN_NONE;
1100 }
1101
1102 PyDoc_STRVAR(BPy_IDGroup_get_doc,
1103 ".. method:: get(key, default=None)\n"
1104 "\n"
1105 "   Return the value for key, if it exists, else default.\n"
1106 );
1107 static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
1108 {
1109         IDProperty *idprop;
1110         const char *key;
1111         PyObject *def = Py_None;
1112
1113         if (!PyArg_ParseTuple(args, "s|O:get", &key, &def))
1114                 return NULL;
1115
1116         idprop = IDP_GetPropertyFromGroup(self->prop, key);
1117         if (idprop) {
1118                 PyObject *pyobj = BPy_IDGroup_WrapData(self->id, idprop, self->prop);
1119                 if (pyobj)
1120                         return pyobj;
1121         }
1122
1123         Py_INCREF(def);
1124         return def;
1125 }
1126
1127 static struct PyMethodDef BPy_IDGroup_methods[] = {
1128         {"pop", (PyCFunction)BPy_IDGroup_pop, METH_O, BPy_IDGroup_pop_doc},
1129         {"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
1130         {"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
1131         {"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
1132         {"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
1133         {"update", (PyCFunction)BPy_IDGroup_update, METH_O, BPy_IDGroup_update_doc},
1134         {"get", (PyCFunction)BPy_IDGroup_get, METH_VARARGS, BPy_IDGroup_get_doc},
1135         {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS, BPy_IDGroup_to_dict_doc},
1136         {"clear", (PyCFunction)BPy_IDGroup_clear, METH_NOARGS, BPy_IDGroup_clear_doc},
1137         {NULL, NULL, 0, NULL}
1138 };
1139
1140 static PySequenceMethods BPy_IDGroup_Seq = {
1141         (lenfunc) BPy_IDGroup_Map_Len,      /* lenfunc sq_length */
1142         NULL,                               /* binaryfunc sq_concat */
1143         NULL,                               /* ssizeargfunc sq_repeat */
1144         NULL,                               /* ssizeargfunc sq_item */ /* TODO - setting this will allow PySequence_Check to return True */
1145         NULL,                               /* intintargfunc ***was_sq_slice*** */
1146         NULL,                               /* intobjargproc sq_ass_item */
1147         NULL,                               /* ssizeobjargproc ***was_sq_ass_slice*** */
1148         (objobjproc) BPy_IDGroup_Contains,  /* objobjproc sq_contains */
1149         NULL,                               /* binaryfunc sq_inplace_concat */
1150         NULL,                               /* ssizeargfunc sq_inplace_repeat */
1151 };
1152
1153 static PyMappingMethods BPy_IDGroup_Mapping = {
1154         (lenfunc)BPy_IDGroup_Map_Len,           /*inquiry mp_length */
1155         (binaryfunc)BPy_IDGroup_Map_GetItem,    /*binaryfunc mp_subscript */
1156         (objobjargproc)BPy_IDGroup_Map_SetItem, /*objobjargproc mp_ass_subscript */
1157 };
1158
1159 PyTypeObject BPy_IDGroup_Type = {
1160         PyVarObject_HEAD_INIT(NULL, 0)
1161         /*  For printing, in format "<module>.<name>" */
1162         "IDPropertyGroup",       /* char *tp_name; */
1163         sizeof(BPy_IDProperty),     /* int tp_basicsize; */
1164         0,                          /* tp_itemsize;  For allocation */
1165
1166         /* Methods to implement standard operations */
1167
1168         NULL,                       /* destructor tp_dealloc; */
1169         NULL,                       /* printfunc tp_print; */
1170         NULL,                       /* getattrfunc tp_getattr; */
1171         NULL,                       /* setattrfunc tp_setattr; */
1172         NULL,                       /* cmpfunc tp_compare; */
1173         (reprfunc)BPy_IDGroup_repr,     /* reprfunc tp_repr; */
1174
1175         /* Method suites for standard classes */
1176
1177         NULL,                       /* PyNumberMethods *tp_as_number; */
1178         &BPy_IDGroup_Seq,           /* PySequenceMethods *tp_as_sequence; */
1179         &BPy_IDGroup_Mapping,       /* PyMappingMethods *tp_as_mapping; */
1180
1181         /* More standard operations (here for binary compatibility) */
1182
1183         (hashfunc)BPy_IDGroup_hash, /* hashfunc tp_hash; */
1184         NULL,                       /* ternaryfunc tp_call; */
1185         NULL,                       /* reprfunc tp_str; */
1186         NULL,                       /* getattrofunc tp_getattro; */
1187         NULL,                       /* setattrofunc tp_setattro; */
1188
1189         /* Functions to access object as input/output buffer */
1190         NULL,                       /* PyBufferProcs *tp_as_buffer; */
1191
1192         /*** Flags to define presence of optional/expanded features ***/
1193         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
1194
1195         NULL,                       /*  char *tp_doc;  Documentation string */
1196         /*** Assigned meaning in release 2.0 ***/
1197         /* call function for all accessible objects */
1198         NULL,                       /* traverseproc tp_traverse; */
1199
1200         /* delete references to contained objects */
1201         NULL,                       /* inquiry tp_clear; */
1202
1203         /***  Assigned meaning in release 2.1 ***/
1204         /*** rich comparisons ***/
1205         NULL,                       /* richcmpfunc tp_richcompare; */
1206
1207         /***  weak reference enabler ***/
1208         0,                          /* long tp_weaklistoffset; */
1209
1210         /*** Added in release 2.2 ***/
1211         /*   Iterators */
1212         (getiterfunc)BPy_IDGroup_iter, /* getiterfunc tp_iter; */
1213         NULL,                       /* iternextfunc tp_iternext; */
1214         /*** Attribute descriptor and subclassing stuff ***/
1215         BPy_IDGroup_methods,        /* struct PyMethodDef *tp_methods; */
1216         NULL,                       /* struct PyMemberDef *tp_members; */
1217         BPy_IDGroup_getseters,       /* struct PyGetSetDef *tp_getset; */
1218 };
1219
1220 /********Array Wrapper********/
1221
1222 static PyTypeObject *idp_array_py_type(BPy_IDArray *self, bool *r_is_double)
1223 {
1224         switch (self->prop->subtype) {
1225                 case IDP_FLOAT:
1226                         *r_is_double = false;
1227                         return &PyFloat_Type;
1228                 case IDP_DOUBLE:
1229                         *r_is_double = true;
1230                         return &PyFloat_Type;
1231                 case IDP_INT:
1232                         *r_is_double = false;
1233                         return &PyLong_Type;
1234                 default:
1235                         *r_is_double = false;
1236                         return NULL;
1237         }
1238 }
1239
1240 static PyObject *BPy_IDArray_repr(BPy_IDArray *self)
1241 {
1242         return PyUnicode_FromFormat("<bpy id property array [%d]>", self->prop->len);
1243 }
1244
1245 PyDoc_STRVAR(BPy_IDArray_get_typecode_doc,
1246 "The type of the data in the array {'f': float, 'd': double, 'i': int}."
1247 );
1248 static PyObject *BPy_IDArray_get_typecode(BPy_IDArray *self)
1249 {
1250         switch (self->prop->subtype) {
1251                 case IDP_FLOAT:  return PyUnicode_FromString("f");
1252                 case IDP_DOUBLE: return PyUnicode_FromString("d");
1253                 case IDP_INT:    return PyUnicode_FromString("i");
1254         }
1255
1256         PyErr_Format(PyExc_RuntimeError,
1257                      "%s: invalid/corrupt array type '%d'!",
1258                      __func__, self->prop->subtype);
1259
1260         return NULL;
1261 }
1262
1263 static PyGetSetDef BPy_IDArray_getseters[] = {
1264         /* matches pythons array.typecode */
1265         {(char *)"typecode", (getter)BPy_IDArray_get_typecode, (setter)NULL, BPy_IDArray_get_typecode_doc, NULL},
1266         {NULL, NULL, NULL, NULL, NULL},
1267 };
1268
1269 PyDoc_STRVAR(BPy_IDArray_to_list_doc,
1270 ".. method:: to_list()\n"
1271 "\n"
1272 "   Return the array as a list.\n"
1273 );
1274 static PyObject *BPy_IDArray_to_list(BPy_IDArray *self)
1275 {
1276         return BPy_IDGroup_MapDataToPy(self->prop);
1277 }
1278
1279 static PyMethodDef BPy_IDArray_methods[] = {
1280         {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS, BPy_IDArray_to_list_doc},
1281         {NULL, NULL, 0, NULL}
1282 };
1283
1284 static int BPy_IDArray_Len(BPy_IDArray *self)
1285 {
1286         return self->prop->len;
1287 }
1288
1289 static PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, int index)
1290 {
1291         if (index < 0 || index >= self->prop->len) {
1292                 PyErr_SetString(PyExc_IndexError, "index out of range!");
1293                 return NULL;
1294         }
1295
1296         switch (self->prop->subtype) {
1297                 case IDP_FLOAT:
1298                         return PyFloat_FromDouble(((float *)IDP_Array(self->prop))[index]);
1299                 case IDP_DOUBLE:
1300                         return PyFloat_FromDouble(((double *)IDP_Array(self->prop))[index]);
1301                 case IDP_INT:
1302                         return PyLong_FromLong((long)((int *)IDP_Array(self->prop))[index]);
1303         }
1304
1305         PyErr_Format(PyExc_RuntimeError,
1306                      "%s: invalid/corrupt array type '%d'!",
1307                      __func__, self->prop->subtype);
1308
1309         return NULL;
1310 }
1311
1312 static int BPy_IDArray_SetItem(BPy_IDArray *self, int index, PyObject *value)
1313 {
1314         if (index < 0 || index >= self->prop->len) {
1315                 PyErr_SetString(PyExc_RuntimeError, "index out of range!");
1316                 return -1;
1317         }
1318
1319         switch (self->prop->subtype) {
1320                 case IDP_FLOAT:
1321                 {
1322                         const float f = (float)PyFloat_AsDouble(value);
1323                         if (f == -1 && PyErr_Occurred()) {
1324                                 return -1;
1325                         }
1326                         ((float *)IDP_Array(self->prop))[index] = f;
1327                         break;
1328                 }
1329                 case IDP_DOUBLE:
1330                 {
1331                         const double d = PyFloat_AsDouble(value);
1332                         if (d == -1 && PyErr_Occurred()) {
1333                                 return -1;
1334                         }
1335                         ((double *)IDP_Array(self->prop))[index] = d;
1336                         break;
1337                 }
1338                 case IDP_INT:
1339                 {
1340                         const int i = _PyLong_AsInt(value);
1341                         if (i == -1 && PyErr_Occurred()) {
1342                                 return -1;
1343                         }
1344
1345                         ((int *)IDP_Array(self->prop))[index] = i;
1346                         break;
1347                 }
1348         }
1349         return 0;
1350 }
1351
1352 static PySequenceMethods BPy_IDArray_Seq = {
1353         (lenfunc) BPy_IDArray_Len,          /* inquiry sq_length */
1354         NULL,                               /* binaryfunc sq_concat */
1355         NULL,                               /* intargfunc sq_repeat */
1356         (ssizeargfunc)BPy_IDArray_GetItem,  /* intargfunc sq_item */
1357         NULL,                               /* intintargfunc sq_slice */
1358         (ssizeobjargproc)BPy_IDArray_SetItem, /* intobjargproc sq_ass_item */
1359         NULL,                               /* intintobjargproc sq_ass_slice */
1360         NULL,                               /* objobjproc sq_contains */
1361         /* Added in release 2.0 */
1362         NULL,                               /* binaryfunc sq_inplace_concat */
1363         NULL,                               /* intargfunc sq_inplace_repeat */
1364 };
1365
1366
1367
1368 /* sequence slice (get): idparr[a:b] */
1369 static PyObject *BPy_IDArray_slice(BPy_IDArray *self, int begin, int end)
1370 {
1371         IDProperty *prop = self->prop;
1372         PyObject *tuple;
1373         int count;
1374
1375         CLAMP(begin, 0, prop->len);
1376         if (end < 0) end = prop->len + end + 1;
1377         CLAMP(end, 0, prop->len);
1378         begin = MIN2(begin, end);
1379
1380         tuple = PyTuple_New(end - begin);
1381
1382         switch (prop->subtype) {
1383                 case IDP_FLOAT:
1384                 {
1385                         const float *array = (float *)IDP_Array(prop);
1386                         for (count = begin; count < end; count++) {
1387                                 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count]));
1388                         }
1389                         break;
1390                 }
1391                 case IDP_DOUBLE:
1392                 {
1393                         const double *array = (double *)IDP_Array(prop);
1394                         for (count = begin; count < end; count++) {
1395                                 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count]));
1396                         }
1397                         break;
1398                 }
1399                 case IDP_INT:
1400                 {
1401                         const int *array = (int *)IDP_Array(prop);
1402                         for (count = begin; count < end; count++) {
1403                                 PyTuple_SET_ITEM(tuple, count - begin, PyLong_FromLong(array[count]));
1404                         }
1405                         break;
1406                 }
1407         }
1408
1409         return tuple;
1410 }
1411 /* sequence slice (set): idparr[a:b] = value */
1412 static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject *seq)
1413 {
1414         IDProperty *prop = self->prop;
1415         bool is_double;
1416         const PyTypeObject *py_type = idp_array_py_type(self, &is_double);
1417         const size_t elem_size = is_double ? sizeof(double) : sizeof(float);
1418         size_t alloc_len;
1419         size_t size;
1420         void *vec;
1421
1422         CLAMP(begin, 0, prop->len);
1423         CLAMP(end, 0, prop->len);
1424         begin = MIN2(begin, end);
1425
1426         size = (end - begin);
1427         alloc_len = size * elem_size;
1428
1429         vec = MEM_mallocN(alloc_len, "array assignment"); /* NOTE: we count on int/float being the same size here */
1430         if (PyC_AsArray(vec, seq, size, py_type, is_double, "slice assignment: ") == -1) {
1431                 MEM_freeN(vec);
1432                 return -1;
1433         }
1434
1435         memcpy((void *)(((char *)IDP_Array(prop)) + (begin * elem_size)), vec, alloc_len);
1436
1437         MEM_freeN(vec);
1438         return 0;
1439 }
1440
1441 static PyObject *BPy_IDArray_subscript(BPy_IDArray *self, PyObject *item)
1442 {
1443         if (PyIndex_Check(item)) {
1444                 Py_ssize_t i;
1445                 i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1446                 if (i == -1 && PyErr_Occurred())
1447                         return NULL;
1448                 if (i < 0)
1449                         i += self->prop->len;
1450                 return BPy_IDArray_GetItem(self, i);
1451         }
1452         else if (PySlice_Check(item)) {
1453                 Py_ssize_t start, stop, step, slicelength;
1454
1455                 if (PySlice_GetIndicesEx(item, self->prop->len, &start, &stop, &step, &slicelength) < 0)
1456                         return NULL;
1457
1458                 if (slicelength <= 0) {
1459                         return PyTuple_New(0);
1460                 }
1461                 else if (step == 1) {
1462                         return BPy_IDArray_slice(self, start, stop);
1463                 }
1464                 else {
1465                         PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors");
1466                         return NULL;
1467                 }
1468         }
1469         else {
1470                 PyErr_Format(PyExc_TypeError,
1471                              "vector indices must be integers, not %.200s",
1472                              __func__, Py_TYPE(item)->tp_name);
1473                 return NULL;
1474         }
1475 }
1476
1477 static int BPy_IDArray_ass_subscript(BPy_IDArray *self, PyObject *item, PyObject *value)
1478 {
1479         if (PyIndex_Check(item)) {
1480                 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
1481                 if (i == -1 && PyErr_Occurred())
1482                         return -1;
1483                 if (i < 0)
1484                         i += self->prop->len;
1485                 return BPy_IDArray_SetItem(self, i, value);
1486         }
1487         else if (PySlice_Check(item)) {
1488                 Py_ssize_t start, stop, step, slicelength;
1489
1490                 if (PySlice_GetIndicesEx(item, self->prop->len, &start, &stop, &step, &slicelength) < 0)
1491                         return -1;
1492
1493                 if (step == 1)
1494                         return BPy_IDArray_ass_slice(self, start, stop, value);
1495                 else {
1496                         PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors");
1497                         return -1;
1498                 }
1499         }
1500         else {
1501                 PyErr_Format(PyExc_TypeError,
1502                              "vector indices must be integers, not %.200s",
1503                              Py_TYPE(item)->tp_name);
1504                 return -1;
1505         }
1506 }
1507
1508 static PyMappingMethods BPy_IDArray_AsMapping = {
1509         (lenfunc)BPy_IDArray_Len,
1510         (binaryfunc)BPy_IDArray_subscript,
1511         (objobjargproc)BPy_IDArray_ass_subscript
1512 };
1513
1514 static int itemsize_by_idarray_type(int array_type)
1515 {
1516         if (array_type == IDP_INT) return sizeof(int);
1517         if (array_type == IDP_FLOAT) return sizeof(float);
1518         if (array_type == IDP_DOUBLE) return sizeof(double);
1519         return -1;  /* should never happen */
1520 }
1521
1522 static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags)
1523 {
1524         IDProperty *prop = self->prop;
1525         int itemsize = itemsize_by_idarray_type(prop->subtype);
1526         int length = itemsize * prop->len;
1527
1528         if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) {
1529                 return -1;
1530         }
1531
1532         view->itemsize = itemsize;
1533         view->format = (char *)idp_format_from_array_type(prop->subtype);
1534
1535         Py_ssize_t *shape = MEM_mallocN(sizeof(Py_ssize_t), __func__);
1536         shape[0] = prop->len;
1537         view->shape = shape;
1538
1539         return 0;
1540 }
1541
1542 static void BPy_IDArray_releasebuffer(BPy_IDArray *UNUSED(self), Py_buffer *view)
1543 {
1544         MEM_freeN(view->shape);
1545 }
1546
1547 static PyBufferProcs BPy_IDArray_Buffer = {
1548         (getbufferproc)BPy_IDArray_getbuffer,
1549         (releasebufferproc)BPy_IDArray_releasebuffer,
1550 };
1551
1552
1553 PyTypeObject BPy_IDArray_Type = {
1554         PyVarObject_HEAD_INIT(NULL, 0)
1555         /*  For printing, in format "<module>.<name>" */
1556         "IDPropertyArray",           /* char *tp_name; */
1557         sizeof(BPy_IDArray),       /* int tp_basicsize; */
1558         0,                          /* tp_itemsize;  For allocation */
1559
1560         /* Methods to implement standard operations */
1561
1562         NULL,                       /* destructor tp_dealloc; */
1563         NULL,                       /* printfunc tp_print; */
1564         NULL,     /* getattrfunc tp_getattr; */
1565         NULL,     /* setattrfunc tp_setattr; */
1566         NULL,                       /* cmpfunc tp_compare; */
1567         (reprfunc)BPy_IDArray_repr,     /* reprfunc tp_repr; */
1568
1569         /* Method suites for standard classes */
1570
1571         NULL,                       /* PyNumberMethods *tp_as_number; */
1572         &BPy_IDArray_Seq,           /* PySequenceMethods *tp_as_sequence; */
1573         &BPy_IDArray_AsMapping,     /* PyMappingMethods *tp_as_mapping; */
1574
1575         /* More standard operations (here for binary compatibility) */
1576
1577         NULL,                       /* hashfunc tp_hash; */
1578         NULL,                       /* ternaryfunc tp_call; */
1579         NULL,                       /* reprfunc tp_str; */
1580         NULL,                       /* getattrofunc tp_getattro; */
1581         NULL,                       /* setattrofunc tp_setattro; */
1582
1583         /* Functions to access object as input/output buffer */
1584         &BPy_IDArray_Buffer,        /* PyBufferProcs *tp_as_buffer; */
1585
1586         /*** Flags to define presence of optional/expanded features ***/
1587         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
1588
1589         NULL,                       /*  char *tp_doc;  Documentation string */
1590         /*** Assigned meaning in release 2.0 ***/
1591         /* call function for all accessible objects */
1592         NULL,                       /* traverseproc tp_traverse; */
1593
1594         /* delete references to contained objects */
1595         NULL,                       /* inquiry tp_clear; */
1596
1597         /***  Assigned meaning in release 2.1 ***/
1598         /*** rich comparisons ***/
1599         NULL,                       /* richcmpfunc tp_richcompare; */
1600
1601         /***  weak reference enabler ***/
1602         0,                          /* long tp_weaklistoffset; */
1603
1604         /*** Added in release 2.2 ***/
1605         /*   Iterators */
1606         NULL,                       /* getiterfunc tp_iter; */
1607         NULL,                       /* iternextfunc tp_iternext; */
1608
1609         /*** Attribute descriptor and subclassing stuff ***/
1610         BPy_IDArray_methods,        /* struct PyMethodDef *tp_methods; */
1611         NULL,                       /* struct PyMemberDef *tp_members; */
1612         BPy_IDArray_getseters,       /* struct PyGetSetDef *tp_getset; */
1613         NULL,                       /* struct _typeobject *tp_base; */
1614         NULL,                       /* PyObject *tp_dict; */
1615         NULL,                       /* descrgetfunc tp_descr_get; */
1616         NULL,                       /* descrsetfunc tp_descr_set; */
1617         0,                          /* long tp_dictoffset; */
1618         NULL,                       /* initproc tp_init; */
1619         NULL,                       /* allocfunc tp_alloc; */
1620         NULL,                       /* newfunc tp_new; */
1621         /*  Low-level free-memory routine */
1622         NULL,                       /* freefunc tp_free;  */
1623         /* For PyObject_IS_GC */
1624         NULL,                       /* inquiry tp_is_gc;  */
1625         NULL,                       /* PyObject *tp_bases; */
1626         /* method resolution order */
1627         NULL,                       /* PyObject *tp_mro;  */
1628         NULL,                       /* PyObject *tp_cache; */
1629         NULL,                       /* PyObject *tp_subclasses; */
1630         NULL,                       /* PyObject *tp_weaklist; */
1631         NULL
1632 };
1633
1634 /*********** ID Property Group iterator ********/
1635
1636 static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
1637 {
1638         return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name);
1639 }
1640
1641 static PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
1642 {
1643
1644         if (self->cur) {
1645                 PyObject *ret;
1646                 IDProperty *cur;
1647
1648                 cur = self->cur;
1649                 self->cur = self->cur->next;
1650
1651                 if (self->mode == IDPROP_ITER_ITEMS) {
1652                         ret = PyTuple_New(2);
1653                         PyTuple_SET_ITEMS(ret,
1654                                 PyUnicode_FromString(cur->name),
1655                                 BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
1656                         return ret;
1657                 }
1658                 else {
1659                         return PyUnicode_FromString(cur->name);
1660                 }
1661         }
1662         else {
1663                 PyErr_SetNone(PyExc_StopIteration);
1664                 return NULL;
1665         }
1666 }
1667
1668 PyTypeObject BPy_IDGroup_Iter_Type = {
1669         PyVarObject_HEAD_INIT(NULL, 0)
1670         /*  For printing, in format "<module>.<name>" */
1671         "IDPropertyGroupIter",           /* char *tp_name; */
1672         sizeof(BPy_IDGroup_Iter),       /* int tp_basicsize; */
1673         0,                          /* tp_itemsize;  For allocation */
1674
1675         /* Methods to implement standard operations */
1676
1677         NULL,                       /* destructor tp_dealloc; */
1678         NULL,                       /* printfunc tp_print; */
1679         NULL,     /* getattrfunc tp_getattr; */
1680         NULL,     /* setattrfunc tp_setattr; */
1681         NULL,                       /* cmpfunc tp_compare; */
1682         (reprfunc) IDGroup_Iter_repr,     /* reprfunc tp_repr; */
1683
1684         /* Method suites for standard classes */
1685
1686         NULL,                       /* PyNumberMethods *tp_as_number; */
1687         NULL,                       /* PySequenceMethods *tp_as_sequence; */
1688         NULL,                       /* PyMappingMethods *tp_as_mapping; */
1689
1690         /* More standard operations (here for binary compatibility) */
1691
1692         NULL,                       /* hashfunc tp_hash; */
1693         NULL,                       /* ternaryfunc tp_call; */
1694         NULL,                       /* reprfunc tp_str; */
1695         NULL,                       /* getattrofunc tp_getattro; */
1696         NULL,                       /* setattrofunc tp_setattro; */
1697
1698         /* Functions to access object as input/output buffer */
1699         NULL,                       /* PyBufferProcs *tp_as_buffer; */
1700
1701         /*** Flags to define presence of optional/expanded features ***/
1702         Py_TPFLAGS_DEFAULT,         /* long tp_flags; */
1703
1704         NULL,                       /*  char *tp_doc;  Documentation string */
1705         /*** Assigned meaning in release 2.0 ***/
1706         /* call function for all accessible objects */
1707         NULL,                       /* traverseproc tp_traverse; */
1708
1709         /* delete references to contained objects */
1710         NULL,                       /* inquiry tp_clear; */
1711
1712         /***  Assigned meaning in release 2.1 ***/
1713         /*** rich comparisons ***/
1714         NULL,                       /* richcmpfunc tp_richcompare; */
1715
1716         /***  weak reference enabler ***/
1717         0,                          /* long tp_weaklistoffset; */
1718
1719         /*** Added in release 2.2 ***/
1720         /*   Iterators */
1721         PyObject_SelfIter,                  /* getiterfunc tp_iter; */
1722         (iternextfunc) BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
1723 };
1724
1725 void IDProp_Init_Types(void)
1726 {
1727         PyType_Ready(&BPy_IDGroup_Type);
1728         PyType_Ready(&BPy_IDGroup_Iter_Type);
1729         PyType_Ready(&BPy_IDArray_Type);
1730 }
1731
1732 /*----------------------------MODULE INIT-------------------------*/
1733
1734 /* --- */
1735
1736 static struct PyModuleDef IDProp_types_module_def = {
1737         PyModuleDef_HEAD_INIT,
1738         "idprop.types",  /* m_name */
1739         NULL,  /* m_doc */
1740         0,  /* m_size */
1741         NULL,  /* m_methods */
1742         NULL,  /* m_reload */
1743         NULL,  /* m_traverse */
1744         NULL,  /* m_clear */
1745         NULL,  /* m_free */
1746 };
1747
1748 static PyObject *BPyInit_idprop_types(void)
1749 {
1750         PyObject *submodule;
1751
1752         submodule = PyModule_Create(&IDProp_types_module_def);
1753
1754         IDProp_Init_Types();
1755
1756 #define MODULE_TYPE_ADD(s, t) \
1757         PyModule_AddObject(s, t.tp_name, (PyObject *)&t); Py_INCREF((PyObject *)&t)
1758
1759         /* bmesh_py_types.c */
1760         MODULE_TYPE_ADD(submodule, BPy_IDGroup_Type);
1761         MODULE_TYPE_ADD(submodule, BPy_IDGroup_Iter_Type);
1762         MODULE_TYPE_ADD(submodule, BPy_IDArray_Type);
1763
1764 #undef MODULE_TYPE_ADD
1765
1766         return submodule;
1767 }
1768
1769 /* --- */
1770
1771 static PyMethodDef IDProp_methods[] = {
1772         {NULL, NULL, 0, NULL}
1773 };
1774
1775
1776 PyDoc_STRVAR(IDProp_module_doc,
1777 "This module provides access id property types (currently mainly for docs)."
1778 );
1779 static struct PyModuleDef IDProp_module_def = {
1780         PyModuleDef_HEAD_INIT,
1781         "idprop",  /* m_name */
1782         IDProp_module_doc,  /* m_doc */
1783         0,  /* m_size */
1784         IDProp_methods,  /* m_methods */
1785         NULL,  /* m_reload */
1786         NULL,  /* m_traverse */
1787         NULL,  /* m_clear */
1788         NULL,  /* m_free */
1789 };
1790
1791 PyObject *BPyInit_idprop(void)
1792 {
1793         PyObject *mod;
1794         PyObject *submodule;
1795         PyObject *sys_modules = PyThreadState_GET()->interp->modules;
1796
1797         mod = PyModule_Create(&IDProp_module_def);
1798
1799         /* idprop.types */
1800         PyModule_AddObject(mod, "types", (submodule = BPyInit_idprop_types()));
1801         PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
1802         Py_INCREF(submodule);
1803
1804         return mod;
1805 }
1806
1807
1808 #ifndef NDEBUG
1809 /* -------------------------------------------------------------------- */
1810 /* debug only function */
1811
1812 void IDP_spit(IDProperty *prop)
1813 {
1814         if (prop) {
1815                 PyGILState_STATE gilstate;
1816                 bool use_gil = true; /* !PyC_IsInterpreterActive(); */
1817                 PyObject *ret_dict;
1818                 PyObject *ret_str;
1819
1820                 if (use_gil) {
1821                         gilstate = PyGILState_Ensure();
1822                 }
1823
1824                 /* to_dict() */
1825                 ret_dict = BPy_IDGroup_MapDataToPy(prop);
1826                 ret_str = PyObject_Repr(ret_dict);
1827                 Py_DECREF(ret_dict);
1828
1829                 printf("IDProperty(%p): %s\n", prop, _PyUnicode_AsString(ret_str));
1830
1831                 Py_DECREF(ret_str);
1832
1833                 if (use_gil) {
1834                         PyGILState_Release(gilstate);
1835                 }
1836         }
1837         else {
1838                 printf("IDProperty: <NIL>\n");
1839         }
1840 }
1841
1842 #endif