[Bf-blender-cvs] [37e6a1995ac] master: PyAPI: use a new type for storing the deferred result of bpy.props

Campbell Barton noreply at git.blender.org
Sat Feb 20 06:31:34 CET 2021


Commit: 37e6a1995ac7eeabd5b6a56621ad5a850dae4149
Author: Campbell Barton
Date:   Sat Feb 20 16:16:43 2021 +1100
Branches: master
https://developer.blender.org/rB37e6a1995ac7eeabd5b6a56621ad5a850dae4149

PyAPI: use a new type for storing the deferred result of bpy.props

This is needed to support Python 3.10's Postponed annotation evaluation.

It also simplifies type checking.

===================================================================

M	source/blender/python/intern/bpy_props.c
M	source/blender/python/intern/bpy_props.h
M	source/blender/python/intern/bpy_rna.c

===================================================================

diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index 878bf4aec5d..c596f81a91c 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -193,6 +193,71 @@ static const EnumPropertyItem property_subtype_array_items[] = {
   "'XYZ', 'XYZ_LENGTH', 'COLOR_GAMMA', 'COORDINATES', 'LAYER', 'LAYER_MEMBER', 'NONE'].\n" \
   "   :type subtype: string\n"
 
+/* -------------------------------------------------------------------- */
+/** \name Deferred Property Type
+ *
+ * Operators and classes use this so it can store the arguments given but defer
+ * running it until the operator runs where these values are used to setup
+ * the default arguments for that operator instance.
+ * \{ */
+
+static void bpy_prop_deferred_dealloc(BPy_PropDeferred *self)
+{
+  if (self->kw) {
+    PyObject_GC_UnTrack(self);
+    Py_CLEAR(self->kw);
+  }
+  PyObject_GC_Del(self);
+}
+
+static int bpy_prop_deferred_traverse(BPy_PropDeferred *self, visitproc visit, void *arg)
+{
+  Py_VISIT(self->kw);
+  return 0;
+}
+
+static int bpy_prop_deferred_clear(BPy_PropDeferred *self)
+{
+  Py_CLEAR(self->kw);
+  return 0;
+}
+
+static PyObject *bpy_prop_deferred_repr(BPy_PropDeferred *self)
+{
+  return PyUnicode_FromFormat("<%.200s, %R, %R>", Py_TYPE(self)->tp_name, self->fn, self->kw);
+}
+
+PyTypeObject bpy_prop_deferred_Type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+
+        .tp_name = "bpy_prop_deferred",
+    .tp_basicsize = sizeof(BPy_PropDeferred),
+    .tp_dealloc = (destructor)bpy_prop_deferred_dealloc,
+    .tp_repr = (reprfunc)bpy_prop_deferred_repr,
+
+    .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+
+    .tp_traverse = (traverseproc)bpy_prop_deferred_traverse,
+    .tp_clear = (inquiry)bpy_prop_deferred_clear,
+};
+
+static PyObject *bpy_prop_deferred_data_CreatePyObject(PyObject *fn, PyObject *kw)
+{
+  BPy_PropDeferred *self = PyObject_GC_New(BPy_PropDeferred, &bpy_prop_deferred_Type);
+  self->fn = fn;
+  if (kw == NULL) {
+    kw = PyDict_New();
+  }
+  else {
+    Py_INCREF(kw);
+  }
+  self->kw = kw;
+  PyObject_GC_Track(self);
+  return (PyObject *)self;
+}
+
+/** \} */
+
 /* PyObject's */
 static PyObject *pymeth_BoolProperty = NULL;
 static PyObject *pymeth_BoolVectorProperty = NULL;
@@ -248,27 +313,6 @@ static void bpy_prop_assign_flag_override(PropertyRNA *prop, const int flag_over
   RNA_def_property_override_flag(prop, flag_override);
 }
 
