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