Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/python/intern/bpy_rna.c')
-rw-r--r--source/blender/python/intern/bpy_rna.c99
1 files changed, 92 insertions, 7 deletions
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 14be9ceda94..56de0bfc18e 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -15,6 +15,7 @@
#include <float.h> /* FLT_MIN/MAX */
#include <stddef.h>
+#include "RNA_path.h"
#include "RNA_types.h"
#include "BLI_bitmap.h"
@@ -779,7 +780,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop)
return ret;
}
-/* NOTE(campbell): Regarding comparison `__cmp__`:
+/* NOTE(@campbellbarton): Regarding comparison `__cmp__`:
* checking the 'ptr->data' matches works in almost all cases,
* however there are a few RNA properties that are fake sub-structs and
* share the pointer with the parent, in those cases this happens 'a.b == a'
@@ -2212,6 +2213,26 @@ static int pyrna_prop_collection_bool(BPy_PropertyRNA *self)
} \
(void)0
+/**
+ * \param result: The result of calling a subscription operation on a collection (never NULL).
+ */
+static int pyrna_prop_collection_subscript_is_valid_or_error(const PyObject *value)
+{
+ if (value != Py_None) {
+ BLI_assert(BPy_StructRNA_Check(value));
+ const BPy_StructRNA *value_pyrna = (const BPy_StructRNA *)value;
+ if (UNLIKELY(value_pyrna->ptr.type == NULL)) {
+ /* It's important to use a `TypeError` as that is what's returned when `__getitem__` is
+ * called on an object that doesn't support item access. */
+ PyErr_Format(PyExc_TypeError,
+ "'%.200s' object is not subscriptable (only iteration is supported)",
+ Py_TYPE(value)->tp_name);
+ return -1;
+ }
+ }
+ return 0;
+}
+
/* Internal use only. */
static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_ssize_t keynum)
{
@@ -2222,8 +2243,35 @@ static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_s
PYRNA_PROP_COLLECTION_ABS_INDEX(NULL);
- if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) {
- return pyrna_struct_CreatePyObject(&newptr);
+ if (RNA_property_collection_lookup_int_has_fn(self->prop)) {
+ if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) {
+ return pyrna_struct_CreatePyObject(&newptr);
+ }
+ }
+ else {
+ /* No callback defined, just iterate and find the nth item. */
+ const int key = (int)keynum_abs;
+ PyObject *result = NULL;
+ bool found = false;
+ CollectionPropertyIterator iter;
+ RNA_property_collection_begin(&self->ptr, self->prop, &iter);
+ for (int i = 0; iter.valid; RNA_property_collection_next(&iter), i++) {
+ if (i == key) {
+ result = pyrna_struct_CreatePyObject(&iter.ptr);
+ found = true;
+ break;
+ }
+ }
+ /* It's important to end the iterator after `result` has been created
+ * so iterators may optionally invalidate items that were iterated over, see: T100286. */
+ RNA_property_collection_end(&iter);
+ if (found) {
+ if (result && (pyrna_prop_collection_subscript_is_valid_or_error(result) == -1)) {
+ Py_DECREF(result);
+ result = NULL; /* The exception has been set. */
+ }
+ return result;
+ }
}
const int len = RNA_property_collection_length(&self->ptr, self->prop);
@@ -2305,8 +2353,45 @@ static PyObject *pyrna_prop_collection_subscript_str(BPy_PropertyRNA *self, cons
PYRNA_PROP_CHECK_OBJ(self);
- if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) {
- return pyrna_struct_CreatePyObject(&newptr);
+ if (RNA_property_collection_lookup_string_has_fn(self->prop)) {
+ if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) {
+ return pyrna_struct_CreatePyObject(&newptr);
+ }
+ }
+ else {
+ /* No callback defined, just iterate and find the nth item. */
+ const int keylen = strlen(keyname);
+ char name[256];
+ int namelen;
+ PyObject *result = NULL;
+ bool found = false;
+ CollectionPropertyIterator iter;
+ RNA_property_collection_begin(&self->ptr, self->prop, &iter);
+ for (int i = 0; iter.valid; RNA_property_collection_next(&iter), i++) {
+ PropertyRNA *nameprop = RNA_struct_name_property(iter.ptr.type);
+ char *nameptr = RNA_property_string_get_alloc(
+ &iter.ptr, nameprop, name, sizeof(name), &namelen);
+ if ((keylen == namelen) && STREQ(nameptr, keyname)) {
+ found = true;
+ }
+ if ((char *)&name != nameptr) {
+ MEM_freeN(nameptr);
+ }
+ if (found) {
+ result = pyrna_struct_CreatePyObject(&iter.ptr);
+ break;
+ }
+ }
+ /* It's important to end the iterator after `result` has been created
+ * so iterators may optionally invalidate items that were iterated over, see: T100286. */
+ RNA_property_collection_end(&iter);
+ if (found) {
+ if (result && (pyrna_prop_collection_subscript_is_valid_or_error(result) == -1)) {
+ Py_DECREF(result);
+ result = NULL; /* The exception has been set. */
+ }
+ return result;
+ }
}
PyErr_Format(PyExc_KeyError, "bpy_prop_collection[key]: key \"%.200s\" not found", keyname);
@@ -8253,7 +8338,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr,
continue;
}
- /* TODO(campbell): this is used for classmethod's too,
+ /* TODO(@campbellbarton): this is used for classmethod's too,
* even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg.
* Keep this as-is since it's working, but we should be using
* 'FUNC_USE_SELF_TYPE' for many functions. */
@@ -8344,7 +8429,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr,
continue;
}
- /* TODO(campbell): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */
+ /* TODO(@campbellbarton): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */
identifier = RNA_property_identifier(prop);
item = PyObject_GetAttrString(py_class, identifier);