Blender V4.3
idprop_py_api.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <algorithm>
10
11#include <Python.h>
12
13#include "MEM_guardedalloc.h"
14
15#include "BLI_utildefines.h"
16
17#include "idprop_py_api.hh"
18#include "idprop_py_ui_api.hh"
19
20#include "BKE_idprop.hh"
21
22#include "DNA_ID.h" /* ID property definitions. */
23
24#define USE_STRING_COERCE
25
26#ifdef USE_STRING_COERCE
27# include "py_capi_utils.hh"
28#endif
29
30#include "python_utildefines.hh"
31
32extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
33extern PyObject *pyrna_id_CreatePyObject(ID *id);
34extern bool pyrna_id_CheckPyObject(PyObject *obj);
35
36/* Currently there is no need to expose this publicly. */
37static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed);
38static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed);
39static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed);
40
44
45static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type);
46static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value);
47
48/* -------------------------------------------------------------------- */
54static PyObject *idprop_py_from_idp_string(const IDProperty *prop)
55{
56 if (prop->subtype == IDP_STRING_SUB_BYTE) {
57 return PyBytes_FromStringAndSize(IDP_String(prop), prop->len);
58 }
59
60#ifdef USE_STRING_COERCE
61 return PyC_UnicodeFromBytesAndSize(static_cast<const char *>(IDP_Array(prop)), prop->len - 1);
62#else
63 return PyUnicode_FromStringAndSize(IDP_String(prop), prop->len - 1);
64#endif
65}
66
67static PyObject *idprop_py_from_idp_int(const IDProperty *prop)
68{
69 return PyLong_FromLong(long(IDP_Int(prop)));
70}
71
72static PyObject *idprop_py_from_idp_float(const IDProperty *prop)
73{
74 return PyFloat_FromDouble(double(IDP_Float(prop)));
75}
76
77static PyObject *idprop_py_from_idp_double(const IDProperty *prop)
78{
79 return PyFloat_FromDouble(IDP_Double(prop));
80}
81
82static PyObject *idprop_py_from_idp_bool(const IDProperty *prop)
83{
84 return PyBool_FromLong(IDP_Bool(prop));
85}
86
87static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *parent)
88{
89 BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type);
90 group->owner_id = id;
91 group->prop = prop;
92 group->parent = parent; /* can be nullptr */
93 return (PyObject *)group;
94}
95
96static PyObject *idprop_py_from_idp_id(IDProperty *prop)
97{
98 return pyrna_id_CreatePyObject(static_cast<ID *>(prop->data.pointer));
99}
100
101static PyObject *idprop_py_from_idp_array(ID *id, IDProperty *prop)
102{
104 array->owner_id = id;
105 array->prop = prop;
106 return (PyObject *)array;
107}
108
109static PyObject *idprop_py_from_idp_idparray(ID *id, IDProperty *prop)
110{
111 PyObject *seq = PyList_New(prop->len);
113 int i;
114
115 if (!seq) {
116 PyErr_Format(
117 PyExc_RuntimeError, "%s: IDP_IDPARRAY: PyList_New(%d) failed", __func__, prop->len);
118 return nullptr;
119 }
120
121 for (i = 0; i < prop->len; i++) {
122 PyObject *wrap = BPy_IDGroup_WrapData(id, array++, prop);
123
124 /* BPy_IDGroup_MapDataToPy sets the error */
125 if (UNLIKELY(wrap == nullptr)) {
126 Py_DECREF(seq);
127 return nullptr;
128 }
129
130 PyList_SET_ITEM(seq, i, wrap);
131 }
132
133 return seq;
134}
135
138/* -------------------------------------------------------------------- */
142/* use for both array and group */
144{
145 return _Py_HashPointer(self->prop);
146}
147
149{
150 return PyUnicode_FromFormat("<bpy id prop: owner=\"%s\", name=\"%s\", address=%p>",
151 self->owner_id ? self->owner_id->name : "<NONE>",
152 self->prop->name,
153 self->prop);
154}
155
156PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent)
157{
158 switch (prop->type) {
159 case IDP_STRING:
160 return idprop_py_from_idp_string(prop);
161 case IDP_INT:
162 return idprop_py_from_idp_int(prop);
163 case IDP_FLOAT:
164 return idprop_py_from_idp_float(prop);
165 case IDP_DOUBLE:
166 return idprop_py_from_idp_double(prop);
167 case IDP_BOOLEAN:
168 return idprop_py_from_idp_bool(prop);
169 case IDP_GROUP:
170 return idprop_py_from_idp_group(id, prop, parent);
171 case IDP_ARRAY:
172 return idprop_py_from_idp_array(id, prop);
173 case IDP_IDPARRAY:
174 return idprop_py_from_idp_idparray(id, prop); /* this could be better a internal type */
175 case IDP_ID:
176 return idprop_py_from_idp_id(prop);
177 default:
178 Py_RETURN_NONE;
179 }
180}
181
182/* UNUSED, currently assignment overwrites into new properties, rather than setting in-place. */
183#if 0
184static int BPy_IDGroup_SetData(BPy_IDProperty *self, IDProperty *prop, PyObject *value)
185{
186 switch (prop->type) {
187 case IDP_STRING: {
188 char *st;
189 if (!PyUnicode_Check(value)) {
190 PyErr_SetString(PyExc_TypeError, "expected a string!");
191 return -1;
192 }
193/* NOTE: if this code is enabled, bytes support needs to be added */
194# ifdef USE_STRING_COERCE
195 {
196 int alloc_len;
197 PyObject *value_coerce = nullptr;
198
199 st = (char *)PyC_UnicodeAsBytes(value, &value_coerce);
200 alloc_len = strlen(st) + 1;
201
202 st = PyUnicode_AsUTF8(value);
203 IDP_ResizeArray(prop, alloc_len);
204 memcpy(IDP_Array(prop), st, alloc_len);
205 Py_XDECREF(value_coerce);
206 }
207# else
208 length_ssize_t st_len;
209 st = PyUnicode_AsUTF8AndSize(value, &st_len);
210 IDP_ResizeArray(prop, st_len + 1);
211 memcpy(IDP_Array(prop), st, st_len + 1);
212# endif
213
214 return 0;
215 }
216
217 case IDP_INT: {
218 int ivalue = PyLong_AsSsize_t(value);
219 if (ivalue == -1 && PyErr_Occurred()) {
220 PyErr_SetString(PyExc_TypeError, "expected an int type");
221 return -1;
222 }
223 IDP_Int(prop) = ivalue;
224 break;
225 }
226 case IDP_FLOAT: {
227 float fvalue = float(PyFloat_AsDouble(value));
228 if (fvalue == -1 && PyErr_Occurred()) {
229 PyErr_SetString(PyExc_TypeError, "expected a float");
230 return -1;
231 }
232 IDP_Float(self->prop) = fvalue;
233 break;
234 }
235 case IDP_DOUBLE: {
236 double dvalue = PyFloat_AsDouble(value);
237 if (dvalue == -1 && PyErr_Occurred()) {
238 PyErr_SetString(PyExc_TypeError, "expected a float");
239 return -1;
240 }
241 IDP_Double(self->prop) = dvalue;
242 break;
243 }
244 default:
245 PyErr_SetString(PyExc_AttributeError, "attempt to set read-only attribute!");
246 return -1;
247 }
248 return 0;
249}
250#endif
251
252static PyObject *BPy_IDGroup_GetName(BPy_IDProperty *self, void * /*closure*/)
253{
254 return PyUnicode_FromString(self->prop->name);
255}
256
257static int BPy_IDGroup_SetName(BPy_IDProperty *self, PyObject *value, void * /*closure*/)
258{
259 const char *name;
260 Py_ssize_t name_len;
261
262 if (!PyUnicode_Check(value)) {
263 PyErr_SetString(PyExc_TypeError, "expected a string!");
264 return -1;
265 }
266
267 name = PyUnicode_AsUTF8AndSize(value, &name_len);
268
269 if (!(name_len < MAX_IDPROP_NAME)) {
270 PyErr_SetString(PyExc_TypeError, "string length cannot exceed 63 characters!");
271 return -1;
272 }
273
274 memcpy(self->prop->name, name, name_len + 1);
275 return 0;
276}
277
278#if 0
279static PyObject *BPy_IDGroup_GetType(BPy_IDProperty *self)
280{
281 return PyLong_FromLong(self->prop->type);
282}
283#endif
284
285static PyGetSetDef BPy_IDGroup_getseters[] = {
286 {"name",
287 (getter)BPy_IDGroup_GetName,
288 (setter)BPy_IDGroup_SetName,
289 "The name of this Group.",
290 nullptr},
291 {nullptr, nullptr, nullptr, nullptr, nullptr},
292};
293
295{
296 if (self->prop->type != IDP_GROUP) {
297 PyErr_SetString(PyExc_TypeError, "len() of unsized object");
298 return -1;
299 }
300
301 return self->prop->len;
302}
303
304static PyObject *BPy_IDGroup_Map_GetItem(BPy_IDProperty *self, PyObject *item)
305{
306 IDProperty *idprop;
307 const char *name;
308
309 if (self->prop->type != IDP_GROUP) {
310 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
311 return nullptr;
312 }
313
314 name = PyUnicode_AsUTF8(item);
315
316 if (name == nullptr) {
317 PyErr_SetString(PyExc_TypeError, "only strings are allowed as keys of ID properties");
318 return nullptr;
319 }
320
321 idprop = IDP_GetPropertyFromGroup(self->prop, name);
322
323 if (idprop == nullptr) {
324 PyErr_SetString(PyExc_KeyError, "key not in subgroup dict");
325 return nullptr;
326 }
327
328 return BPy_IDGroup_WrapData(self->owner_id, idprop, self->prop);
329}
330
331/* Return identified matching IDProperty type, or -1 if error (e.g. mixed and/or incompatible
332 * types, etc.). */
333static char idp_sequence_type(PyObject *seq_fast)
334{
335 PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast);
336 PyObject *item;
337 char type = IDP_INT;
338
339 Py_ssize_t i, len = PySequence_Fast_GET_SIZE(seq_fast);
340
341 for (i = 0; i < len; i++) {
342 item = seq_fast_items[i];
343 if (PyFloat_Check(item)) {
344 /* Mixed float and any other type but int.
345 * NOTE: Mixed float/int is allowed, and considered as float values. */
346 if (!ELEM(type, IDP_INT, IDP_DOUBLE)) {
347 return -1;
348 }
349 type = IDP_DOUBLE;
350 }
351 else if (PyBool_Check(item)) {
352 /* Mixed boolean and any other type. */
353 if (i != 0 && (type != IDP_BOOLEAN)) {
354 return -1;
355 }
356 type = IDP_BOOLEAN;
357 }
358 else if (PyLong_Check(item)) {
359 /* Mixed int and any other type but float.
360 * NOTE: Mixed float/int is allowed, and considered as float values. */
361 if (!ELEM(type, IDP_INT, IDP_DOUBLE)) {
362 return -1;
363 }
364 }
365 else if (PyMapping_Check(item)) {
366 /* Mixed dict and any other type. */
367 if (i != 0 && (type != IDP_IDPARRAY)) {
368 return -1;
369 }
370 type = IDP_IDPARRAY;
371 }
372 else {
373 return -1;
374 }
375 }
376
377 return type;
378}
379
380static const char *idp_try_read_name(PyObject *name_obj)
381{
382 const char *name = nullptr;
383 if (name_obj) {
384 Py_ssize_t name_len;
385 name = PyUnicode_AsUTF8AndSize(name_obj, &name_len);
386
387 if (name == nullptr) {
388 PyErr_Format(PyExc_KeyError,
389 "invalid id-property key, expected a string, not a %.200s",
390 Py_TYPE(name_obj)->tp_name);
391 return nullptr;
392 }
393
394 if (!(name_len < MAX_IDPROP_NAME)) {
395 PyErr_SetString(PyExc_KeyError,
396 "the length of IDProperty names is limited to 63 characters");
397 return nullptr;
398 }
399 }
400 else {
401 name = "";
402 }
403 return name;
404}
405
408/* -------------------------------------------------------------------- */
432 const char *name,
433 PyObject *ob,
434 const bool do_conversion,
435 const bool can_create)
436{
437 IDProperty *prop = nullptr;
438 const double value = PyFloat_AsDouble(ob);
439 if (prop_exist) {
440 if (prop_exist->type == IDP_DOUBLE) {
441 IDP_Double(prop_exist) = value;
442 prop = prop_exist;
443 }
444 else if (do_conversion) {
445 switch (prop_exist->type) {
446 case IDP_FLOAT:
447 IDP_Float(prop_exist) = float(value);
448 prop = prop_exist;
449 break;
450 case IDP_STRING:
451 case IDP_INT:
452 case IDP_ARRAY:
453 case IDP_GROUP:
454 case IDP_ID:
455 case IDP_DOUBLE:
456 case IDP_IDPARRAY:
457 case IDP_BOOLEAN:
458 break;
459 }
460 }
461 }
462 if (!prop && can_create) {
463 prop = blender::bke::idprop::create(name, value).release();
464 }
465 return prop;
466}
467
469 const char *name,
470 PyObject *ob,
471 const bool do_conversion,
472 const bool can_create)
473{
474 IDProperty *prop = nullptr;
475 const bool value = PyC_Long_AsBool(ob);
476 if (prop_exist) {
477 if (prop_exist->type == IDP_BOOLEAN) {
478 IDP_Bool(prop_exist) = value;
479 prop = prop_exist;
480 }
481 else if (do_conversion) {
482 switch (prop_exist->type) {
483 case IDP_INT:
484 IDP_Int(prop_exist) = int(value);
485 prop = prop_exist;
486 break;
487 case IDP_STRING:
488 case IDP_FLOAT:
489 case IDP_ARRAY:
490 case IDP_GROUP:
491 case IDP_ID:
492 case IDP_DOUBLE:
493 case IDP_IDPARRAY:
494 case IDP_BOOLEAN:
495 break;
496 }
497 }
498 }
499 if (!prop && can_create) {
500 prop = blender::bke::idprop::create_bool(name, value).release();
501 }
502 return prop;
503}
504
506 const char *name,
507 PyObject *ob,
508 const bool do_conversion,
509 const bool can_create)
510{
511 IDProperty *prop = nullptr;
512 if (prop_exist) {
513 if (prop_exist->type == IDP_INT) {
514 const int value = PyC_Long_AsI32(ob);
515 if (value == -1 && PyErr_Occurred()) {
516 return prop;
517 }
518 IDP_Int(prop_exist) = value;
519 prop = prop_exist;
520 }
521 else if (do_conversion) {
522 const int64_t value = PyC_Long_AsI64(ob);
523 if (value == -1 && PyErr_Occurred()) {
524 return prop;
525 }
526 switch (prop_exist->type) {
527 case IDP_FLOAT:
528 IDP_Float(prop_exist) = float(value);
529 prop = prop_exist;
530 break;
531 case IDP_DOUBLE:
532 IDP_Double(prop_exist) = double(value);
533 prop = prop_exist;
534 break;
535 case IDP_STRING:
536 case IDP_INT:
537 case IDP_ARRAY:
538 case IDP_GROUP:
539 case IDP_ID:
540 case IDP_IDPARRAY:
541 case IDP_BOOLEAN:
542 break;
543 }
544 }
545 }
546 if (!prop && can_create) {
547 const int value = PyC_Long_AsI32(ob);
548 if (value == -1 && PyErr_Occurred()) {
549 return prop;
550 }
551 prop = blender::bke::idprop::create(name, value).release();
552 }
553 return prop;
554}
555
557 const char *name,
558 PyObject *ob,
559 const bool /*do_conversion*/,
560 const bool can_create)
561{
562 IDProperty *prop = nullptr;
563 Py_ssize_t value_len = 0;
564 const char *value = nullptr;
565
566#ifdef USE_STRING_COERCE
567 PyObject *value_coerce = nullptr;
568 value = PyC_UnicodeAsBytesAndSize(ob, &value_len, &value_coerce);
569#else
570 value = PyUnicode_AsUTF8AndSize(ob, &value_len);
571#endif
572
573 if (prop_exist) {
574 if (prop_exist->type == IDP_STRING && prop_exist->subtype == IDP_STRING_SUB_UTF8) {
575 IDP_AssignStringMaxSize(prop_exist, value, int(value_len) + 1);
576 prop = prop_exist;
577 }
578 /* No conversion. */
579 }
580
581 if (!prop && can_create) {
582 IDPropertyTemplate val = {0};
583 val.string.str = value;
584 val.string.len = int(value_len) + 1;
586 prop = IDP_New(IDP_STRING, &val, name);
587 }
588
589#ifdef USE_STRING_COERCE
590 Py_XDECREF(value_coerce);
591#endif
592
593 return prop;
594}
595
597 const char *name,
598 PyObject *ob,
599 const bool /*do_conversion*/,
600 const bool can_create)
601{
602 IDProperty *prop = nullptr;
603 Py_ssize_t value_len = PyBytes_GET_SIZE(ob);
604 const char *value = PyBytes_AS_STRING(ob);
605
606 if (prop_exist) {
607 if (prop_exist->type == IDP_STRING && prop_exist->subtype == IDP_STRING_SUB_BYTE) {
608 IDP_AssignStringMaxSize(prop_exist, value, int(value_len) + 1);
609 prop = prop_exist;
610 }
611 /* No conversion. */
612 }
613
614 if (!prop && can_create) {
615 IDPropertyTemplate val = {0};
616 val.string.str = value;
617 val.string.len = int(value_len);
619 prop = IDP_New(IDP_STRING, &val, name);
620 }
621
622 return prop;
623}
624
625static int idp_array_type_from_formatstr_and_size(const char *typestr, Py_ssize_t itemsize)
626{
627 const char format = PyC_StructFmt_type_from_str(typestr);
628
630 if (itemsize == 4) {
631 return IDP_FLOAT;
632 }
633 if (itemsize == 8) {
634 return IDP_DOUBLE;
635 }
636 }
638 if (itemsize == 4) {
639 return IDP_INT;
640 }
641 /* TODO: Support Booleans? */
642 }
643
644 return -1;
645}
646
647static const char *idp_format_from_array_type(int type)
648{
649 if (type == IDP_INT) {
650 return "i";
651 }
652 if (type == IDP_FLOAT) {
653 return "f";
654 }
655 if (type == IDP_DOUBLE) {
656 return "d";
657 }
658 if (type == IDP_BOOLEAN) {
659 return "b";
660 }
661 return nullptr;
662}
663
665 const char *name,
666 Py_buffer &buffer,
667 const int idp_type,
668 const bool /*do_conversion*/,
669 const bool can_create)
670{
671 BLI_assert(idp_type != -1);
672 IDProperty *prop = nullptr;
673
674 if (prop_exist) {
675 if (prop_exist->type == IDP_ARRAY && prop_exist->subtype == idp_type) {
676 BLI_assert(buffer.len == prop_exist->len);
677 memcpy(IDP_Array(prop_exist), buffer.buf, buffer.len);
678 prop = prop_exist;
679 }
680 /* No conversion. */
681 }
682 if (!prop && can_create) {
683 IDPropertyTemplate val = {0};
684 val.array.type = idp_type;
685 val.array.len = buffer.len / buffer.itemsize;
686 prop = IDP_New(IDP_ARRAY, &val, name);
687 memcpy(IDP_Array(prop), buffer.buf, buffer.len);
688 }
689 return prop;
690}
691
693 const char *name,
694 PyObject *ob,
695 const bool do_conversion,
696 const bool can_create)
697{
698 IDProperty *prop = nullptr;
699 IDPropertyTemplate val = {0};
700
701 PyObject **ob_seq_fast_items;
702 PyObject *item;
703 int i;
704
705 ob_seq_fast_items = PySequence_Fast_ITEMS(ob);
706
707 /* IDProperties do not support mixed type of data in an array. Try to extract a single type from
708 * the whole sequence, or error. */
709 val.array.type = idp_sequence_type(ob);
710 if (val.array.type == char(-1)) {
711 PyErr_SetString(PyExc_TypeError,
712 "only floats, ints, booleans and dicts are allowed in ID property arrays");
713 return nullptr;
714 }
716 /* Should never happen. */
717 PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type");
719 return nullptr;
720 }
721
722 val.array.len = PySequence_Fast_GET_SIZE(ob);
723
724 /* NOTE: For now do not consider resizing existing array property. Also do not handle IDPARRAY.
725 * - 'static type' also means 'fixed length' (e.g. vectors or matrices cases).
726 * - For 'dynamic type' case, it's not really a problem if array properties get replaced
727 * currently.
728 */
729 if (prop_exist && prop_exist->len == val.array.len) {
730 switch (val.array.type) {
731 case IDP_DOUBLE: {
732 const bool to_float = (prop_exist->subtype == IDP_FLOAT);
733 if (!(prop_exist->type == IDP_ARRAY &&
734 (prop_exist->subtype == IDP_DOUBLE || (do_conversion && to_float))))
735 {
736 break;
737 }
738 prop = prop_exist;
739 void *prop_data = IDP_Array(prop);
740 for (i = 0; i < val.array.len; i++) {
741 item = ob_seq_fast_items[i];
742 const double value = PyFloat_AsDouble(item);
743 if ((value == -1.0) && PyErr_Occurred()) {
744 continue;
745 }
746 if (to_float) {
747 static_cast<float *>(prop_data)[i] = float(value);
748 }
749 else {
750 static_cast<double *>(prop_data)[i] = value;
751 }
752 }
753 break;
754 }
755 case IDP_INT: {
756 const bool to_float = (prop_exist->subtype == IDP_FLOAT);
757 const bool to_double = (prop_exist->subtype == IDP_DOUBLE);
758 if (!(prop_exist->type == IDP_ARRAY &&
759 (prop_exist->subtype == IDP_INT || (do_conversion && (to_float || to_double)))))
760 {
761 break;
762 }
763 prop = prop_exist;
764 void *prop_data = IDP_Array(prop);
765 for (i = 0; i < val.array.len; i++) {
766 item = ob_seq_fast_items[i];
767 if (to_float || to_double) {
768 const int64_t value = PyC_Long_AsI64(item);
769 if ((value == -1) && PyErr_Occurred()) {
770 continue;
771 }
772 if (to_float) {
773 static_cast<float *>(prop_data)[i] = float(value);
774 }
775 else { /* if (to_double) */
776 static_cast<double *>(prop_data)[i] = double(value);
777 }
778 }
779 else {
780 const int value = PyC_Long_AsI32(item);
781 if ((value == -1) && PyErr_Occurred()) {
782 continue;
783 }
784 static_cast<int *>(prop_data)[i] = value;
785 }
786 }
787 break;
788 }
789 case IDP_BOOLEAN: {
790 const bool to_int = (prop_exist->subtype == IDP_INT);
791 if (!(prop_exist->type == IDP_ARRAY &&
792 (prop_exist->subtype == IDP_BOOLEAN || (do_conversion && to_int))))
793 {
794 break;
795 }
796 prop = prop_exist;
797 void *prop_data = IDP_Array(prop);
798 for (i = 0; i < val.array.len; i++) {
799 item = ob_seq_fast_items[i];
800 const int value = PyC_Long_AsBool(item);
801 if ((value == -1) && PyErr_Occurred()) {
802 continue;
803 }
804 if (to_int) {
805 static_cast<int *>(prop_data)[i] = value;
806 }
807 else {
808 static_cast<bool *>(prop_data)[i] = bool(value);
809 }
810 }
811 break;
812 }
813 case IDP_IDPARRAY: {
814 /* TODO? */
815 break;
816 }
817 }
818 }
819
820 if (prop || !can_create) {
821 return prop;
822 }
823
824 switch (val.array.type) {
825 case IDP_DOUBLE: {
826 double *prop_data;
827 prop = IDP_New(IDP_ARRAY, &val, name);
828 prop_data = static_cast<double *>(IDP_Array(prop));
829 for (i = 0; i < val.array.len; i++) {
830 item = ob_seq_fast_items[i];
831 if (((prop_data[i] = PyFloat_AsDouble(item)) == -1.0) && PyErr_Occurred()) {
832 IDP_FreeProperty(prop);
833 return nullptr;
834 }
835 }
836 break;
837 }
838 case IDP_INT: {
839 int *prop_data;
840 prop = IDP_New(IDP_ARRAY, &val, name);
841 prop_data = static_cast<int *>(IDP_Array(prop));
842 for (i = 0; i < val.array.len; i++) {
843 item = ob_seq_fast_items[i];
844 if (((prop_data[i] = PyC_Long_AsI32(item)) == -1) && PyErr_Occurred()) {
845 IDP_FreeProperty(prop);
846 return nullptr;
847 }
848 }
849 break;
850 }
851 case IDP_IDPARRAY: {
852 prop = IDP_NewIDPArray(name);
853 for (i = 0; i < val.array.len; i++) {
854 item = ob_seq_fast_items[i];
855 if (BPy_IDProperty_Map_ValidateAndCreate(nullptr, prop, item) == false) {
856 IDP_FreeProperty(prop);
857 return nullptr;
858 }
859 }
860 break;
861 }
862 case IDP_BOOLEAN: {
863 prop = IDP_New(IDP_ARRAY, &val, name);
864 bool *prop_data = static_cast<bool *>(IDP_Array(prop));
865 for (i = 0; i < val.array.len; i++) {
866 item = ob_seq_fast_items[i];
867 const int value = PyC_Long_AsBool(item);
868 if ((value == -1) && PyErr_Occurred()) {
869 IDP_FreeProperty(prop);
870 return nullptr;
871 }
872 prop_data[i] = (value != 0);
873 }
874 break;
875 }
876 }
877 return prop;
878}
879
881 const char *name,
882 PyObject *ob,
883 const bool do_conversion,
884 const bool can_create)
885{
886 Py_buffer buffer;
887 bool use_buffer = false;
888 int idp_buffer_type = -1;
889
890 if (PyObject_CheckBuffer(ob)) {
891 if (PyObject_GetBuffer(ob, &buffer, PyBUF_ND | PyBUF_FORMAT) == -1) {
892 /* Request failed. A `PyExc_BufferError` will have been raised,
893 * so clear it to silently fall back to accessing as a sequence. */
894 PyErr_Clear();
895 }
896 else {
897 idp_buffer_type = idp_array_type_from_formatstr_and_size(buffer.format, buffer.itemsize);
898 if (idp_buffer_type != -1) {
899 /* If creating a new IDProp is not allowed, and the existing one is not usable (same size
900 * and type), then the 'buffer assignment' process cannot be used. */
901 if (!can_create && (!prop_exist || (prop_exist->type != idp_buffer_type) ||
902 (prop_exist->len != buffer.len)))
903 {
904 PyBuffer_Release(&buffer);
905 }
906 else {
907 use_buffer = true;
908 }
909 }
910 else {
911 PyBuffer_Release(&buffer);
912 }
913 }
914 }
915
916 if (use_buffer) {
918 prop_exist, name, buffer, idp_buffer_type, do_conversion, can_create);
919 PyBuffer_Release(&buffer);
920 return prop;
921 }
922
923 PyObject *ob_seq_fast = PySequence_Fast(ob, "py -> idprop");
924 if (ob_seq_fast != nullptr) {
926 prop_exist, name, ob_seq_fast, do_conversion, can_create);
927 Py_DECREF(ob_seq_fast);
928 return prop;
929 }
930
931 return nullptr;
932}
933
934static IDProperty *idp_from_PyMapping(IDProperty * /*prop_exist*/,
935 const char *name,
936 PyObject *ob,
937 const bool /*do_conversion*/,
938 const bool /*can_create*/)
939{
940 IDProperty *prop;
941
942 /* TODO: Handle editing in-place of existing property (#IDP_FLAG_STATIC_TYPE flag). */
943
944 PyObject *keys, *vals, *key, *pval;
945 int i, len;
946 /* yay! we get into recursive stuff now! */
947 keys = PyMapping_Keys(ob);
948 vals = PyMapping_Values(ob);
949
950 /* We allocate the group first; if we hit any invalid data,
951 * we can delete it easily enough. */
952 prop = blender::bke::idprop::create_group(name).release();
953 len = PyMapping_Length(ob);
954 for (i = 0; i < len; i++) {
955 key = PySequence_GetItem(keys, i);
956 pval = PySequence_GetItem(vals, i);
957 if (BPy_IDProperty_Map_ValidateAndCreate(key, prop, pval) == false) {
958 IDP_FreeProperty(prop);
959 Py_XDECREF(keys);
960 Py_XDECREF(vals);
961 Py_XDECREF(key);
962 Py_XDECREF(pval);
963 /* error is already set */
964 return nullptr;
965 }
966 Py_XDECREF(key);
967 Py_XDECREF(pval);
968 }
969 Py_XDECREF(keys);
970 Py_XDECREF(vals);
971 return prop;
972}
973
975 const char *name,
976 PyObject *ob,
977 const bool /*do_conversion*/,
978 const bool can_create)
979{
980 IDProperty *prop = nullptr;
981 ID *value = nullptr;
982 pyrna_id_FromPyObject(ob, &value);
983
984 if (value && (value->flag & ID_FLAG_EMBEDDED_DATA) != 0) {
985 PyErr_SetString(PyExc_ValueError, "Cannot assign an embedded ID pointer to an id-property");
986 return nullptr;
987 }
988
989 if (prop_exist) {
990 if (prop_exist->type == IDP_ID) {
991 IDP_AssignID(prop_exist, value, 0);
992 prop = prop_exist;
993 }
994 /* No conversion. */
995 }
996 if (!prop && can_create) {
997 prop = blender::bke::idprop::create(name, value).release();
998 }
999 return prop;
1000}
1001
1003 const char *name,
1004 PyObject *ob,
1005 const bool do_conversion,
1006 const bool can_create)
1007{
1008 if (name == nullptr) {
1009 return nullptr;
1010 }
1011
1012 if (PyFloat_Check(ob)) {
1013 return idp_from_PyFloat(prop_exist, name, ob, do_conversion, can_create);
1014 }
1015 if (PyBool_Check(ob)) {
1016 return idp_from_PyBool(prop_exist, name, ob, do_conversion, can_create);
1017 }
1018 if (PyLong_Check(ob)) {
1019 return idp_from_PyLong(prop_exist, name, ob, do_conversion, can_create);
1020 }
1021 if (PyUnicode_Check(ob)) {
1022 return idp_from_PyUnicode(prop_exist, name, ob, do_conversion, can_create);
1023 }
1024 if (PyBytes_Check(ob)) {
1025 return idp_from_PyBytes(prop_exist, name, ob, do_conversion, can_create);
1026 }
1027 if (PySequence_Check(ob)) {
1028 return idp_from_PySequence(prop_exist, name, ob, do_conversion, can_create);
1029 }
1030 if (ob == Py_None || pyrna_id_CheckPyObject(ob)) {
1031 return idp_from_DatablockPointer(prop_exist, name, ob, do_conversion, can_create);
1032 }
1033 if (PyMapping_Check(ob)) {
1034 return idp_from_PyMapping(prop_exist, name, ob, do_conversion, can_create);
1035 }
1036
1037 PyErr_Format(
1038 PyExc_TypeError, "invalid id-property type %.200s not supported", Py_TYPE(ob)->tp_name);
1039 return nullptr;
1040}
1041
1044/* -------------------------------------------------------------------- */
1048bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob)
1049{
1050 const char *name = idp_try_read_name(name_obj);
1051 if (!name) {
1052 return false;
1053 }
1054
1055 /* If the container is an array of IDProperties, always add a new property to it. */
1056 if (group->type == IDP_IDPARRAY) {
1057 IDProperty *new_prop = idp_from_PyObject(nullptr, name, ob, false, true);
1058 if (new_prop == nullptr) {
1059 return false;
1060 }
1061
1062 IDP_AppendArray(group, new_prop);
1063 /* IDP_AppendArray does a shallow copy (memcpy), only free memory */
1064 MEM_freeN(new_prop);
1065
1066 return true;
1067 }
1068
1069 IDProperty *prop_exist = IDP_GetPropertyFromGroup(group, name);
1070
1071 /* If existing property is flagged to be statically typed, do not re-type it. Assign the value if
1072 * possible (potentially converting it), or fail. See #122743. */
1073 if (prop_exist && (prop_exist->flag & IDP_FLAG_STATIC_TYPE) != 0) {
1074 IDProperty *prop = idp_from_PyObject(prop_exist, name, ob, true, false);
1075 BLI_assert(ELEM(prop, prop_exist, nullptr));
1076 if (prop != prop_exist) {
1077 PyErr_Format(PyExc_TypeError,
1078 "Cannot assign a '%.200s' value to the existing '%s' %s IDProperty",
1079 Py_TYPE(ob)->tp_name,
1080 name,
1081 IDP_type_str(prop_exist));
1082 return false;
1083 }
1084 return true;
1085 }
1086
1087 /* Attempt to assign new value in existing IDProperty, if types (and potentially subtypes) match
1088 * exactly. Otherwise, create a new IDProperty. */
1089 IDProperty *new_prop = idp_from_PyObject(prop_exist, name, ob, false, true);
1090 if (new_prop == nullptr) {
1091 return false;
1092 }
1093 if (new_prop == prop_exist) {
1094 return true;
1095 }
1096
1097 /* Property was created with no existing counterpart, just insert it in the group container. */
1098 if (!prop_exist) {
1099 IDP_ReplaceInGroup_ex(group, new_prop, nullptr);
1100 return true;
1101 }
1102
1103 /* Try to preserve UI data from the existing, replaced property. See: #37073. */
1104 if (prop_exist->ui_data) {
1105 /* Take ownership of the existing property's UI data. */
1106 const eIDPropertyUIDataType src_type = IDP_ui_data_type(prop_exist);
1107 IDPropertyUIData *ui_data = prop_exist->ui_data;
1108 prop_exist->ui_data = nullptr;
1109
1110 new_prop->ui_data = IDP_TryConvertUIData(ui_data, src_type, IDP_ui_data_type(new_prop));
1111 }
1112 /* Copy over the 'overridable' flag from existing property. */
1113 new_prop->flag |= (prop_exist->flag & IDP_FLAG_OVERRIDABLE_LIBRARY);
1114
1115 IDP_ReplaceInGroup_ex(group, new_prop, prop_exist);
1116 return true;
1117}
1118
1119int BPy_Wrap_SetMapItem(IDProperty *prop, PyObject *key, PyObject *val)
1120{
1121 if (prop->type != IDP_GROUP) {
1122 PyErr_SetString(PyExc_TypeError, "unsubscriptable object");
1123 return -1;
1124 }
1125
1126 if (val == nullptr) { /* del idprop[key] */
1127 IDProperty *pkey;
1128 const char *name = PyUnicode_AsUTF8(key);
1129
1130 if (name == nullptr) {
1131 PyErr_Format(PyExc_KeyError, "expected a string, not %.200s", Py_TYPE(key)->tp_name);
1132 return -1;
1133 }
1134
1135 pkey = IDP_GetPropertyFromGroup(prop, name);
1136 if (pkey) {
1137 IDP_FreeFromGroup(prop, pkey);
1138 return 0;
1139 }
1140
1141 PyErr_SetString(PyExc_KeyError, "property not found in group");
1142 return -1;
1143 }
1144
1145 bool ok;
1146
1147 ok = BPy_IDProperty_Map_ValidateAndCreate(key, prop, val);
1148 if (ok == false) {
1149 return -1;
1150 }
1151
1152 return 0;
1153}
1154
1155static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val)
1156{
1157 return BPy_Wrap_SetMapItem(self->prop, key, val);
1158}
1159
1161{
1162 PyObject *iterable = BPy_IDGroup_ViewKeys_CreatePyObject(self);
1163 PyObject *ret;
1164 if (iterable) {
1165 ret = PyObject_GetIter(iterable);
1166 Py_DECREF(iterable);
1167 }
1168 else {
1169 ret = nullptr;
1170 }
1171 return ret;
1172}
1173
1175{
1176 switch (prop->type) {
1177 case IDP_STRING:
1178 return idprop_py_from_idp_string(prop);
1179 case IDP_INT:
1180 return idprop_py_from_idp_int(prop);
1181 case IDP_FLOAT:
1182 return idprop_py_from_idp_float(prop);
1183 case IDP_DOUBLE:
1184 return idprop_py_from_idp_double(prop);
1185 case IDP_BOOLEAN:
1186 return idprop_py_from_idp_bool(prop);
1187 case IDP_ID:
1188 return idprop_py_from_idp_id(prop);
1189 case IDP_ARRAY: {
1190 PyObject *seq = PyList_New(prop->len);
1191 int i;
1192
1193 if (!seq) {
1194 PyErr_Format(
1195 PyExc_RuntimeError, "%s: IDP_ARRAY: PyList_New(%d) failed", __func__, prop->len);
1196 return nullptr;
1197 }
1198
1199 switch (prop->subtype) {
1200 case IDP_FLOAT: {
1201 const float *array = (float *)IDP_Array(prop);
1202 for (i = 0; i < prop->len; i++) {
1203 PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i]));
1204 }
1205 break;
1206 }
1207 case IDP_DOUBLE: {
1208 const double *array = (double *)IDP_Array(prop);
1209 for (i = 0; i < prop->len; i++) {
1210 PyList_SET_ITEM(seq, i, PyFloat_FromDouble(array[i]));
1211 }
1212 break;
1213 }
1214 case IDP_INT: {
1215 const int *array = (int *)IDP_Array(prop);
1216 for (i = 0; i < prop->len; i++) {
1217 PyList_SET_ITEM(seq, i, PyLong_FromLong(array[i]));
1218 }
1219 break;
1220 }
1221 case IDP_BOOLEAN: {
1222 const int8_t *array = (const int8_t *)IDP_Array(prop);
1223 for (i = 0; i < prop->len; i++) {
1224 PyList_SET_ITEM(seq, i, PyBool_FromLong(array[i]));
1225 }
1226 break;
1227 }
1228 default:
1229 PyErr_Format(
1230 PyExc_RuntimeError, "%s: invalid/corrupt array type '%d'!", __func__, prop->subtype);
1231 Py_DECREF(seq);
1232 return nullptr;
1233 }
1234
1235 return seq;
1236 }
1237 case IDP_IDPARRAY: {
1238 PyObject *seq = PyList_New(prop->len);
1239 IDProperty *array = IDP_IDPArray(prop);
1240 int i;
1241
1242 if (!seq) {
1243 PyErr_Format(
1244 PyExc_RuntimeError, "%s: IDP_IDPARRAY: PyList_New(%d) failed", __func__, prop->len);
1245 return nullptr;
1246 }
1247
1248 for (i = 0; i < prop->len; i++) {
1249 PyObject *wrap = BPy_IDGroup_MapDataToPy(array++);
1250
1251 /* BPy_IDGroup_MapDataToPy sets the error */
1252 if (UNLIKELY(wrap == nullptr)) {
1253 Py_DECREF(seq);
1254 return nullptr;
1255 }
1256
1257 PyList_SET_ITEM(seq, i, wrap);
1258 }
1259 return seq;
1260 }
1261 case IDP_GROUP: {
1262 PyObject *dict = _PyDict_NewPresized(prop->len);
1263 IDProperty *loop;
1264
1265 for (loop = static_cast<IDProperty *>(prop->data.group.first); loop; loop = loop->next) {
1266 PyObject *wrap = BPy_IDGroup_MapDataToPy(loop);
1267
1268 /* BPy_IDGroup_MapDataToPy sets the error */
1269 if (UNLIKELY(wrap == nullptr)) {
1270 Py_DECREF(dict);
1271 return nullptr;
1272 }
1273
1274 PyDict_SetItemString(dict, loop->name, wrap);
1275 Py_DECREF(wrap);
1276 }
1277 return dict;
1278 }
1279 }
1280
1281 PyErr_Format(PyExc_RuntimeError,
1282 "%s ERROR: '%s' property exists with a bad type code '%d'!",
1283 __func__,
1284 prop->name,
1285 prop->type);
1286 return nullptr;
1287}
1288
1291/* -------------------------------------------------------------------- */
1296{
1297 if (self->group == nullptr) {
1298 return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
1299 }
1300 return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
1301}
1302
1304{
1305 if (self->group != nullptr) {
1306 PyObject_GC_UnTrack(self);
1307 }
1308 Py_CLEAR(self->group);
1309 PyObject_GC_Del(self);
1310}
1311
1312static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
1313{
1314 Py_VISIT(self->group);
1315 return 0;
1316}
1317
1319{
1320 Py_CLEAR(self->group);
1321 return 0;
1322}
1323
1325{
1326 return (self->group != nullptr);
1327}
1328
1330{
1331 if (self->len_init == self->group->prop->len) {
1332 return true;
1333 }
1334 PyErr_SetString(PyExc_RuntimeError, "IDPropertyGroup changed size during iteration");
1335 return false;
1336}
1337
1339{
1340 if (self->cur != nullptr) {
1341 /* When `cur` is set, `group` cannot be nullptr. */
1343 return nullptr;
1344 }
1345 IDProperty *cur = self->cur;
1346 self->cur = self->reversed ? self->cur->prev : self->cur->next;
1347 return PyUnicode_FromString(cur->name);
1348 }
1349 PyErr_SetNone(PyExc_StopIteration);
1350 return nullptr;
1351}
1352
1354{
1355 if (self->cur != nullptr) {
1356 /* When `cur` is set, `group` cannot be nullptr. */
1358 return nullptr;
1359 }
1360 IDProperty *cur = self->cur;
1361 self->cur = self->reversed ? self->cur->prev : self->cur->next;
1362 return BPy_IDGroup_WrapData(self->group->owner_id, cur, self->group->prop);
1363 }
1364 PyErr_SetNone(PyExc_StopIteration);
1365 return nullptr;
1366}
1367
1369{
1370 if (self->cur != nullptr) {
1371 /* When `cur` is set, `group` cannot be nullptr. */
1373 return nullptr;
1374 }
1375 IDProperty *cur = self->cur;
1376 self->cur = self->reversed ? self->cur->prev : self->cur->next;
1377 PyObject *ret = PyTuple_New(2);
1379 PyUnicode_FromString(cur->name),
1380 BPy_IDGroup_WrapData(self->group->owner_id, cur, self->group->prop));
1381 return ret;
1382 }
1383 PyErr_SetNone(PyExc_StopIteration);
1384 return nullptr;
1385}
1386
1387PyTypeObject BPy_IDGroup_IterKeys_Type = {PyVarObject_HEAD_INIT(nullptr, 0)};
1388PyTypeObject BPy_IDGroup_IterValues_Type = {PyVarObject_HEAD_INIT(nullptr, 0)};
1389PyTypeObject BPy_IDGroup_IterItems_Type = {PyVarObject_HEAD_INIT(nullptr, 0)};
1390
1391/* ID Property Group Iterator. */
1393{
1394#define SHARED_MEMBER_SET(member, value) \
1395 { \
1396 k_ty->member = v_ty->member = i_ty->member = value; \
1397 } \
1398 ((void)0)
1399
1400 PyTypeObject *k_ty = &BPy_IDGroup_IterKeys_Type;
1401 PyTypeObject *v_ty = &BPy_IDGroup_IterValues_Type;
1402 PyTypeObject *i_ty = &BPy_IDGroup_IterItems_Type;
1403
1404 /* Unique members. */
1405 k_ty->tp_name = "IDPropertyGroupIterKeys";
1406 v_ty->tp_name = "IDPropertyGroupIterValues";
1407 i_ty->tp_name = "IDPropertyGroupIterItems";
1408
1409 k_ty->tp_iternext = (iternextfunc)BPy_Group_IterKeys_next;
1410 v_ty->tp_iternext = (iternextfunc)BPy_Group_IterValues_next;
1411 i_ty->tp_iternext = (iternextfunc)BPy_Group_IterItems_next;
1412
1413 /* Shared members. */
1414 SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_Iter));
1415 SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_Iter_dealloc);
1416 SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_Iter_repr);
1417 SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
1418 SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_Iter_traverse);
1419 SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_Iter_clear);
1420 SHARED_MEMBER_SET(tp_is_gc, (inquiry)BPy_IDGroup_Iter_is_gc);
1421 SHARED_MEMBER_SET(tp_iter, PyObject_SelfIter);
1422
1423#undef SHARED_MEMBER_SET
1424}
1425
1427 const bool reversed,
1428 PyTypeObject *type)
1429{
1430 BLI_assert(group ? group->prop->type == IDP_GROUP : true);
1431 BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, type);
1432 iter->reversed = reversed;
1433 iter->group = group;
1434 if (group != nullptr) {
1435 Py_INCREF(group);
1436 BLI_assert(!PyObject_GC_IsTracked((PyObject *)iter));
1437 PyObject_GC_Track(iter);
1438 iter->cur = static_cast<IDProperty *>(
1439 (reversed ? group->prop->data.group.last : group->prop->data.group.first));
1440 iter->len_init = group->prop->len;
1441 }
1442 else {
1443 iter->cur = nullptr;
1444 iter->len_init = 0;
1445 }
1446 return (PyObject *)iter;
1447}
1448
1449static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed)
1450{
1451 return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterKeys_Type);
1452}
1453
1454static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed)
1455{
1457}
1458
1459static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed)
1460{
1461 return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterItems_Type);
1462}
1463
1466/* -------------------------------------------------------------------- */
1481{
1482 if (self->group == nullptr) {
1483 return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
1484 }
1485 return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
1486}
1487
1489{
1490 if (self->group != nullptr) {
1491 PyObject_GC_UnTrack(self);
1492 }
1493 Py_CLEAR(self->group);
1494 PyObject_GC_Del(self);
1495}
1496
1497static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg)
1498{
1499 Py_VISIT(self->group);
1500 return 0;
1501}
1502
1504{
1505 Py_CLEAR(self->group);
1506 return 0;
1507}
1508
1510{
1511 return (self->group != nullptr);
1512}
1513
1514/* View Specific API's (Key/Value/Items). */
1515
1517{
1518 return BPy_IDGroup_IterKeys_CreatePyObject(self->group, self->reversed);
1519}
1520
1522{
1523 return BPy_IDGroup_IterValues_CreatePyObject(self->group, self->reversed);
1524}
1525
1527{
1528 return BPy_IDGroup_IterItems_CreatePyObject(self->group, self->reversed);
1529}
1530
1532{
1533 if (self->group == nullptr) {
1534 return 0;
1535 }
1536 return self->group->prop->len;
1537}
1538
1540{
1541 if (self->group == nullptr) {
1542 return 0;
1543 }
1544 return BPy_IDGroup_Contains(self->group, value);
1545}
1546
1548{
1549 if (self->group == nullptr) {
1550 return 0;
1551 }
1552 /* TODO: implement this without first converting to a list. */
1553 PyObject *list = PySequence_List((PyObject *)self);
1554 const int result = PySequence_Contains(list, value);
1555 Py_DECREF(list);
1556 return result;
1557}
1558
1560{
1561 if (self->group == nullptr) {
1562 return 0;
1563 }
1564 /* TODO: implement this without first converting to a list. */
1565 PyObject *list = PySequence_List((PyObject *)self);
1566 const int result = PySequence_Contains(list, value);
1567 Py_DECREF(list);
1568 return result;
1569}
1570
1571static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence = {
1572 /*sq_length*/ (lenfunc)BPy_Group_View_len,
1573 /*sq_concat*/ nullptr,
1574 /*sq_repeat*/ nullptr,
1575 /*sq_item*/ nullptr,
1576 /*was_sq_slice*/ nullptr,
1577 /*sq_ass_item*/ nullptr,
1578 /*was_sq_ass_slice*/ nullptr,
1579 /*sq_contains*/ (objobjproc)BPy_Group_ViewKeys_Contains,
1580};
1581
1582static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence = {
1583 /*sq_length*/ (lenfunc)BPy_Group_View_len,
1584 /*sq_concat*/ nullptr,
1585 /*sq_repeat*/ nullptr,
1586 /*sq_item*/ nullptr,
1587 /*was_sq_slice*/ nullptr,
1588 /*sq_ass_item*/ nullptr,
1589 /*was_sq_ass_slice*/ nullptr,
1590 /*sq_contains*/ (objobjproc)BPy_Group_ViewValues_Contains,
1591};
1592
1593static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence = {
1594 /*sq_length*/ (lenfunc)BPy_Group_View_len,
1595 /*sq_concat*/ nullptr,
1596 /*sq_repeat*/ nullptr,
1597 /*sq_item*/ nullptr,
1598 /*was_sq_slice*/ nullptr,
1599 /*sq_ass_item*/ nullptr,
1600 /*was_sq_ass_slice*/ nullptr,
1601 /*sq_contains*/ (objobjproc)BPy_Group_ViewItems_Contains,
1602};
1603
1604/* Methods. */
1605
1607 /* Wrap. */
1608 BPy_IDGroup_View_reversed_doc,
1609 "Return a reverse iterator over the ID Property keys values or items.");
1610
1611static PyObject *BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject * /*ignored*/)
1612{
1613 BPy_IDGroup_View *result = IDGroup_View_New_WithType(self->group, Py_TYPE(self));
1614 result->reversed = !self->reversed;
1615 return (PyObject *)result;
1616}
1617
1618static PyMethodDef BPy_IDGroup_View_methods[] = {
1619 {"__reversed__",
1620 (PyCFunction)(void (*)(void))BPy_IDGroup_View_reversed,
1621 METH_NOARGS,
1622 BPy_IDGroup_View_reversed_doc},
1623 {nullptr, nullptr},
1624};
1625
1626PyTypeObject BPy_IDGroup_ViewKeys_Type = {PyVarObject_HEAD_INIT(nullptr, 0)};
1627PyTypeObject BPy_IDGroup_ViewValues_Type = {PyVarObject_HEAD_INIT(nullptr, 0)};
1628PyTypeObject BPy_IDGroup_ViewItems_Type = {PyVarObject_HEAD_INIT(nullptr, 0)};
1629
1630/* ID Property Group View. */
1632{
1633 PyTypeObject *k_ty = &BPy_IDGroup_ViewKeys_Type;
1634 PyTypeObject *v_ty = &BPy_IDGroup_ViewValues_Type;
1635 PyTypeObject *i_ty = &BPy_IDGroup_ViewItems_Type;
1636
1637 /* Unique members. */
1638 k_ty->tp_name = "IDPropertyGroupViewKeys";
1639 v_ty->tp_name = "IDPropertyGroupViewValues";
1640 i_ty->tp_name = "IDPropertyGroupViewItems";
1641
1642 k_ty->tp_iter = (getiterfunc)BPy_Group_ViewKeys_iter;
1643 v_ty->tp_iter = (getiterfunc)BPy_Group_ViewValues_iter;
1644 i_ty->tp_iter = (getiterfunc)BPy_Group_ViewItems_iter;
1645
1646 k_ty->tp_as_sequence = &BPy_IDGroup_ViewKeys_as_sequence;
1647 v_ty->tp_as_sequence = &BPy_IDGroup_ViewValues_as_sequence;
1648 i_ty->tp_as_sequence = &BPy_IDGroup_ViewItems_as_sequence;
1649
1650/* Shared members. */
1651#define SHARED_MEMBER_SET(member, value) \
1652 { \
1653 k_ty->member = v_ty->member = i_ty->member = value; \
1654 } \
1655 ((void)0)
1656
1657 SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_View));
1658 SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_View_dealloc);
1659 SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_View_repr);
1660 SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
1661 SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_View_traverse);
1662 SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_View_clear);
1663 SHARED_MEMBER_SET(tp_is_gc, (inquiry)BPy_IDGroup_View_is_gc);
1665
1666#undef SHARED_MEMBER_SET
1667}
1668
1671/* -------------------------------------------------------------------- */
1676 /* Wrap. */
1677 BPy_IDGroup_pop_doc,
1678 ".. method:: pop(key, default)\n"
1679 "\n"
1680 " Remove an item from the group, returning a Python representation.\n"
1681 "\n"
1682 " :raises KeyError: When the item doesn't exist.\n"
1683 "\n"
1684 " :arg key: Name of item to remove.\n"
1685 " :type key: str\n"
1686 " :arg default: Value to return when key isn't found, otherwise raise an exception.\n"
1687 " :type default: Any\n");
1688static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args)
1689{
1690 IDProperty *idprop;
1691 PyObject *pyform;
1692
1693 char *key;
1694 PyObject *def = nullptr;
1695
1696 if (!PyArg_ParseTuple(args, "s|O:get", &key, &def)) {
1697 return nullptr;
1698 }
1699
1700 idprop = IDP_GetPropertyFromGroup(self->prop, key);
1701 if (idprop == nullptr) {
1702 if (def == nullptr) {
1703 PyErr_SetString(PyExc_KeyError, "item not in group");
1704 return nullptr;
1705 }
1706 return Py_NewRef(def);
1707 }
1708
1709 pyform = BPy_IDGroup_MapDataToPy(idprop);
1710 if (pyform == nullptr) {
1711 /* Ok something bad happened with the #PyObject, so don't remove the prop from the group.
1712 * if `pyform` is nullptr, then it already should have raised an exception. */
1713 return nullptr;
1714 }
1715
1716 IDP_FreeFromGroup(self->prop, idprop);
1717 return pyform;
1718}
1719
1720/* utility function */
1721static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
1722{
1723 int j;
1724
1725 printf("%s: ID Property Error found and corrected!\n", func);
1726
1727 /* fill rest of list with valid references to None */
1728 for (j = len; j < prop->len; j++) {
1729 PyList_SET_ITEM(seq, j, Py_NewRef(Py_None));
1730 }
1731
1732 /* Set correct group length. */
1733 prop->len = len;
1734}
1735
1737{
1738 PyObject *list = PyList_New(prop->len);
1739 IDProperty *loop;
1740 int i;
1741
1742 for (i = 0, loop = static_cast<IDProperty *>(prop->data.group.first); loop && (i < prop->len);
1743 loop = loop->next, i++)
1744 {
1745 PyList_SET_ITEM(list, i, PyUnicode_FromString(loop->name));
1746 }
1747
1748 /* if the id prop is corrupt, count the remaining */
1749 for (; loop; loop = loop->next, i++) {
1750 /* pass */
1751 }
1752
1753 if (i != prop->len) { /* if the loop didn't finish, we know the length is wrong */
1754 BPy_IDGroup_CorrectListLen(prop, list, i, __func__);
1755 Py_DECREF(list); /* Free the list. */
1756 /* Call self again. */
1757 return BPy_Wrap_GetKeys(prop);
1758 }
1759
1760 return list;
1761}
1762
1763PyObject *BPy_Wrap_GetValues(ID *id, IDProperty *prop)
1764{
1765 PyObject *list = PyList_New(prop->len);
1766 IDProperty *loop;
1767 int i;
1768
1769 for (i = 0, loop = static_cast<IDProperty *>(prop->data.group.first); loop;
1770 loop = loop->next, i++)
1771 {
1772 PyList_SET_ITEM(list, i, BPy_IDGroup_WrapData(id, loop, prop));
1773 }
1774
1775 if (i != prop->len) {
1776 BPy_IDGroup_CorrectListLen(prop, list, i, __func__);
1777 Py_DECREF(list); /* Free the list. */
1778 /* Call self again. */
1779 return BPy_Wrap_GetValues(id, prop);
1780 }
1781
1782 return list;
1783}
1784
1785PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
1786{
1787 PyObject *seq = PyList_New(prop->len);
1788 IDProperty *loop;
1789 int i;
1790
1791 for (i = 0, loop = static_cast<IDProperty *>(prop->data.group.first); loop;
1792 loop = loop->next, i++)
1793 {
1794 PyObject *item = PyTuple_New(2);
1796 item, PyUnicode_FromString(loop->name), BPy_IDGroup_WrapData(id, loop, prop));
1797 PyList_SET_ITEM(seq, i, item);
1798 }
1799
1800 if (i != prop->len) {
1801 BPy_IDGroup_CorrectListLen(prop, seq, i, __func__);
1802 Py_DECREF(seq); /* Free the list. */
1803 /* Call self again. */
1804 return BPy_Wrap_GetItems(id, prop);
1805 }
1806
1807 return seq;
1808}
1809
1811{
1812 PyObject *self = prop ? idprop_py_from_idp_group(id, prop, nullptr) : nullptr;
1814 Py_XDECREF(self); /* Owned by `ret`. */
1815 return ret;
1816}
1817
1819{
1820 PyObject *self = prop ? idprop_py_from_idp_group(id, prop, nullptr) : nullptr;
1822 Py_XDECREF(self); /* Owned by `ret`. */
1823 return ret;
1824}
1825
1827{
1828 PyObject *self = prop ? idprop_py_from_idp_group(id, prop, nullptr) : nullptr;
1830 Py_XDECREF(self); /* Owned by `ret`. */
1831 return ret;
1832}
1833
1835 /* Wrap. */
1836 BPy_IDGroup_keys_doc,
1837 ".. method:: keys()\n"
1838 "\n"
1839 " Return the keys associated with this group as a list of strings.\n");
1844
1846 /* Wrap. */
1847 BPy_IDGroup_values_doc,
1848 ".. method:: values()\n"
1849 "\n"
1850 " Return the values associated with this group.\n");
1855
1857 /* Wrap. */
1858 BPy_IDGroup_items_doc,
1859 ".. method:: items()\n"
1860 "\n"
1861 " Iterate through the items in the dict; behaves like dictionary method items.\n");
1866
1867static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
1868{
1869 const char *name = PyUnicode_AsUTF8(value);
1870
1871 if (!name) {
1872 PyErr_Format(PyExc_TypeError, "expected a string, not a %.200s", Py_TYPE(value)->tp_name);
1873 return -1;
1874 }
1875
1876 return IDP_GetPropertyFromGroup(self->prop, name) ? 1 : 0;
1877}
1878
1880 /* Wrap. */
1881 BPy_IDGroup_update_doc,
1882 ".. method:: update(other)\n"
1883 "\n"
1884 " Update key, values.\n"
1885 "\n"
1886 " :arg other: Updates the values in the group with this.\n"
1887 /* TODO: replace `Any` with an alias for all types an ID property can use. */
1888 " :type other: :class:`IDPropertyGroup` | dict[str, Any]\n");
1889static PyObject *BPy_IDGroup_update(BPy_IDProperty *self, PyObject *value)
1890{
1891 PyObject *pkey, *pval;
1892 Py_ssize_t i = 0;
1893
1894 if (BPy_IDGroup_Check(value)) {
1895 BPy_IDProperty *other = (BPy_IDProperty *)value;
1896 if (UNLIKELY(self->prop == other->prop)) {
1897 Py_RETURN_NONE;
1898 }
1899
1900 /* XXX, possible one is inside the other */
1901 IDP_MergeGroup(self->prop, other->prop, true);
1902 }
1903 else if (PyDict_Check(value)) {
1904 while (PyDict_Next(value, &i, &pkey, &pval)) {
1905 BPy_IDGroup_Map_SetItem(self, pkey, pval);
1906 if (PyErr_Occurred()) {
1907 return nullptr;
1908 }
1909 }
1910 }
1911 else {
1912 PyErr_Format(PyExc_TypeError,
1913 "expected a dict or an IDPropertyGroup type, not a %.200s",
1914 Py_TYPE(value)->tp_name);
1915 return nullptr;
1916 }
1917
1918 Py_RETURN_NONE;
1919}
1920
1922 /* Wrap. */
1923 BPy_IDGroup_to_dict_doc,
1924 ".. method:: to_dict()\n"
1925 "\n"
1926 " Return a purely Python version of the group.\n");
1928{
1929 return BPy_IDGroup_MapDataToPy(self->prop);
1930}
1931
1933 /* Wrap. */
1934 BPy_IDGroup_clear_doc,
1935 ".. method:: clear()\n"
1936 "\n"
1937 " Clear all members from this group.\n");
1939{
1940 IDP_ClearProperty(self->prop);
1941 Py_RETURN_NONE;
1942}
1943
1945 /* Wrap. */
1946 BPy_IDGroup_get_doc,
1947 ".. method:: get(key, default=None)\n"
1948 "\n"
1949 " Return the value for key, if it exists, else default.\n");
1950static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
1951{
1952 IDProperty *idprop;
1953 const char *key;
1954 PyObject *def = Py_None;
1955
1956 if (!PyArg_ParseTuple(args, "s|O:get", &key, &def)) {
1957 return nullptr;
1958 }
1959
1960 idprop = IDP_GetPropertyFromGroup(self->prop, key);
1961 if (idprop) {
1962 PyObject *pyobj = BPy_IDGroup_WrapData(self->owner_id, idprop, self->prop);
1963 if (pyobj) {
1964 return pyobj;
1965 }
1966 }
1967
1968 Py_INCREF(def);
1969 return def;
1970}
1971
1972#if (defined(__GNUC__) && !defined(__clang__))
1973# pragma GCC diagnostic push
1974# pragma GCC diagnostic ignored "-Wcast-function-type"
1975#endif
1976
1977static PyMethodDef BPy_IDGroup_methods[] = {
1978 {"pop", (PyCFunction)BPy_IDGroup_pop, METH_VARARGS, BPy_IDGroup_pop_doc},
1979 {"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
1980 {"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
1981 {"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
1982 {"update", (PyCFunction)BPy_IDGroup_update, METH_O, BPy_IDGroup_update_doc},
1983 {"get", (PyCFunction)BPy_IDGroup_get, METH_VARARGS, BPy_IDGroup_get_doc},
1984 {"to_dict", (PyCFunction)BPy_IDGroup_to_dict, METH_NOARGS, BPy_IDGroup_to_dict_doc},
1985 {"clear", (PyCFunction)BPy_IDGroup_clear, METH_NOARGS, BPy_IDGroup_clear_doc},
1986 {nullptr, nullptr, 0, nullptr},
1987};
1988
1989#if (defined(__GNUC__) && !defined(__clang__))
1990# pragma GCC diagnostic pop
1991#endif
1992
1995/* -------------------------------------------------------------------- */
1999static PySequenceMethods BPy_IDGroup_Seq = {
2000 /*sq_length*/ (lenfunc)BPy_IDGroup_Map_Len,
2001 /*sq_concat*/ nullptr,
2002 /*sq_repeat*/ nullptr,
2003 /* TODO: setting this will allow `PySequence_Check()` to return True. */
2004 /*sq_item*/ nullptr,
2005 /*was_sq_slice*/ nullptr, /* DEPRECATED. */
2006 /*sq_ass_item*/ nullptr,
2007 /*was_sq_ass_slice*/ nullptr, /* DEPRECATED. */
2008 /*sq_contains*/ (objobjproc)BPy_IDGroup_Contains,
2009 /*sq_inplace_concat*/ nullptr,
2010 /*sq_inplace_repeat*/ nullptr,
2011};
2012
2013static PyMappingMethods BPy_IDGroup_Mapping = {
2014 /*mp_length*/ (lenfunc)BPy_IDGroup_Map_Len,
2015 /*mp_subscript*/ (binaryfunc)BPy_IDGroup_Map_GetItem,
2016 /*mp_ass_subscript*/ (objobjargproc)BPy_IDGroup_Map_SetItem,
2017};
2018
2019PyTypeObject BPy_IDGroup_Type = {
2020 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
2021 /* For printing, in format `<module>.<name>`. */
2022 /*tp_name*/ "IDPropertyGroup",
2023 /*tp_basicsize*/ sizeof(BPy_IDProperty),
2024 /*tp_itemsize*/ 0,
2025 /*tp_dealloc*/ nullptr,
2026 /*tp_vectorcall_offset*/ 0,
2027 /*tp_getattr*/ nullptr,
2028 /*tp_setattr*/ nullptr,
2029 /*tp_as_async*/ nullptr,
2030 /*tp_repr*/ (reprfunc)BPy_IDGroup_repr,
2031 /*tp_as_number*/ nullptr,
2032 /*tp_as_sequence*/ &BPy_IDGroup_Seq,
2033 /*tp_as_mapping*/ &BPy_IDGroup_Mapping,
2034 /*tp_hash*/ (hashfunc)BPy_IDGroup_hash,
2035 /*tp_call*/ nullptr,
2036 /*tp_str*/ nullptr,
2037 /*tp_getattro*/ nullptr,
2038 /*tp_setattro*/ nullptr,
2039 /*tp_as_buffer*/ nullptr,
2040 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
2041 /*tp_doc*/ nullptr,
2042 /*tp_traverse*/ nullptr,
2043 /*tp_clear*/ nullptr,
2044 /*tp_richcompare*/ nullptr,
2045 /*tp_weaklistoffset*/ 0,
2046 /*tp_iter*/ (getiterfunc)BPy_IDGroup_iter,
2047 /*tp_iternext*/ nullptr,
2048 /*tp_methods*/ BPy_IDGroup_methods,
2049 /*tp_members*/ nullptr,
2050 /*tp_getset*/ BPy_IDGroup_getseters,
2051 /*tp_base*/ nullptr,
2052 /*tp_dict*/ nullptr,
2053 /*tp_descr_get*/ nullptr,
2054 /*tp_descr_set*/ nullptr,
2055 /*tp_dictoffset*/ 0,
2056 /*tp_init*/ nullptr,
2057 /*tp_alloc*/ nullptr,
2058 /*tp_new*/ nullptr,
2059 /*tp_free*/ nullptr,
2060 /*tp_is_gc*/ nullptr,
2061 /*tp_bases*/ nullptr,
2062 /*tp_mro*/ nullptr,
2063 /*tp_cache*/ nullptr,
2064 /*tp_subclasses*/ nullptr,
2065 /*tp_weaklist*/ nullptr,
2066 /*tp_del*/ nullptr,
2067 /*tp_version_tag*/ 0,
2068 /*tp_finalize*/ nullptr,
2069 /*tp_vectorcall*/ nullptr,
2070};
2071
2074/* -------------------------------------------------------------------- */
2078static PyTypeObject *idp_array_py_type(BPy_IDArray *self, size_t *elem_size)
2079{
2080 switch (self->prop->subtype) {
2081 case IDP_FLOAT:
2082 *elem_size = sizeof(float);
2083 return &PyFloat_Type;
2084 case IDP_DOUBLE:
2085 *elem_size = sizeof(double);
2086 return &PyFloat_Type;
2087 case IDP_BOOLEAN:
2088 *elem_size = sizeof(int8_t);
2089 return &PyBool_Type;
2090 case IDP_INT:
2091 *elem_size = sizeof(int);
2092 return &PyLong_Type;
2093 default:
2094 *elem_size = 0;
2095 return nullptr;
2096 }
2097}
2098
2100{
2101 return PyUnicode_FromFormat("<bpy id property array [%d]>", self->prop->len);
2102}
2103
2105 /* Wrap. */
2106 BPy_IDArray_get_typecode_doc,
2107 "The type of the data in the array {'f': float, 'd': double, 'i': int, 'b': bool}.");
2108static PyObject *BPy_IDArray_get_typecode(BPy_IDArray *self, void * /*closure*/)
2109{
2110 const char *typecode;
2111 switch (self->prop->subtype) {
2112 case IDP_FLOAT:
2113 typecode = "f";
2114 break;
2115 case IDP_DOUBLE:
2116 typecode = "d";
2117 break;
2118 case IDP_INT:
2119 typecode = "i";
2120 break;
2121 case IDP_BOOLEAN:
2122 typecode = "b";
2123 break;
2124 default: {
2125 PyErr_Format(PyExc_RuntimeError,
2126 "%s: invalid/corrupt array type '%d'!",
2127 __func__,
2128 self->prop->subtype);
2129
2130 return nullptr;
2131 }
2132 }
2133 return PyUnicode_FromString(typecode);
2134}
2135
2136static PyGetSetDef BPy_IDArray_getseters[] = {
2137 /* matches pythons array.typecode */
2138 {"typecode",
2140 (setter) nullptr,
2141 BPy_IDArray_get_typecode_doc,
2142 nullptr},
2143 {nullptr, nullptr, nullptr, nullptr, nullptr},
2144};
2145
2147 /* Wrap. */
2148 BPy_IDArray_to_list_doc,
2149 ".. method:: to_list()\n"
2150 "\n"
2151 " Return the array as a list.\n");
2153{
2154 return BPy_IDGroup_MapDataToPy(self->prop);
2155}
2156
2157#if (defined(__GNUC__) && !defined(__clang__))
2158# pragma GCC diagnostic push
2159# pragma GCC diagnostic ignored "-Wcast-function-type"
2160#endif
2161
2162static PyMethodDef BPy_IDArray_methods[] = {
2163 {"to_list", (PyCFunction)BPy_IDArray_to_list, METH_NOARGS, BPy_IDArray_to_list_doc},
2164 {nullptr, nullptr, 0, nullptr},
2165};
2166
2167#if (defined(__GNUC__) && !defined(__clang__))
2168# pragma GCC diagnostic pop
2169#endif
2170
2172{
2173 return self->prop->len;
2174}
2175
2176static PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, Py_ssize_t index)
2177{
2178 if (index < 0 || index >= self->prop->len) {
2179 PyErr_SetString(PyExc_IndexError, "index out of range!");
2180 return nullptr;
2181 }
2182
2183 switch (self->prop->subtype) {
2184 case IDP_FLOAT:
2185 return PyFloat_FromDouble(((float *)IDP_Array(self->prop))[index]);
2186 case IDP_DOUBLE:
2187 return PyFloat_FromDouble(((double *)IDP_Array(self->prop))[index]);
2188 case IDP_INT:
2189 return PyLong_FromLong(long(((int *)IDP_Array(self->prop))[index]));
2190 case IDP_BOOLEAN:
2191 return PyBool_FromLong(long(((int8_t *)IDP_Array(self->prop))[index]));
2192 }
2193
2194 PyErr_Format(
2195 PyExc_RuntimeError, "%s: invalid/corrupt array type '%d'!", __func__, self->prop->subtype);
2196
2197 return nullptr;
2198}
2199
2200static int BPy_IDArray_SetItem(BPy_IDArray *self, Py_ssize_t index, PyObject *value)
2201{
2202 if (index < 0 || index >= self->prop->len) {
2203 PyErr_SetString(PyExc_RuntimeError, "index out of range!");
2204 return -1;
2205 }
2206
2207 switch (self->prop->subtype) {
2208 case IDP_FLOAT: {
2209 const float f = float(PyFloat_AsDouble(value));
2210 if (f == -1 && PyErr_Occurred()) {
2211 return -1;
2212 }
2213 ((float *)IDP_Array(self->prop))[index] = f;
2214 break;
2215 }
2216 case IDP_DOUBLE: {
2217 const double d = PyFloat_AsDouble(value);
2218 if (d == -1 && PyErr_Occurred()) {
2219 return -1;
2220 }
2221 ((double *)IDP_Array(self->prop))[index] = d;
2222 break;
2223 }
2224 case IDP_INT: {
2225 const int i = PyC_Long_AsI32(value);
2226 if (i == -1 && PyErr_Occurred()) {
2227 return -1;
2228 }
2229
2230 ((int *)IDP_Array(self->prop))[index] = i;
2231 break;
2232 }
2233 case IDP_BOOLEAN: {
2234 const int i = PyC_Long_AsBool(value);
2235 if (i == -1 && PyErr_Occurred()) {
2236 return -1;
2237 }
2238
2239 ((int8_t *)IDP_Array(self->prop))[index] = i;
2240 break;
2241 }
2242 }
2243 return 0;
2244}
2245
2246static PySequenceMethods BPy_IDArray_Seq = {
2247 /*sq_length*/ (lenfunc)BPy_IDArray_Len,
2248 /*sq_concat*/ nullptr,
2249 /*sq_repeat*/ nullptr,
2250 /*sq_item*/ (ssizeargfunc)BPy_IDArray_GetItem,
2251 /*was_sq_slice*/ nullptr, /* DEPRECATED. */
2252 /*sq_ass_item*/ (ssizeobjargproc)BPy_IDArray_SetItem,
2253 /*was_sq_ass_slice*/ nullptr, /* DEPRECATED. */
2254 /*sq_contains*/ nullptr,
2255 /*sq_inplace_concat*/ nullptr,
2256 /*sq_inplace_repeat*/ nullptr,
2257};
2258
2259/* sequence slice (get): idparr[a:b] */
2260static PyObject *BPy_IDArray_slice(BPy_IDArray *self, int begin, int end)
2261{
2262 IDProperty *prop = self->prop;
2263 PyObject *tuple;
2264 int count;
2265
2266 CLAMP(begin, 0, prop->len);
2267 if (end < 0) {
2268 end = prop->len + end + 1;
2269 }
2270 CLAMP(end, 0, prop->len);
2271 begin = std::min(begin, end);
2272
2273 tuple = PyTuple_New(end - begin);
2274
2275 switch (prop->subtype) {
2276 case IDP_FLOAT: {
2277 const float *array = (float *)IDP_Array(prop);
2278 for (count = begin; count < end; count++) {
2279 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count]));
2280 }
2281 break;
2282 }
2283 case IDP_DOUBLE: {
2284 const double *array = (double *)IDP_Array(prop);
2285 for (count = begin; count < end; count++) {
2286 PyTuple_SET_ITEM(tuple, count - begin, PyFloat_FromDouble(array[count]));
2287 }
2288 break;
2289 }
2290 case IDP_INT: {
2291 const int *array = (int *)IDP_Array(prop);
2292 for (count = begin; count < end; count++) {
2293 PyTuple_SET_ITEM(tuple, count - begin, PyLong_FromLong(array[count]));
2294 }
2295 break;
2296 }
2297 case IDP_BOOLEAN: {
2298 const int8_t *array = (const int8_t *)IDP_Array(prop);
2299 for (count = begin; count < end; count++) {
2300 PyTuple_SET_ITEM(tuple, count - begin, PyBool_FromLong(long(array[count])));
2301 }
2302 break;
2303 }
2304 }
2305
2306 return tuple;
2307}
2308/* sequence slice (set): idparr[a:b] = value */
2309static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject *seq)
2310{
2311 IDProperty *prop = self->prop;
2312 size_t elem_size;
2313 const PyTypeObject *py_type = idp_array_py_type(self, &elem_size);
2314 size_t alloc_len;
2315 size_t size;
2316 void *vec;
2317
2318 CLAMP(begin, 0, prop->len);
2319 CLAMP(end, 0, prop->len);
2320 begin = std::min(begin, end);
2321
2322 size = (end - begin);
2323 alloc_len = size * elem_size;
2324
2325 /* NOTE: we count on int/float being the same size here */
2326 vec = MEM_mallocN(alloc_len, "array assignment");
2327
2328 if (PyC_AsArray(vec, elem_size, seq, size, py_type, "slice assignment: ") == -1) {
2329 MEM_freeN(vec);
2330 return -1;
2331 }
2332
2333 memcpy((void *)(((char *)IDP_Array(prop)) + (begin * elem_size)), vec, alloc_len);
2334
2335 MEM_freeN(vec);
2336 return 0;
2337}
2338
2339static PyObject *BPy_IDArray_subscript(BPy_IDArray *self, PyObject *item)
2340{
2341 if (PyIndex_Check(item)) {
2342 Py_ssize_t i;
2343 i = PyNumber_AsSsize_t(item, PyExc_IndexError);
2344 if (i == -1 && PyErr_Occurred()) {
2345 return nullptr;
2346 }
2347 if (i < 0) {
2348 i += self->prop->len;
2349 }
2350 return BPy_IDArray_GetItem(self, i);
2351 }
2352 if (PySlice_Check(item)) {
2353 Py_ssize_t start, stop, step, slicelength;
2354
2355 if (PySlice_GetIndicesEx(item, self->prop->len, &start, &stop, &step, &slicelength) < 0) {
2356 return nullptr;
2357 }
2358
2359 if (slicelength <= 0) {
2360 return PyTuple_New(0);
2361 }
2362 if (step == 1) {
2363 return BPy_IDArray_slice(self, start, stop);
2364 }
2365
2366 PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors");
2367 return nullptr;
2368 }
2369
2370 PyErr_Format(PyExc_TypeError,
2371 "vector indices must be integers, not %.200s",
2372 __func__,
2373 Py_TYPE(item)->tp_name);
2374 return nullptr;
2375}
2376
2377static int BPy_IDArray_ass_subscript(BPy_IDArray *self, PyObject *item, PyObject *value)
2378{
2379 if (PyIndex_Check(item)) {
2380 Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
2381 if (i == -1 && PyErr_Occurred()) {
2382 return -1;
2383 }
2384 if (i < 0) {
2385 i += self->prop->len;
2386 }
2387 return BPy_IDArray_SetItem(self, i, value);
2388 }
2389 if (PySlice_Check(item)) {
2390 Py_ssize_t start, stop, step, slicelength;
2391
2392 if (PySlice_GetIndicesEx(item, self->prop->len, &start, &stop, &step, &slicelength) < 0) {
2393 return -1;
2394 }
2395
2396 if (step == 1) {
2397 return BPy_IDArray_ass_slice(self, start, stop, value);
2398 }
2399
2400 PyErr_SetString(PyExc_TypeError, "slice steps not supported with vectors");
2401 return -1;
2402 }
2403
2404 PyErr_Format(
2405 PyExc_TypeError, "vector indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
2406 return -1;
2407}
2408
2409static PyMappingMethods BPy_IDArray_AsMapping = {
2410 /*mp_length*/ (lenfunc)BPy_IDArray_Len,
2411 /*mp_subscript*/ (binaryfunc)BPy_IDArray_subscript,
2412 /*mp_ass_subscript*/ (objobjargproc)BPy_IDArray_ass_subscript,
2413};
2414
2415static int itemsize_by_idarray_type(int array_type)
2416{
2417 if (array_type == IDP_INT) {
2418 return sizeof(int);
2419 }
2420 if (array_type == IDP_FLOAT) {
2421 return sizeof(float);
2422 }
2423 if (array_type == IDP_DOUBLE) {
2424 return sizeof(double);
2425 }
2426 if (array_type == IDP_BOOLEAN) {
2427 return sizeof(bool);
2428 }
2429 return -1; /* should never happen */
2430}
2431
2432static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags)
2433{
2434 IDProperty *prop = self->prop;
2435 const int itemsize = itemsize_by_idarray_type(prop->subtype);
2436 const int length = itemsize * prop->len;
2437
2438 if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) {
2439 return -1;
2440 }
2441
2442 view->itemsize = itemsize;
2443 view->format = (char *)idp_format_from_array_type(prop->subtype);
2444
2445 Py_ssize_t *shape = static_cast<Py_ssize_t *>(MEM_mallocN(sizeof(Py_ssize_t), __func__));
2446 shape[0] = prop->len;
2447 view->shape = shape;
2448
2449 return 0;
2450}
2451
2452static void BPy_IDArray_releasebuffer(BPy_IDArray * /*self*/, Py_buffer *view)
2453{
2454 MEM_freeN(view->shape);
2455}
2456
2457static PyBufferProcs BPy_IDArray_Buffer = {
2458 /*bf_getbuffer*/ (getbufferproc)BPy_IDArray_getbuffer,
2459 /*bf_releasebuffer*/ (releasebufferproc)BPy_IDArray_releasebuffer,
2460};
2461
2464/* -------------------------------------------------------------------- */
2468PyTypeObject BPy_IDArray_Type = {
2469 /*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
2470 /* For printing, in format `<module>.<name>`. */
2471 /*tp_name*/ "IDPropertyArray",
2472 /*tp_basicsize*/ sizeof(BPy_IDArray),
2473 /*tp_itemsize*/ 0,
2474 /*tp_dealloc*/ nullptr,
2475 /*tp_vectorcall_offset*/ 0,
2476 /*tp_getattr*/ nullptr,
2477 /*tp_setattr*/ nullptr,
2478 /*tp_as_async*/ nullptr,
2479 /*tp_repr*/ (reprfunc)BPy_IDArray_repr,
2480 /*tp_as_number*/ nullptr,
2481 /*tp_as_sequence*/ &BPy_IDArray_Seq,
2482 /*tp_as_mapping*/ &BPy_IDArray_AsMapping,
2483 /*tp_hash*/ nullptr,
2484 /*tp_call*/ nullptr,
2485 /*tp_str*/ nullptr,
2486 /*tp_getattro*/ nullptr,
2487 /*tp_setattro*/ nullptr,
2488 /*tp_as_buffer*/ &BPy_IDArray_Buffer,
2489 /*tp_flags*/ Py_TPFLAGS_DEFAULT,
2490 /*tp_doc*/ nullptr,
2491 /*tp_traverse*/ nullptr,
2492 /*tp_clear*/ nullptr,
2493 /*tp_richcompare*/ nullptr,
2494 /*tp_weaklistoffset*/ 0,
2495 /*tp_iter*/ nullptr,
2496 /*tp_iternext*/ nullptr,
2497 /*tp_methods*/ BPy_IDArray_methods,
2498 /*tp_members*/ nullptr,
2499 /*tp_getset*/ BPy_IDArray_getseters,
2500 /*tp_base*/ nullptr,
2501 /*tp_dict*/ nullptr,
2502 /*tp_descr_get*/ nullptr,
2503 /*tp_descr_set*/ nullptr,
2504 /*tp_dictoffset*/ 0,
2505 /*tp_init*/ nullptr,
2506 /*tp_alloc*/ nullptr,
2507 /*tp_new*/ nullptr,
2508 /*tp_free*/ nullptr,
2509 /*tp_is_gc*/ nullptr,
2510 /*tp_bases*/ nullptr,
2511 /*tp_mro*/ nullptr,
2512 /*tp_cache*/ nullptr,
2513 /*tp_subclasses*/ nullptr,
2514 /*tp_weaklist*/ nullptr,
2515 /*tp_del*/ nullptr,
2516 /*tp_version_tag*/ 0,
2517 /*tp_finalize*/ nullptr,
2518 /*tp_vectorcall*/ nullptr,
2519};
2520
2523/* -------------------------------------------------------------------- */
2528{
2531
2532 PyType_Ready(&BPy_IDGroup_Type);
2533 PyType_Ready(&BPy_IDArray_Type);
2534
2535 PyType_Ready(&BPy_IDGroup_IterKeys_Type);
2536 PyType_Ready(&BPy_IDGroup_IterValues_Type);
2537 PyType_Ready(&BPy_IDGroup_IterItems_Type);
2538
2539 PyType_Ready(&BPy_IDGroup_ViewKeys_Type);
2540 PyType_Ready(&BPy_IDGroup_ViewValues_Type);
2541 PyType_Ready(&BPy_IDGroup_ViewItems_Type);
2542}
2543
2552{
2553 BLI_assert(group ? group->prop->type == IDP_GROUP : true);
2554 BPy_IDGroup_View *iter = PyObject_GC_New(BPy_IDGroup_View, type);
2555 iter->reversed = false;
2556 iter->group = group;
2557 if (group != nullptr) {
2558 Py_INCREF(group);
2559 BLI_assert(!PyObject_GC_IsTracked((PyObject *)iter));
2560 PyObject_GC_Track(iter);
2561 }
2562 return iter;
2563}
2564
2566{
2567 return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewKeys_Type);
2568}
2569
2571{
2572 return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewValues_Type);
2573}
2574
2576{
2577 return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewItems_Type);
2578}
2579
2582/* -------------------------------------------------------------------- */
2586static PyModuleDef IDProp_types_module_def = {
2587 /*m_base*/ PyModuleDef_HEAD_INIT,
2588 /*m_name*/ "idprop.types",
2589 /*m_doc*/ nullptr,
2590 /*m_size*/ 0,
2591 /*m_methods*/ nullptr,
2592 /*m_slots*/ nullptr,
2593 /*m_traverse*/ nullptr,
2594 /*m_clear*/ nullptr,
2595 /*m_free*/ nullptr,
2596};
2597
2598static PyObject *BPyInit_idprop_types()
2599{
2600 PyObject *submodule;
2601
2602 submodule = PyModule_Create(&IDProp_types_module_def);
2603
2606
2607 /* `bmesh_py_types.cc` */
2608 PyModule_AddType(submodule, &BPy_IDGroup_Type);
2609
2610 PyModule_AddType(submodule, &BPy_IDGroup_ViewKeys_Type);
2611 PyModule_AddType(submodule, &BPy_IDGroup_ViewValues_Type);
2612 PyModule_AddType(submodule, &BPy_IDGroup_ViewItems_Type);
2613
2614 PyModule_AddType(submodule, &BPy_IDGroup_IterKeys_Type);
2615 PyModule_AddType(submodule, &BPy_IDGroup_IterValues_Type);
2616 PyModule_AddType(submodule, &BPy_IDGroup_IterItems_Type);
2617
2618 PyModule_AddType(submodule, &BPy_IDArray_Type);
2619
2620 return submodule;
2621}
2622
2625/* -------------------------------------------------------------------- */
2629static PyMethodDef IDProp_methods[] = {
2630 {nullptr, nullptr, 0, nullptr},
2631};
2632
2634 /* Wrap. */
2635 IDProp_module_doc,
2636 "This module provides access id property types (currently mainly for docs).");
2637static PyModuleDef IDProp_module_def = {
2638 /*m_base*/ PyModuleDef_HEAD_INIT,
2639 /*m_name*/ "idprop",
2640 /*m_doc*/ IDProp_module_doc,
2641 /*m_size*/ 0,
2642 /*m_methods*/ IDProp_methods,
2643 /*m_slots*/ nullptr,
2644 /*m_traverse*/ nullptr,
2645 /*m_clear*/ nullptr,
2646 /*m_free*/ nullptr,
2647};
2648
2650{
2651 PyObject *mod;
2652 PyObject *submodule;
2653 PyObject *sys_modules = PyImport_GetModuleDict();
2654
2655 mod = PyModule_Create(&IDProp_module_def);
2656
2657 /* idprop.types */
2658 PyModule_AddObject(mod, "types", (submodule = BPyInit_idprop_types()));
2659 PyDict_SetItem(sys_modules, PyModule_GetNameObject(submodule), submodule);
2660
2661 return mod;
2662}
2663
IDProperty * IDP_NewIDPArray(const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:69
#define IDP_Float(prop)
IDPropertyUIData * IDP_TryConvertUIData(IDPropertyUIData *src, eIDPropertyUIDataType src_type, eIDPropertyUIDataType dst_type)
Definition idprop.cc:1757
#define IDP_IDPArray(prop)
void IDP_FreeFromGroup(IDProperty *group, IDProperty *prop) ATTR_NONNULL()
Definition idprop.cc:757
#define IDP_Int(prop)
#define IDP_Bool(prop)
eIDPropertyUIDataType IDP_ui_data_type(const IDProperty *prop)
Definition idprop.cc:1665
IDProperty * IDP_GetPropertyFromGroup(const IDProperty *prop, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:763
void IDP_AssignStringMaxSize(IDProperty *prop, const char *st, size_t st_maxncpy) ATTR_NONNULL()
Definition idprop.cc:413
const char * IDP_type_str(eIDPropertyType type, short sub_type)
eIDPropertyUIDataType
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1227
void IDP_ReplaceInGroup_ex(IDProperty *group, IDProperty *prop, IDProperty *prop_exist)
Definition idprop.cc:654
#define IDP_String(prop)
void IDP_ClearProperty(IDProperty *prop)
Definition idprop.cc:1233
IDProperty * IDP_New(char type, const IDPropertyTemplate *val, const char *name, eIDPropertyFlag flags={}) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:989
#define IDP_Double(prop)
void IDP_AppendArray(IDProperty *prop, IDProperty *item)
Definition idprop.cc:140
void IDP_AssignID(IDProperty *prop, ID *id, int flag)
Definition idprop.cc:538
void IDP_ResizeArray(IDProperty *prop, int newlen)
Definition idprop.cc:215
void IDP_MergeGroup(IDProperty *dest, const IDProperty *src, bool do_overwrite) ATTR_NONNULL()
Definition idprop.cc:717
#define IDP_Array(prop)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define CLAMP(a, b, c)
#define UNLIKELY(x)
#define ELEM(...)
typedef double(DMatrix)[4][4]
ID and Library types, which are fundamental for SDNA.
@ ID_FLAG_EMBEDDED_DATA
Definition DNA_ID.h:725
#define MAX_IDPROP_NAME
Definition DNA_ID.h:185
@ IDP_STRING_SUB_UTF8
@ IDP_STRING_SUB_BYTE
@ IDP_DOUBLE
@ IDP_FLOAT
@ IDP_STRING
@ IDP_BOOLEAN
@ IDP_IDPARRAY
@ IDP_INT
@ IDP_GROUP
@ IDP_ARRAY
@ IDP_ID
@ IDP_FLAG_STATIC_TYPE
@ IDP_FLAG_OVERRIDABLE_LIBRARY
Read Guarded memory(de)allocation.
PyObject * self
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define printf
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
PyObject * BPy_Wrap_GetKeys(IDProperty *prop)
static PyObject * BPy_IDGroup_clear(BPy_IDProperty *self)
static IDProperty * idp_from_PyLong(IDProperty *prop_exist, const char *name, PyObject *ob, const bool do_conversion, const bool can_create)
static PyMethodDef IDProp_methods[]
static int BPy_Group_ViewItems_Contains(BPy_IDGroup_View *self, PyObject *value)
static PyObject * idprop_py_from_idp_double(const IDProperty *prop)
static PyObject * idprop_py_from_idp_idparray(ID *id, IDProperty *prop)
static PyObject * BPyInit_idprop_types()
static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence
PyTypeObject BPy_IDGroup_ViewItems_Type
static PyObject * BPy_Group_ViewItems_iter(BPy_IDGroup_View *self)
static PyGetSetDef BPy_IDGroup_getseters[]
PyDoc_STRVAR(BPy_IDGroup_View_reversed_doc, "Return a reverse iterator over the ID Property keys values or items.")
PyTypeObject BPy_IDGroup_ViewValues_Type
static PyObject * BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed)
static int BPy_Group_ViewKeys_Contains(BPy_IDGroup_View *self, PyObject *value)
PyObject * BPyInit_idprop()
static PyObject * BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed)
PyTypeObject BPy_IDGroup_ViewKeys_Type
bool pyrna_id_CheckPyObject(PyObject *obj)
Definition bpy_rna.cc:7823
static PyObject * BPy_IDGroup_keys(BPy_IDProperty *self)
static PyObject * BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed)
static PyObject * BPy_IDGroup_GetName(BPy_IDProperty *self, void *)
static PyMethodDef BPy_IDGroup_View_methods[]
static PyObject * BPy_Group_ViewValues_iter(BPy_IDGroup_View *self)
PyObject * BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop)
static PyGetSetDef BPy_IDArray_getseters[]
static PyObject * BPy_IDGroup_to_dict(BPy_IDProperty *self)
PyObject * BPy_Wrap_GetItems(ID *id, IDProperty *prop)
int BPy_Wrap_SetMapItem(IDProperty *prop, PyObject *key, PyObject *val)
static PyObject * idprop_py_from_idp_array(ID *id, IDProperty *prop)
static IDProperty * idp_from_PyBool(IDProperty *prop_exist, const char *name, PyObject *ob, const bool do_conversion, const bool can_create)
static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
static void BPy_IDArray_releasebuffer(BPy_IDArray *, Py_buffer *view)
static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg)
static PyMethodDef BPy_IDArray_methods[]
static IDProperty * idp_from_PyMapping(IDProperty *, const char *name, PyObject *ob, const bool, const bool)
static PyObject * BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args)
static Py_ssize_t BPy_Group_View_len(BPy_IDGroup_View *self)
static IDProperty * idp_from_PyUnicode(IDProperty *prop_exist, const char *name, PyObject *ob, const bool, const bool can_create)
static PyObject * BPy_IDArray_repr(BPy_IDArray *self)
static int BPy_IDArray_SetItem(BPy_IDArray *self, Py_ssize_t index, PyObject *value)
static int BPy_IDArray_ass_subscript(BPy_IDArray *self, PyObject *item, PyObject *value)
static const char * idp_format_from_array_type(int type)
static IDProperty * idp_from_PyObject(IDProperty *prop_exist, const char *name, PyObject *ob, const bool do_conversion, const bool can_create)
static BPy_IDGroup_View * IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type)
PyTypeObject BPy_IDGroup_IterKeys_Type
static IDProperty * idp_from_PySequence_Buffer(IDProperty *prop_exist, const char *name, Py_buffer &buffer, const int idp_type, const bool, const bool can_create)
static PyObject * BPy_Group_IterKeys_next(BPy_IDGroup_Iter *self)
static PyObject * BPy_IDArray_GetItem(BPy_IDArray *self, Py_ssize_t index)
PyObject * BPy_Wrap_GetValues(ID *id, IDProperty *prop)
static PyObject * BPy_IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
static void IDGroup_Iter_init_type()
PyObject * BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop)
static int itemsize_by_idarray_type(int array_type)
static IDProperty * idp_from_PySequence_Fast(IDProperty *prop_exist, const char *name, PyObject *ob, const bool do_conversion, const bool can_create)
static PyObject * BPy_Group_IterValues_next(BPy_IDGroup_Iter *self)
static int BPy_IDGroup_View_is_gc(BPy_IDGroup_View *self)
static PyObject * BPy_IDGroup_update(BPy_IDProperty *self, PyObject *value)
static PyObject * BPy_IDArray_to_list(BPy_IDArray *self)
static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence
static PyObject * BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
static const char * idp_try_read_name(PyObject *name_obj)
PyTypeObject BPy_IDGroup_Type
bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, PyObject *ob)
PyTypeObject BPy_IDArray_Type
static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
PyObject * pyrna_id_CreatePyObject(ID *id)
Definition bpy_rna.cc:7802
static PyObject * BPy_Group_IterItems_next(BPy_IDGroup_Iter *self)
PyObject * BPy_IDGroup_MapDataToPy(IDProperty *prop)
static PySequenceMethods BPy_IDGroup_Seq
static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
static PyObject * idprop_py_from_idp_int(const IDProperty *prop)
PyTypeObject BPy_IDGroup_IterValues_Type
static PyObject * idprop_py_from_idp_bool(const IDProperty *prop)
static int BPy_Group_ViewValues_Contains(BPy_IDGroup_View *self, PyObject *value)
static IDProperty * idp_from_PySequence(IDProperty *prop_exist, const char *name, PyObject *ob, const bool do_conversion, const bool can_create)
static PyModuleDef IDProp_types_module_def
static PyBufferProcs BPy_IDArray_Buffer
static bool BPy_Group_Iter_same_size_or_raise_error(BPy_IDGroup_Iter *self)
static IDProperty * idp_from_PyFloat(IDProperty *prop_exist, const char *name, PyObject *ob, const bool do_conversion, const bool can_create)
static void BPy_IDGroup_View_dealloc(BPy_IDGroup_View *self)
static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
static PySequenceMethods BPy_IDArray_Seq
static PyObject * IDGroup_Iter_New_WithType(BPy_IDProperty *group, const bool reversed, PyTypeObject *type)
static PyObject * BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group)
static PyObject * BPy_IDArray_slice(BPy_IDArray *self, int begin, int end)
static void IDGroup_View_init_type()
static PyObject * BPy_IDGroup_Map_GetItem(BPy_IDProperty *self, PyObject *item)
static PyMappingMethods BPy_IDArray_AsMapping
static PyObject * BPy_IDArray_get_typecode(BPy_IDArray *self, void *)
static int BPy_IDGroup_SetName(BPy_IDProperty *self, PyObject *value, void *)
static PyObject * BPy_IDGroup_repr(BPy_IDProperty *self)
static PyObject * BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group)
static PyModuleDef IDProp_module_def
void IDProp_Init_Types()
static PyObject * idprop_py_from_idp_float(const IDProperty *prop)
static int BPy_IDGroup_Iter_is_gc(BPy_IDGroup_Iter *self)
static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence
static Py_ssize_t BPy_IDGroup_Map_Len(BPy_IDProperty *self)
static PyTypeObject * idp_array_py_type(BPy_IDArray *self, size_t *elem_size)
static PyObject * BPy_IDGroup_View_repr(BPy_IDGroup_View *self)
static char idp_sequence_type(PyObject *seq_fast)
static PyMethodDef BPy_IDGroup_methods[]
static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject *val)
static int BPy_IDGroup_View_clear(BPy_IDGroup_View *self)
static PyObject * idprop_py_from_idp_id(IDProperty *prop)
static PyObject * BPy_IDGroup_items(BPy_IDProperty *self)
static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject *seq)
static PyObject * BPy_IDGroup_values(BPy_IDProperty *self)
PyObject * BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent)
static PyObject * BPy_Group_ViewKeys_iter(BPy_IDGroup_View *self)
bool pyrna_id_FromPyObject(PyObject *obj, ID **id)
Definition bpy_rna.cc:7812
static IDProperty * idp_from_PyBytes(IDProperty *prop_exist, const char *name, PyObject *ob, const bool, const bool can_create)
static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags)
PyTypeObject BPy_IDGroup_IterItems_Type
static PyObject * BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject *)
static PyObject * BPy_IDArray_subscript(BPy_IDArray *self, PyObject *item)
static PyObject * BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group)
static Py_hash_t BPy_IDGroup_hash(BPy_IDProperty *self)
static PyObject * idprop_py_from_idp_string(const IDProperty *prop)
static Py_ssize_t BPy_IDArray_Len(BPy_IDArray *self)
static int idp_array_type_from_formatstr_and_size(const char *typestr, Py_ssize_t itemsize)
static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
static PyObject * BPy_IDGroup_iter(BPy_IDProperty *self)
#define SHARED_MEMBER_SET(member, value)
static PyObject * idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *parent)
PyObject * BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop)
static PyMappingMethods BPy_IDGroup_Mapping
static IDProperty * idp_from_DatablockPointer(IDProperty *prop_exist, const char *name, PyObject *ob, const bool, const bool can_create)
#define BPy_IDGroup_Check(v)
void IDPropertyUIData_Init_Types()
int count
format
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
std::unique_ptr< IDProperty, IDPropertyDeleter > create_bool(StringRefNull prop_name, bool value, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_BOOLEAN, set its name and value.
std::unique_ptr< IDProperty, IDPropertyDeleter > create(StringRefNull prop_name, int32_t value, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_INT, set its name and value.
std::unique_ptr< IDProperty, IDPropertyDeleter > create_group(StringRefNull prop_name, eIDPropertyFlag flags={})
Allocate a new IDProperty of type IDP_GROUP.
float wrap(float value, float max, float min)
Definition node_math.h:71
const char * PyC_UnicodeAsBytesAndSize(PyObject *py_str, Py_ssize_t *r_size, PyObject **r_coerce)
char PyC_StructFmt_type_from_str(const char *typestr)
PyObject * PyC_UnicodeFromBytesAndSize(const char *str, Py_ssize_t size)
bool PyC_StructFmt_type_is_int_any(char format)
int PyC_Long_AsBool(PyObject *value)
int PyC_AsArray(void *array, const size_t array_item_size, PyObject *value, const Py_ssize_t length, const PyTypeObject *type, const char *error_prefix)
bool PyC_StructFmt_type_is_float_any(char format)
const char * PyC_UnicodeAsBytes(PyObject *py_str, PyObject **r_coerce)
header-only utilities
#define PyTuple_SET_ITEMS(op_arg,...)
return ret
__int64 int64_t
Definition stdint.h:89
signed char int8_t
Definition stdint.h:75
ListBase group
Definition DNA_ID.h:146
void * pointer
Definition DNA_ID.h:145
short flag
Definition DNA_ID.h:161
int len
Definition DNA_ID.h:174
struct IDProperty * next
Definition DNA_ID.h:152
IDPropertyUIData * ui_data
Definition DNA_ID.h:182
char name[64]
Definition DNA_ID.h:163
IDPropertyData data
Definition DNA_ID.h:168
struct IDProperty * prev
Definition DNA_ID.h:152
char subtype
Definition DNA_ID.h:159
char type
Definition DNA_ID.h:154
Definition DNA_ID.h:413
void * first
const char * str
Definition BKE_idprop.hh:37
struct IDPropertyTemplate::@30 array
struct IDPropertyTemplate::@29 string
ccl_device_inline int mod(int x, int m)
Definition util/math.h:520