Fix #127127: bpy.types sometimes reference their parent's "bl_rna"

Accessing `bl_rna` for type defined in `scripts/modules/bpy_types.py`
could return their parent-classes RNA when accessed via their parent
types __subclasses__() method.
This commit is contained in:
Campbell Barton
2024-09-06 14:32:08 +10:00
parent 55ac8c701c
commit 43f7b50df3
4 changed files with 121 additions and 61 deletions

View File

@@ -740,7 +740,8 @@ void BPy_init_modules(bContext *C)
Py_DECREF(mod);
/* needs to be first so bpy_types can run */
PyModule_AddObject(mod, "types", BPY_rna_types());
PyObject *bpy_types = BPY_rna_types();
PyModule_AddObject(mod, "types", bpy_types);
/* needs to be first so bpy_types can run */
BPY_library_load_type_ready();
@@ -752,6 +753,8 @@ void BPy_init_modules(bContext *C)
bpy_import_test("bpy_types");
PyModule_AddObject(mod, "data", BPY_rna_module()); /* imports bpy_types by running this */
bpy_import_test("bpy_types");
BPY_rna_types_finalize_external_types(bpy_types);
PyModule_AddObject(mod, "props", BPY_rna_props());
/* ops is now a python module that does the conversion from SOME_OT_foo -> some.foo */
PyModule_AddObject(mod, "ops", BPY_operator_module());

View File

@@ -101,6 +101,8 @@ static PyObject *pyrna_prop_collection_values(BPy_PropertyRNA *self);
static PyObject *pyrna_register_class(PyObject *self, PyObject *py_class);
static PyObject *pyrna_unregister_class(PyObject *self, PyObject *py_class);
static StructRNA *srna_from_ptr(PointerRNA *ptr);
#define BPY_DOC_ID_PROP_TYPE_NOTE \
" .. note::\n" \
"\n" \
@@ -8066,6 +8068,57 @@ PyObject *BPY_rna_types()
return submodule;
}
void BPY_rna_types_finalize_external_types(PyObject *submodule)
{
/* NOTE: Blender is generally functional without running this logic
* however failure set the classes `bl_rna` (via `pyrna_subtype_set_rna`)
* means *partially* initialized classes exist.
* It's simpler to avoid this altogether as it's a corner case Python developers should
* not have to concern themselves with as it could cause errors with RNA introspection.
*
* If the classes are accessed via `bpy.types` they will be initialized correctly
* however classes can also be accessed via `bpy.types.ID.__subclasses__()`
* which doesn't ensure the `bl_rna` is set. See: #127127. */
BPy_TypesModule_State *state = static_cast<BPy_TypesModule_State *>(
PyModule_GetState(submodule));
PyObject *arg_key, *arg_value;
Py_ssize_t arg_pos = 0;
while (PyDict_Next(bpy_types_dict, &arg_pos, &arg_key, &arg_value)) {
const char *key_str = PyUnicode_AsUTF8(arg_key);
if (key_str[0] == '_') {
continue;
}
BLI_assert_msg(
PyObject_IsSubclass(arg_value, (PyObject *)&pyrna_struct_Type),
"Members of bpy_types.py which are not StructRNA sub-classes must use a \"_\" prefix!");
PointerRNA newptr;
if (RNA_property_collection_lookup_string(&state->ptr, state->prop, key_str, &newptr)) {
StructRNA *srna = srna_from_ptr(&newptr);
/* Within the Python logic of `./scripts/modules/bpy_types.py`
* it's possible this was already initialized. */
if (RNA_struct_py_type_get(srna) == nullptr) {
pyrna_subtype_set_rna(arg_value, srna);
}
}
#ifndef NDEBUG
else {
/* Avoid noisy warnings based on build-options. */
# ifndef WITH_USD
if (STREQ(key_str, "USDHook")) {
continue;
}
# endif
CLOG_WARN(
BPY_LOG_RNA, "bpy_types.py defines \"%.200s\" which is not a known RNA type!", key_str);
}
#endif
}
}
/** \} */
StructRNA *pyrna_struct_as_srna(PyObject *self, const bool parent, const char *error_prefix)

View File

@@ -189,6 +189,7 @@ PyObject *BPY_rna_module(void);
void BPY_update_rna_module(void);
// PyObject *BPY_rna_doc(void);
PyObject *BPY_rna_types(void);
void BPY_rna_types_finalize_external_types(PyObject *submodule);
PyObject *pyrna_struct_CreatePyObject_with_primitive_support(PointerRNA *ptr);
PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr);