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