-/* operators and classes use this so it can store the args given but defer
- * running it until the operator runs where these values are used to setup
- * the default args for that operator instance */
-static PyObject *bpy_prop_deferred_return(PyObject *func, PyObject *kw)
-{
-  PyObject *ret = PyTuple_New(2);
-  PyTuple_SET_ITEM(ret, 0, func);
-  Py_INCREF(func);
-
-  if (kw == NULL) {
-    kw = PyDict_New();
-  }
-  else {
-    Py_INCREF(kw);
-  }
-
-  PyTuple_SET_ITEM(ret, 1, kw);
-
-  return ret;
-}
-
 /* callbacks */
 static void bpy_prop_update_cb(struct bContext *C,
                                struct PointerRNA *ptr,
@@ -1997,7 +2041,7 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
     if (PyErr_Occurred()) { \
       return NULL; \
     } \
-    return bpy_prop_deferred_return(pymeth_##_func, kw); \
+    return bpy_prop_deferred_data_CreatePyObject(pymeth_##_func, kw); \
   } \
   (void)0
 
@@ -3668,5 +3712,9 @@ PyObject *BPY_rna_props(void)
   ASSIGN_STATIC(CollectionProperty);
   ASSIGN_STATIC(RemoveProperty);
 
+  if (PyType_Ready(&bpy_prop_deferred_Type) < 0) {
+    return NULL;
+  }
+
   return submodule;
 }
diff --git a/source/blender/python/intern/bpy_props.h b/source/blender/python/intern/bpy_props.h
index 3d7860dcdd8..6cebf82d373 100644
--- a/source/blender/python/intern/bpy_props.h
+++ b/source/blender/python/intern/bpy_props.h
@@ -30,6 +30,16 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw);
 PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw);
 StructRNA *pointer_type_from_py(PyObject *value, const char *error_prefix);
 
+typedef struct {
+  PyObject_HEAD
+      /* This isn't GC tracked, it's a function from `bpy.props` so it's not going away. */
+      void *fn;
+  PyObject *kw;
+} BPy_PropDeferred;
+
+extern PyTypeObject bpy_prop_deferred_Type;
+#define BPy_PropDeferred_CheckTypeExact(v) (Py_TYPE(v) == &bpy_prop_deferred_Type)
+
 #define PYRNA_STACK_ARRAY RNA_STACK_ARRAY
 
 #ifdef __cplusplus
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 311a7afef01..0c091d7cd7c 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -4356,12 +4356,6 @@ static int pyrna_struct_pydict_contains(PyObject *self, PyObject *pyname)
 #endif
 
 /* --------------- setattr------------------------------------------- */
-static bool pyrna_is_deferred_prop(const PyObject *value)
-{
-  return PyTuple_CheckExact(value) && PyTuple_GET_SIZE(value) == 2 &&
-         PyCFunction_Check(PyTuple_GET_ITEM(value, 0)) &&
-         PyDict_CheckExact(PyTuple_GET_ITEM(value, 1));
-}
 
 #if 0
 static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr)
@@ -4373,12 +4367,12 @@ static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr
    * >>> bpy.types.Scene.foo
    * <bpy_struct, BoolProperty("foo")>
    * ...rather than returning the deferred class register tuple
-   * as checked by pyrna_is_deferred_prop()
+   * as checked by BPy_PropDeferred_CheckTypeExact()
    *
    * Disable for now,
    * this is faking internal behavior in a way that's too tricky to maintain well. */
 #  if 0
