[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