Changeset View
Changeset View
Standalone View
Standalone View
source/blender/python/intern/bpy_rna.c
- This file is larger than 256 KB, so syntax highlighting is disabled by default.
| Show First 20 Lines • Show All 2,207 Lines • ▼ Show 20 Lines | if (keynum < 0) { \ | ||||
| keynum_abs += RNA_property_collection_length(&self->ptr, self->prop); \ | keynum_abs += RNA_property_collection_length(&self->ptr, self->prop); \ | ||||
| if (keynum_abs < 0) { \ | if (keynum_abs < 0) { \ | ||||
| PyErr_Format(PyExc_IndexError, "bpy_prop_collection[%d]: out of range.", keynum); \ | PyErr_Format(PyExc_IndexError, "bpy_prop_collection[%d]: out of range.", keynum); \ | ||||
| return ret_err; \ | return ret_err; \ | ||||
| } \ | } \ | ||||
| } \ | } \ | ||||
| (void)0 | (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(BPy_StructRNA *value) | |||||
| { | |||||
| if (UNLIKELY(value->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. */ | /* Internal use only. */ | ||||
| static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_ssize_t keynum) | static PyObject *pyrna_prop_collection_subscript_int(BPy_PropertyRNA *self, Py_ssize_t keynum) | ||||
| { | { | ||||
| PointerRNA newptr; | PointerRNA newptr; | ||||
| Py_ssize_t keynum_abs = keynum; | Py_ssize_t keynum_abs = keynum; | ||||
| PYRNA_PROP_CHECK_OBJ(self); | PYRNA_PROP_CHECK_OBJ(self); | ||||
| PYRNA_PROP_COLLECTION_ABS_INDEX(NULL); | PYRNA_PROP_COLLECTION_ABS_INDEX(NULL); | ||||
| if (RNA_property_collection_lookup_int_has_fn(self->prop)) { | |||||
| if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) { | if (RNA_property_collection_lookup_int(&self->ptr, self->prop, keynum_abs, &newptr)) { | ||||
| return pyrna_struct_CreatePyObject(&newptr); | return pyrna_struct_CreatePyObject(&newptr); | ||||
| } | } | ||||
| } | |||||
| else { | |||||
| /* No callback defined, just iterate and find the nth item. */ | |||||
| const int key = (int)keynum; | |||||
| 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) { | |||||
| /* Return an error instead of an item that has been freed, | |||||
| * this is closer to the behavior of `__getitem__` not being supported. */ | |||||
| if (result && (result != Py_None) && | |||||
| (pyrna_prop_collection_subscript_is_valid_or_error((BPy_StructRNA *)result) == -1)) { | |||||
| Py_DECREF(result); | |||||
| result = NULL; /* The exception has been set. */ | |||||
| } | |||||
| return result; | |||||
| } | |||||
| } | |||||
mont29: Would add a comment here about why this is needed, and the fact that this may return an… | |||||
| const int len = RNA_property_collection_length(&self->ptr, self->prop); | const int len = RNA_property_collection_length(&self->ptr, self->prop); | ||||
| if (keynum_abs >= len) { | if (keynum_abs >= len) { | ||||
| PyErr_Format(PyExc_IndexError, | PyErr_Format(PyExc_IndexError, | ||||
| "bpy_prop_collection[index]: " | "bpy_prop_collection[index]: " | ||||
| "index %d out of range, size %d", | "index %d out of range, size %d", | ||||
| keynum, | keynum, | ||||
| len); | len); | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| static PyObject *pyrna_prop_collection_subscript_str(BPy_PropertyRNA *self, const char *keyname) | static PyObject *pyrna_prop_collection_subscript_str(BPy_PropertyRNA *self, const char *keyname) | ||||
| { | { | ||||
| PointerRNA newptr; | PointerRNA newptr; | ||||
| PYRNA_PROP_CHECK_OBJ(self); | PYRNA_PROP_CHECK_OBJ(self); | ||||
| if (RNA_property_collection_lookup_string_has_fn(self->prop)) { | |||||
| if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) { | if (RNA_property_collection_lookup_string(&self->ptr, self->prop, keyname, &newptr)) { | ||||
| return pyrna_struct_CreatePyObject(&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) { | |||||
| /* Return an error instead of an item that has been freed, | |||||
| * this is closer to the behavior of `__getitem__` not being supported. */ | |||||
| if (result && (result != Py_None) && | |||||
| (pyrna_prop_collection_subscript_is_valid_or_error((BPy_StructRNA *)result) == -1)) { | |||||
| Py_DECREF(result); | |||||
| result = NULL; /* The exception has been set. */ | |||||
| } | |||||
| return result; | |||||
| } | |||||
mont29Unsubmitted Not Done Inline ActionsThis could be de-duplicated with the same code above maybe? E.g. in a pyrna_prop_collection_subscript_end ? mont29: This could be de-duplicated with the same code above maybe? E.g. in a… | |||||
campbellbartonAuthorUnsubmitted Done Inline ActionsAgree with the goal of de-duplicating boiler plate. 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;
}Could be replaced with: if (found) {
pyrna_prop_collection_subscript_is_valid_or_error_and_clear(&result);
return result;
}campbellbarton: Agree with the goal of de-duplicating boiler plate.
An issue with this is it moves `Py_DECREF`… | |||||
mont29Unsubmitted Not Done Inline ActionsFair enough, can keep it like for now. mont29: Fair enough, can keep it like for now. | |||||
| } | |||||
| PyErr_Format(PyExc_KeyError, "bpy_prop_collection[key]: key \"%.200s\" not found", keyname); | PyErr_Format(PyExc_KeyError, "bpy_prop_collection[key]: key \"%.200s\" not found", keyname); | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| // static PyObject *pyrna_prop_array_subscript_str(BPy_PropertyRNA *self, char *keyname) | // static PyObject *pyrna_prop_array_subscript_str(BPy_PropertyRNA *self, char *keyname) | ||||
| /** | /** | ||||
| * Special case: `bpy.data.objects["some_id_name", "//some_lib_name.blend"]` | * Special case: `bpy.data.objects["some_id_name", "//some_lib_name.blend"]` | ||||
| ▲ Show 20 Lines • Show All 6,909 Lines • Show Last 20 Lines | |||||
Would add a comment here about why this is needed, and the fact that this may return an 'invalidated' PyObject in case the actual data memory is freed by the call to RNA_property_collection_end (with link back to the deg instance objects iterator code) ?
Same with the string subscript function below.