-  if ((ret == NULL)  /* || pyrna_is_deferred_prop(ret) */ ) {
+  if ((ret == NULL)  /* || BPy_PropDeferred_CheckTypeExact(ret) */ ) {
     StructRNA *srna = srna_from_self(cls, "StructRNA.__getattr__");
     if (srna) {
       PropertyRNA *prop = RNA_struct_type_find_property(srna, PyUnicode_AsUTF8(attr));
@@ -4399,7 +4393,7 @@ static PyObject *pyrna_struct_meta_idprop_getattro(PyObject *cls, PyObject *attr
 static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyObject *value)
 {
   StructRNA *srna = srna_from_self(cls, "StructRNA.__setattr__");
-  const bool is_deferred_prop = (value && pyrna_is_deferred_prop(value));
+  const bool is_deferred_prop = (value && BPy_PropDeferred_CheckTypeExact(value));
   const char *attr_str = PyUnicode_AsUTF8(attr);
 
   if (srna && !pyrna_write_check() &&
@@ -7873,78 +7867,69 @@ StructRNA *srna_from_self(PyObject *self, const char *error_prefix)
 
 static int deferred_register_prop(StructRNA *srna, PyObject *key, PyObject *item)
 {
+  if (!BPy_PropDeferred_CheckTypeExact(item)) {
+    /* No error, ignoring. */
+    return 0;
+  }
+
   /* We only care about results from C which
    * are for sure types, save some time with error */
-  if (pyrna_is_deferred_prop(item)) {
+  PyObject *py_func = ((BPy_PropDeferred *)item)->fn;
+  PyObject *py_kw = ((BPy_PropDeferred *)item)->kw;
+  PyObject *py_srna_cobject, *py_ret;
 
-    PyObject *py_func, *py_kw, *py_srna_cobject, *py_ret;
+  PyObject *args_fake;
 
-    if (PyArg_ParseTuple(item, "OO!", &py_func, &PyDict_Type, &py_kw)) {
-      PyObject *args_fake;
-
-      if (*PyUnicode_AsUTF8(key) == '_') {
-        PyErr_Format(PyExc_ValueError,
-                     "bpy_struct \"%.200s\" registration error: "
-                     "%.200s could not register because the property starts with an '_'\n",
-                     RNA_struct_identifier(srna),
-                     PyUnicode_AsUTF8(key));
-        return -1;
-      }
-      py_srna_cobject = PyCapsule_New(srna, NULL, NULL);
+  if (*PyUnicode_AsUTF8(key) == '_') {
+    PyErr_Format(PyExc_ValueError,
+                 "bpy_struct \"%.200s\" registration error: "
+                 "%.200s could not register because the property starts with an '_'\n",
+                 RNA_struct_identifier(srna),
+                 PyUnicode_AsUTF8(key));
+    return -1;
+  }
+  py_srna_cobject = PyCapsule_New(srna, NULL, NULL);
 
-      /* Not 100% nice :/, modifies the dict passed, should be ok. */
-      PyDict_SetItem(py_kw, bpy_intern_str_attr, key);
+  /* Not 100% nice :/, modifies the dict passed, should be ok. */
+  PyDict_SetItem(py_kw, bpy_intern_str_attr, key);
 
-      args_fake = PyTuple_New(1);
-      PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject);
+  args_fake = PyTuple_New(1);
+  PyTuple_SET_ITEM(args_fake, 0, py_srna_cobject);
 
-      PyObject *type = PyDict_GetItemString(py_kw, "type");
-      StructRNA *type_srna = srna_from_self(type, "");
-      if (type_srna) {
-        if (!RNA_struct_idprops_datablock_allowed(srna) &&
-            (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty ||
-             *(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) ==
-                 BPy_CollectionProperty) &&
-            RNA_struct_idprops_contains_datablock(type_srna)) {
-          PyErr_Format(PyExc_ValueError,
-                       "bpy_struct \"%.200s\" doesn't support datablock properties\n",
-                       RNA_struct_identifier(srna));
-          return -1;
-        }
-      }
+  PyObject *type = PyDict_GetItemString(py_kw, "type");
+  StructRNA *type_srna = srna_from_self(type, "");
+  if (type_srna) {
+    if (!RNA_struct_idprops_datablock_allowed(srna) &&
+        (*(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_PointerProperty ||
+         *(PyCFunctionWithKeywords)PyCFunction_GET_FUNCTION(py_func) == BPy_CollectionProperty) &&
+        RNA_struct_idprops_contains_datablock(type_srna)) {
+      PyErr_Format(PyExc_ValueError,
+                   "bpy_struct \"%.200s\" doesn't support datablock properties\n",
+                   RNA_struct_identifier(srna));
+      return -1;
+    }
+  }
 
-      py_ret = PyObject_Call(py_func, args_fake, py_kw);
+  py_ret = PyObject_Call(py_func, args_fake, py_kw);
 
-      if (py_ret) {
-        Py_DECREF(py_ret);
-        Py_DECREF(args_fake); /* Free's py_srna_cobject too. */
-      }
-      else {
-        /* _must_ print before decreffing args_fake. */
-        PyErr_Print();

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list