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