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