[Bf-blender-cvs] [ddcb6b1023b] master: Cleanup: replace macros with converter callbacks for bpy.props

Campbell Barton noreply at git.blender.org
Fri Jul 30 04:04:12 CEST 2021


Commit: ddcb6b1023bf3089cd6d2db3012b68b9ec2b980f
Author: Campbell Barton
Date:   Thu Jul 29 15:11:58 2021 +1000
Branches: master
https://developer.blender.org/rBddcb6b1023bf3089cd6d2db3012b68b9ec2b980f

Cleanup: replace macros with converter callbacks for bpy.props

Macros were used for expanding shared logic for some properties.

Replace this with Python converters & a funciton that handles
deferred registration.

Add generic converter functions for RNA enums:

- pyrna_enum_value_parse_string
- pyrna_enum_bitfield_parse_set

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

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

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

diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c
index 6d5ca209866..4b4ca5a0209 100644
--- a/source/blender/python/intern/bpy_props.c
+++ b/source/blender/python/intern/bpy_props.c
@@ -503,7 +503,7 @@ struct BPYPropArrayLength {
 };
 
 /**
- * Use with PyArg_ParseTuple's "O&" formatting.
+ * Use with #PyArg_ParseTuple's `O&` formatting.
  */
 static int bpy_prop_array_length_parse(PyObject *o, void *p)
 {
@@ -2422,86 +2422,128 @@ static void bpy_prop_callback_assign_enum(struct PropertyRNA *prop,
  * This define runs at the start of each function and deals with
  * returning a deferred property #BPy_PropDeferred (to be registered later).
  *
- * \note `srna` will always be left set if this function doesn't return.
+ * \param self: The self argument from the caller.
+ * \param args: The positional arguments of the caller.
+ * \param kw: The keyword arguments of the caller.
+ * \param method_object: The method of the caller (unfortunately this can't be deduced).
+ * \param r_deferred_result: The deferred result (or NULL in the case of an error).
+ * The caller must return this value unless a valid `srna` is returned.
+ *
+ * \returns When not null, the caller is expected to perform the registration.
  */
-#define BPY_PROPDEF_HEAD(_func) \
-  { \
-    const int args_len = PyTuple_GET_SIZE(args); \
-    if (args_len == 1) { \
-      self = PyTuple_GET_ITEM(args, 0); \
-      args = PyTuple_New(0); \
-      PyObject *ret = BPy_##_func(self, args, kw); \
-      Py_DECREF(args); \
-      return ret; \
-    } \
-    if (args_len > 1) { \
-      PyErr_SetString(PyExc_ValueError, "all args must be keywords"); \
-      return NULL; \
-    } \
-    srna = srna_from_self(self, #_func "(...):"); \
-    if (srna == NULL) { \
-      if (PyErr_Occurred()) { \
-        return NULL; \
-      } \
-      return bpy_prop_deferred_data_CreatePyObject(pymeth_##_func, kw); \
-    } \
-  } \
-  (void)0
-
-/* terse macros for error checks shared between all funcs can't use function
- * calls because of static strings passed to pyrna_set_to_enum_bitfield */
-#define BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items) \
-  if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) { \
-    PyErr_Format(PyExc_TypeError, \
-                 #_func "(): '%.200s' too long, max length is %d", \
-                 id, \
-                 MAX_IDPROP_NAME - 1); \
-    return NULL; \
-  } \
-  if (UNLIKELY(RNA_def_property_free_identifier(srna, id) == -1)) { \
-    PyErr_Format(PyExc_TypeError, #_func "(): '%s' is defined as a non-dynamic type", id); \
-    return NULL; \
-  } \
-  if (UNLIKELY(pyopts && pyrna_set_to_enum_bitfield( \
-                             _property_flag_items, pyopts, &opts, #_func "(options={ ...}):"))) { \
-    return NULL; \
-  } \
-  if (UNLIKELY(pyopts_override && pyrna_set_to_enum_bitfield(_property_flag_override_items, \
-                                                             pyopts_override, \
-                                                             &opts_override, \
-                                                             #_func "(override={ ...}):"))) { \
-    return NULL; \
-  } \
-  { \
-    const EnumPropertyItem *tag_defines = RNA_struct_property_tag_defines(srna); \
-    if (py_tags && !tag_defines) { \
-      PyErr_Format(PyExc_TypeError, \
-                   #_func "(): property-tags not available for '%s'", \
-                   RNA_struct_identifier(srna)); \
-      return NULL; \
-    } \
-    if (UNLIKELY(py_tags && pyrna_set_to_enum_bitfield( \
-                                tag_defines, py_tags, &prop_tags, #_func "(tags={ ...}):"))) { \
-      return NULL; \
-    } \
-  } \
-  (void)0
-
-#define BPY_PROPDEF_SUBTYPE_CHECK( \
-    _func, _property_flag_items, _property_flag_override_items, _subtype) \
-  BPY_PROPDEF_CHECK(_func, _property_flag_items, _property_flag_override_items); \
-  if (UNLIKELY(pysubtype && RNA_enum_value_from_id(_subtype, pysubtype, &subtype) == 0)) { \
-    const char *enum_str = BPy_enum_as_string(_subtype); \
-    PyErr_Format(PyExc_TypeError, \
-                 #_func \
-                 "(subtype='%s'): " \
-                 "subtype not found in (%s)", \
-                 pysubtype, \
-                 enum_str); \
-    MEM_freeN((void *)enum_str); \
-    return NULL; \
-  } \
-  (void)0
+static StructRNA *bpy_prop_deferred_data_or_srna(PyObject *self,
+                                                 PyObject *args,
+                                                 PyObject *kw,
+                                                 PyObject *method_object,
+                                                 PyObject **r_deferred_result)
+{
+  /* This must be the methods of one of the main property types defined in this file. */
+  BLI_assert(PyCFunction_CheckExact(method_object));
+
+  const int args_len = PyTuple_GET_SIZE(args);
+  PyMethodDef *method_def = ((PyCFunctionObject *)method_object)->m_ml;
+
+  /* Call this function with the first argument set to `self`. */
+  if (args_len == 1) {
+    self = PyTuple_GET_ITEM(args, 0);
+    args = PyTuple_New(0);
+
+    /* This will be #BPy_BoolProperty` or one of the functions that define a type. */
+    PyCFunctionWithKeywords method_fn = (PyCFunctionWithKeywords)method_def->ml_meth;
+    *r_deferred_result = method_fn(self, args, kw);
+    Py_DECREF(args);
+    /* May be an error (depending on `r_deferred_result`). */
+    return NULL;
+  }
+
+  const char *error_prefix = method_def->ml_name;
+  if (args_len > 1) {
+    PyErr_Format(PyExc_ValueError, "%s: all args must be keywords", error_prefix);
+    *r_deferred_result = NULL;
+    /* An error. */
+    return NULL;
+  }
+
+  StructRNA *srna = srna_from_self(self, error_prefix);
+  if (srna == NULL) {
+    *r_deferred_result = PyErr_Occurred() ?
+                             NULL :
+                             bpy_prop_deferred_data_CreatePyObject(method_object, kw);
+    /* May be an error (depending on `r_deferred_result`). */
+    return NULL;
+  }
+
+  /* Crash if this is ever used by accident! */
+#ifndef NDEBUG
+  *r_deferred_result = (PyObject *)(intptr_t)1;
+#endif
+
+  /* No error or deferred result, perform registration immediately. */
+  return srna;
+}
+
+struct BPy_PropIDParse {
+  const char *value;
+  StructRNA *srna;
+};
+
+/**
+ * Use with #PyArg_ParseTuple's `O&` formatting.
+ */
+static int bpy_prop_arg_parse_id(PyObject *o, void *p)
+{
+  struct BPy_PropIDParse *parse_data = p;
+  StructRNA *srna = parse_data->srna;
+
+  if (!PyUnicode_Check(o)) {
+    PyErr_Format(PyExc_TypeError, "expected a string (got %.200s)", Py_TYPE(o)->tp_name);
+    return 0;
+  }
+
+  Py_ssize_t id_len;
+  const char *id;
+
+  id = PyUnicode_AsUTF8AndSize(o, &id_len);
+  if (UNLIKELY(id_len >= MAX_IDPROP_NAME)) {
+    PyErr_Format(PyExc_TypeError, "'%.200s' too long, max length is %d", id, MAX_IDPROP_NAME - 1);
+    return 0;
+  }
+
+  if (UNLIKELY(RNA_def_property_free_identifier(srna, id) == -1)) {
+    PyErr_Format(PyExc_TypeError,
+                 "'%s' is defined as a non-dynamic type for '%s'",
+                 id,
+                 RNA_struct_identifier(srna));
+    return 0;
+  }
+  parse_data->value = id;
+  return 1;
+}
+
+/**
+ * Needed so #RNA_struct_property_tag_defines can be called on the `srna`.
+ */
+struct BPy_EnumProperty_Parse_WithSRNA {
+  struct BPy_EnumProperty_Parse base;
+  StructRNA *srna;
+};
+
+/**
+ * Wrapper for #pyrna_enum_bitfield_parse_set
+ * that looks up tags from the `srna`.
+ */
+static int bpy_prop_arg_parse_tag_defines(PyObject *o, void *p)
+{
+  struct BPy_EnumProperty_Parse_WithSRNA *parse_data = p;
+  parse_data->base.items = RNA_struct_property_tag_defines(parse_data->srna);
+  if (parse_data->base.items == NULL) {
+    PyErr_Format(PyExc_TypeError,
+                 "property-tags not available for '%s'",
+                 RNA_struct_identifier(parse_data->srna));
+    return 0;
+  }
+  return pyrna_enum_bitfield_parse_set(o, &parse_data->base);
+}
 
 /** \} */
 
@@ -2624,26 +2666,40 @@ PyDoc_STRVAR(BPy_BoolProperty_doc,
                          BPY_PROPDEF_SET_DOC);
 static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
 {
-  /* Keep this block first. */
   StructRNA *srna;
-  BPY_PROPDEF_HEAD(BoolProperty);
-  BLI_assert(srna != NULL);
+  { /* Keep this block first. */
+    PyObject *deferred_result;
+    srna = bpy_prop_deferred_data_or_srna(self, args, kw, pymeth_BoolProperty, &deferred_result);
+    if (srna == NULL) {
+      return deferred_result;
+    }
+  }
 
-  const char *id = NULL, *name = NULL, *description = "";
-  Py_ssize_t id_len;
+  struct BPy_PropIDParse id_data = {
+      .srna = srna,
+  };
+  const char *name = NULL, *description = "";
   bool def = false;
   PropertyRNA *prop;
-  PyObject *pyopts = NULL;
-  PyObject *pyopts_override = NULL;
-  int opts = 0;
-  int opts_override = 0;
-  int prop_tags = 0;
-  const char *pysubtype = NULL;
-  int subtype = PROP_NONE;
+  struct BPy_EnumProperty_Parse options_enum = {
+      .items = property_flag_items,
+      .value = 0,
+  };
+  struct BPy_EnumProperty_Parse override_enum = {
+      .items = property_flag_override_items,
+      .value = 0,
+  };
+  struct BPy_EnumProperty_Parse_WithSRNA tags_enum = {
+      .srna = srna,
+  };
+  struct BPy_EnumProperty_Parse subtype_enum = {
+      .items = property_subtype_number_items,
+      .value = PROP_NONE,
+  };
+
   PyObject *update_fn = NULL;
   PyObject *get_fn = NULL;
   PyObject *set_fn = NULL;
-  PyObject *py_tags = NULL;
 
   static const char *_keywords[] = {
       "attr",
@@ -2659,34 +2715,30 @@ static PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
       "set",
       NULL,
   };
-  static _PyArg_Parser _parser = {"s#|$ssO&O!O!O!sOOO:BoolProperty", _keywords, 0};
+  static _PyArg_Parser _parser = {"O&|$ssO&O&O&O&O&OOO:BoolProperty", _keywords, 0};
   if (!_PyArg_ParseTupleAndKeywordsFast(args,
                                         kw,
                                         &_parser,
-                                        &id,
-                                        &id_len,
+                                      

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list