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