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