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