[Bf-blender-cvs] [e125305af41] master: Fix T86332: Error using lambda in annotations in Python 3.10
Campbell Barton
noreply at git.blender.org
Tue Mar 16 05:19:54 CET 2021
Commit: e125305af41a7360c52b9a38024b7e24fde06d70
Author: Campbell Barton
Date: Tue Mar 16 12:18:56 2021 +1100
Branches: master
https://developer.blender.org/rBe125305af41a7360c52b9a38024b7e24fde06d70
Fix T86332: Error using lambda in annotations in Python 3.10
Callbacks used in `bpy.props` didn't hold a references to the functions
they used.
While this has been the case since early 2.5x it didn't cause any
problems as long as the class held a reference.
With Python 3.10 or when using `from __future__ import annotations`,
the annotations are no longer owned by the class once evaluated.
Resolve this by holding a reference in the module, which now supports
traverse & clear callbacks so the objects are visible to Python's
garbage collector.
Also refactor storage of Python data, moving from an array into a struct.
===================================================================
M source/blender/makesrna/RNA_access.h
M source/blender/makesrna/RNA_define.h
M source/blender/makesrna/intern/makesrna.c
M source/blender/makesrna/intern/rna_access.c
M source/blender/makesrna/intern/rna_define.c
M source/blender/makesrna/intern/rna_internal_types.h
M source/blender/python/intern/bpy_interface.c
M source/blender/python/intern/bpy_props.c
M source/blender/python/intern/bpy_props.h
===================================================================
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 4c60ffe4f16..d41220eb22c 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -1021,7 +1021,6 @@ int RNA_property_string_default_length(PointerRNA *ptr, PropertyRNA *prop);
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop);
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value);
int RNA_property_enum_get_default(PointerRNA *ptr, PropertyRNA *prop);
-void *RNA_property_enum_py_data_get(PropertyRNA *prop);
int RNA_property_enum_step(
const struct bContext *C, PointerRNA *ptr, PropertyRNA *prop, int from_value, int step);
diff --git a/source/blender/makesrna/RNA_define.h b/source/blender/makesrna/RNA_define.h
index c49a52ceed7..bc1a8f52b8d 100644
--- a/source/blender/makesrna/RNA_define.h
+++ b/source/blender/makesrna/RNA_define.h
@@ -465,8 +465,6 @@ void RNA_def_property_string_funcs_runtime(PropertyRNA *prop,
StringPropertyLengthFunc lengthfunc,
StringPropertySetFunc setfunc);
-void RNA_def_property_enum_py_data(PropertyRNA *prop, void *py_data);
-
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context);
/* Function */
@@ -507,6 +505,9 @@ void RNA_def_property_duplicate_pointers(StructOrFunctionRNA *cont_, PropertyRNA
void RNA_def_property_free_pointers(PropertyRNA *prop);
int RNA_def_property_free_identifier(StructOrFunctionRNA *cont_, const char *identifier);
+void RNA_def_property_free_pointers_set_py_data_callback(
+ void (*py_data_clear_fn)(PropertyRNA *prop));
+
/* utilities */
const char *RNA_property_typename(PropertyType type);
#define IS_DNATYPE_FLOAT_COMPAT(_str) (strcmp(_str, "float") == 0 || strcmp(_str, "double") == 0)
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 1f887c2eec3..6940e475f39 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -3989,7 +3989,7 @@ static void rna_generate_property(FILE *f, StructRNA *srna, const char *nest, Pr
case PROP_ENUM: {
EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
fprintf(f,
- "\t%s, %s, %s, %s, %s, NULL, ",
+ "\t%s, %s, %s, %s, %s, ",
rna_function_string(eprop->get),
rna_function_string(eprop->set),
rna_function_string(eprop->item_fn),
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 8e5e70642cc..f94dc38ddfe 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -3598,15 +3598,6 @@ int RNA_property_enum_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
return eprop->defaultvalue;
}
-void *RNA_property_enum_py_data_get(PropertyRNA *prop)
-{
- EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
-
- BLI_assert(RNA_property_type(prop) == PROP_ENUM);
-
- return eprop->py_data;
-}
-
/**
* Get the value of the item that is \a step items away from \a from_value.
*
diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c
index 5e188285e39..019823a7de3 100644
--- a/source/blender/makesrna/intern/rna_define.c
+++ b/source/blender/makesrna/intern/rna_define.c
@@ -3305,12 +3305,6 @@ void RNA_def_property_enum_funcs_runtime(PropertyRNA *prop,
}
}
-void RNA_def_property_enum_py_data(PropertyRNA *prop, void *py_data)
-{
- EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop;
- eprop->py_data = py_data;
-}
-
void RNA_def_property_string_funcs(PropertyRNA *prop,
const char *get,
const char *length,
@@ -4632,11 +4626,28 @@ void RNA_def_property_duplicate_pointers(StructOrFunctionRNA *cont_, PropertyRNA
prop->flag_internal |= PROP_INTERN_FREE_POINTERS;
}
+static void (*g_py_data_clear_fn)(PropertyRNA *prop) = NULL;
+
+/**
+ * Set the callback used to decrement the user count of a property.
+ *
+ * This function is called when freeing each dynamically defined property.
+ */
+void RNA_def_property_free_pointers_set_py_data_callback(
+ void (*py_data_clear_fn)(PropertyRNA *prop))
+{
+ g_py_data_clear_fn = py_data_clear_fn;
+}
+
void RNA_def_property_free_pointers(PropertyRNA *prop)
{
if (prop->flag_internal & PROP_INTERN_FREE_POINTERS) {
int a;
+ if (g_py_data_clear_fn) {
+ g_py_data_clear_fn(prop);
+ }
+
if (prop->identifier) {
MEM_freeN((void *)prop->identifier);
}
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 1dd08bb1074..0c0260c889c 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -457,7 +457,6 @@ typedef struct EnumPropertyRNA {
PropEnumGetFuncEx get_ex;
PropEnumSetFuncEx set_ex;
- void *py_data; /* store py callback here */
const EnumPropertyItem *item;
int totitem;
diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c
index 331884c0bd2..5f31e0bb74d 100644
--- a/source/blender/python/intern/bpy_interface.c
+++ b/source/blender/python/intern/bpy_interface.c
@@ -45,6 +45,7 @@
#include "bpy_capi_utils.h"
#include "bpy_intern_string.h"
#include "bpy_path.h"
+#include "bpy_props.h"
#include "bpy_rna.h"
#include "bpy_traceback.h"
@@ -523,6 +524,9 @@ void BPY_python_end(void)
/* finalizing, no need to grab the state, except when we are a module */
gilstate = PyGILState_Ensure();
+ /* Decrement user counts of all callback functions. */
+ BPY_rna_props_clear_all();
+
/* free other python data. */
pyrna_free_types();
diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index 246387486be..6224f84c5fc 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -29,6 +29,7 @@
#include "RNA_types.h"
+#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "bpy_capi_utils.h"
@@ -47,14 +48,7 @@
#include "../generic/py_capi_utils.h"
-/* initial definition of callback slots we'll probably have more than 1 */
-enum {
- BPY_DATA_CB_SLOT_UPDATE = 0,
- BPY_DATA_CB_SLOT_GET = 1,
- BPY_DATA_CB_SLOT_SET = 2,
- BPY_DATA_CB_SLOT_POLL = 3,
- BPY_DATA_CB_SLOT_SIZE = 4,
-};
+static struct BPyPropStore *bpy_prop_py_data_ensure(struct PropertyRNA *prop);
static const EnumPropertyItem property_flag_items[] = {
{PROP_HIDDEN, "HIDDEN", 0, "Hidden", ""},
@@ -193,6 +187,83 @@ static const EnumPropertyItem property_subtype_array_items[] = {
"'XYZ', 'XYZ_LENGTH', 'COLOR_GAMMA', 'COORDINATES', 'LAYER', 'LAYER_MEMBER', 'NONE'].\n" \
" :type subtype: string\n"
+/* -------------------------------------------------------------------- */
+/** \name Python Property Storage API
+ *
+ * Functionality needed to use Python native callbacks from generic C RNA callbacks.
+ * \{ */
+
+/**
+ * Slots for #PyObject slots stored in #PropertyRNA.py_data (these hold a reference).
+ * #bpy_prop_callback_free_py_data manages decrementing.
+ */
+struct BPyPropStore {
+ struct BPyPropStore *next, *prev;
+
+ /**
+ * Only store #PyObject types, can be cast to an an array and operated on.
+ * NULL members are ignored/skipped. */
+ struct {
+ PyObject *update_fn;
+ PyObject *get_fn;
+ PyObject *set_fn;
+ PyObject *poll_fn;
+ /** Arguments by type. */
+ union {
+ struct {
+ PyObject *itemf_fn;
+ } enum_data;
+ };
+ } py_data;
+};
+
+#define BPY_PROP_STORE_PY_DATA_SIZE \
+ (sizeof(((struct BPyPropStore *)NULL)->py_data) / sizeof(PyObject *))
+
+#define ASSIGN_PYOBJECT_INCREF(a, b) \
+ { \
+ BLI_assert((a) == NULL); \
+ Py_INCREF(b); \
+ a = b; \
+ } \
+ ((void)0)
+
+/**
+ * Maintain a list of Python defined properties, so the GC can visit them,
+ * and so they can be cleared on exit.
+ */
+static ListBase g_bpy_prop_store_list = {NULL, NULL};
+
+static struct BPyPropStore *bpy_prop_py_data_ensure(struct PropertyRNA *prop)
+{
+ struct BPyPropStore *prop_store = RNA_property_py_data_get(prop);
+ if (prop_store == NULL) {
+ prop_store = MEM_callocN(sizeof(*prop_store), __func__);
+ RNA_def_py_data(prop, prop_store);
+ BLI_addtail(&g_bpy_prop_store_list, prop_store);
+ }
+ return prop_store;
+}
+
+/**
+ * Perform all removal actions except for freeing, which is handled by RNA.
+ */
+static void bpy_prop_py_data_remove(PropertyRNA *prop)
+{
+ struct BPyPropStore *prop_store = RNA_property_py_data_get(prop);
+ if (prop_store == NULL) {
+ return;
+ }
+
+ PyObject **py_data = (PyObject **)&prop_store->py_data;
+ for (int i = 0; i < BPY_PROP_STORE_PY_DATA_SIZE; i++) {
+ Py_XDECREF(py_data[i]);
+ }
+ BLI_remlink(&g_bpy_prop_store_list, prop_store);
+}
+
+/** \} */
+
/* -------------------------------------------------------------------- */
/** \name Deferred Property Type
*
@@ -371,15 +442,15 @@ static void bpy_prop_update_cb(struct bContext *C,
struct PointerRNA *ptr,
struct PropertyRNA *prop)
{
+ struct BPyPropStore *prop_store = RNA_property_py_data_get(prop);
PyGILState_STATE gilstate;
- PyObject **py_data = RNA_property_py_data_get(prop);
PyObject *py_func;
PyObject *args;
PyObject *self;
PyObject *ret;
const bool is_write_ok = pyrna_write_check();
- BLI_assert(py_data != NULL);
+ BLI_assert(prop_store != NULL);
if (!is_write_ok) {
pyrna_write_set(true);
@@ -387,7 +458,7 @@ static void bpy_prop_update_cb(struct bContext *C,
bpy_context_set(C, &gilstate);
- py_func = py_data[BPY_DATA_CB_SLOT_UPDATE];
+ py_func = prop_store->py_data.update_fn;
args = PyTuple_New(2);
self = pyrna_struct_as_instance(ptr);
@@ -421,7 +492,7 @@ static void bpy_prop_update_cb(struct bContext *C,
static bool bpy_prop_boolean_get_cb(struct PointerRNA *ptr, struct PropertyRNA *prop)
{
- PyObject **py_data = RNA_property_py_
@@ Diff output truncated at 10240 characters. @@
More information about the Bf-blender-cvs
mailing list