[Bf-blender-cvs] [265d97556aa] master: PyAPI: use iterators for ID property methods (keys, values & items)

Campbell Barton noreply at git.blender.org
Fri May 14 16:47:21 CEST 2021


Commit: 265d97556aa0f0f2a0e4dd7584e3b8573bbddd54
Author: Campbell Barton
Date:   Tue May 11 09:40:41 2021 +1000
Branches: master
https://developer.blender.org/rB265d97556aa0f0f2a0e4dd7584e3b8573bbddd54

PyAPI: use iterators for ID property methods (keys, values & items)

- Matches changes in Python 3.x dictionary methods.

- Iterating now raises a run-time error if the property-group changes
  size during iteration.

- IDPropertyGroup.iteritems() has been removed.

- IDPropertyGroup View & Iterator types have been added.

- Some set functionality from dict_keys/values/items aren't yet
  supported (isdisjoint method and boolean set style operations).

Proposed as part of T85675.

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

M	release/scripts/modules/rna_prop_ui.py
M	source/blender/python/generic/idprop_py_api.c
M	source/blender/python/generic/idprop_py_api.h
M	source/blender/python/intern/bpy_rna.c
M	tests/python/bl_pyapi_idprop.py

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

diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index e3158118146..54cde1e1c04 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -235,7 +235,7 @@ def draw(layout, context, context_member, property_type, use_edit=True):
 
     assert(isinstance(rna_item, property_type))
 
-    items = rna_item.items()
+    items = list(rna_item.items())
     items.sort()
 
     # TODO: Allow/support adding new custom props to overrides.
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index fc7054f675a..9b6ca7fcec5 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -42,6 +42,18 @@ extern bool pyrna_id_FromPyObject(PyObject *obj, ID **id);
 extern PyObject *pyrna_id_CreatePyObject(ID *id);
 extern bool pyrna_id_CheckPyObject(PyObject *obj);
 
+/* Currently there is no need to expose this publicly. */
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed);
+
+static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group);
+static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group);
+
+static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type);
+static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value);
+
 /* -------------------------------------------------------------------- */
 /** \name Python from ID-Property (Internal Conversions)
  *
@@ -756,13 +768,7 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject
 
 static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self)
 {
-  BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, &BPy_IDGroup_Iter_Type);
-  iter->group = self;
-  Py_INCREF(self);
-  iter->mode = IDPROP_ITER_KEYS;
-  iter->cur = self->prop->data.group.first;
-  PyObject_GC_Track(iter);
-  return (PyObject *)iter;
+  return BPy_IDGroup_ViewKeys_CreatePyObject(self);
 }
 
 /* for simple, non nested types this is the same as BPy_IDGroup_WrapData */
@@ -874,6 +880,370 @@ PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop)
 
 /** \} */
 
+/* -------------------------------------------------------------------- */
+/** \name ID-Property Group Iterator Type
+ * \{ */
+
+static PyObject *BPy_IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
+{
+  if (self->group == NULL) {
+    return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+  }
+  return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
+{
+  if (self->group != NULL) {
+    PyObject_GC_UnTrack(self);
+  }
+  Py_CLEAR(self->group);
+  PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_Iter_traverse(BPy_IDGroup_Iter *self, visitproc visit, void *arg)
+{
+  Py_VISIT(self->group);
+  return 0;
+}
+
+static int BPy_IDGroup_Iter_clear(BPy_IDGroup_Iter *self)
+{
+  Py_CLEAR(self->group);
+  return 0;
+}
+
+static bool BPy_Group_Iter_same_size_or_raise_error(BPy_IDGroup_Iter *self)
+{
+  if (self->len_init == self->group->prop->len) {
+    return true;
+  }
+  PyErr_SetString(PyExc_RuntimeError, "IDPropertyGroup changed size during iteration");
+  return false;
+}
+
+static PyObject *BPy_Group_IterKeys_next(BPy_IDGroup_Iter *self)
+{
+  if (self->cur != NULL) {
+    /* When `cur` is set, `group` cannot be NULL. */
+    if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+      return NULL;
+    }
+    IDProperty *cur = self->cur;
+    self->cur = self->reversed ? self->cur->prev : self->cur->next;
+    return PyUnicode_FromString(cur->name);
+  }
+  PyErr_SetNone(PyExc_StopIteration);
+  return NULL;
+}
+
+static PyObject *BPy_Group_IterValues_next(BPy_IDGroup_Iter *self)
+{
+  if (self->cur != NULL) {
+    /* When `cur` is set, `group` cannot be NULL. */
+    if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+      return NULL;
+    }
+    IDProperty *cur = self->cur;
+    self->cur = self->reversed ? self->cur->prev : self->cur->next;
+    return BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop);
+  }
+  PyErr_SetNone(PyExc_StopIteration);
+  return NULL;
+}
+
+static PyObject *BPy_Group_IterItems_next(BPy_IDGroup_Iter *self)
+{
+  if (self->cur != NULL) {
+    /* When `cur` is set, `group` cannot be NULL. */
+    if (!BPy_Group_Iter_same_size_or_raise_error(self)) {
+      return NULL;
+    }
+    IDProperty *cur = self->cur;
+    self->cur = self->reversed ? self->cur->prev : self->cur->next;
+    PyObject *ret = PyTuple_New(2);
+    PyTuple_SET_ITEMS(ret,
+                      PyUnicode_FromString(cur->name),
+                      BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
+    return ret;
+  }
+  PyErr_SetNone(PyExc_StopIteration);
+  return NULL;
+}
+
+PyTypeObject BPy_IDGroup_IterKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+PyTypeObject BPy_IDGroup_IterItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
+
+/* ID Property Group Iterator. */
+static void IDGroup_Iter_init_type(void)
+{
+#define SHARED_MEMBER_SET(member, value) \
+  { \
+    k_ty->member = v_ty->member = i_ty->member = value; \
+  } \
+  ((void)0)
+
+  PyTypeObject *k_ty = &BPy_IDGroup_IterKeys_Type;
+  PyTypeObject *v_ty = &BPy_IDGroup_IterValues_Type;
+  PyTypeObject *i_ty = &BPy_IDGroup_IterItems_Type;
+
+  /* Unique members. */
+  k_ty->tp_name = "IDPropertyGroupIterKeys";
+  v_ty->tp_name = "IDPropertyGroupIterValues";
+  i_ty->tp_name = "IDPropertyGroupIterItems";
+
+  k_ty->tp_iternext = (iternextfunc)BPy_Group_IterKeys_next;
+  v_ty->tp_iternext = (iternextfunc)BPy_Group_IterValues_next;
+  i_ty->tp_iternext = (iternextfunc)BPy_Group_IterItems_next;
+
+  /* Shared members. */
+  SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_Iter));
+  SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_Iter_dealloc);
+  SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_Iter_repr);
+  SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
+  SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_Iter_traverse);
+  SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_Iter_clear);
+  SHARED_MEMBER_SET(tp_iter, PyObject_SelfIter);
+
+#undef SHARED_MEMBER_SET
+}
+
+static PyObject *IDGroup_Iter_New_WithType(BPy_IDProperty *group,
+                                           const bool reversed,
+                                           PyTypeObject *type)
+{
+  BLI_assert(group ? group->prop->type == IDP_GROUP : true);
+  BPy_IDGroup_Iter *iter = PyObject_GC_New(BPy_IDGroup_Iter, type);
+  iter->reversed = reversed;
+  iter->group = group;
+  if (group != NULL) {
+    Py_INCREF(group);
+    PyObject_GC_Track(iter);
+    iter->cur = (reversed ? group->prop->data.group.last : group->prop->data.group.first);
+    iter->len_init = group->prop->len;
+  }
+  else {
+    iter->cur = NULL;
+    iter->len_init = 0;
+  }
+  return (PyObject *)iter;
+}
+
+static PyObject *BPy_IDGroup_IterKeys_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+  return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterKeys_Type);
+}
+
+static PyObject *BPy_IDGroup_IterValues_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+  return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterValues_Type);
+}
+
+static PyObject *BPy_IDGroup_IterItems_CreatePyObject(BPy_IDProperty *group, const bool reversed)
+{
+  return IDGroup_Iter_New_WithType(group, reversed, &BPy_IDGroup_IterItems_Type);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name ID-Property Group View Types (Keys/Values/Items)
+ *
+ * This view types is a thin wrapper on keys/values/items, this matches Python's `dict_view` type.
+ * The is returned by `property.keys()` and is separate from the iterator that loops over keys.
+ *
+ * There are some less common features this type could support (matching Python's `dict_view`)
+ *
+ * TODO:
+ * - Efficient contains checks for values and items which currently convert to a list first.
+ * - Missing `dict_views.isdisjoint`.
+ * - Missing `tp_as_number` (`nb_subtract`, `nb_and`, `nb_xor`, `nb_or`).
+ * \{ */
+
+static PyObject *BPy_IDGroup_View_repr(BPy_IDGroup_View *self)
+{
+  if (self->group == NULL) {
+    return PyUnicode_FromFormat("<%s>", Py_TYPE(self)->tp_name);
+  }
+  return PyUnicode_FromFormat("<%s \"%s\">", Py_TYPE(self)->tp_name, self->group->prop->name);
+}
+
+static void BPy_IDGroup_View_dealloc(BPy_IDGroup_View *self)
+{
+  if (self->group != NULL) {
+    PyObject_GC_UnTrack(self);
+  }
+  Py_CLEAR(self->group);
+  PyObject_GC_Del(self);
+}
+
+static int BPy_IDGroup_View_traverse(BPy_IDGroup_View *self, visitproc visit, void *arg)
+{
+  Py_VISIT(self->group);
+  return 0;
+}
+
+static int BPy_IDGroup_View_clear(BPy_IDGroup_View *self)
+{
+  Py_CLEAR(self->group);
+  return 0;
+}
+
+/* View Specific API's (Key/Value/Items). */
+
+static PyObject *BPy_Group_ViewKeys_iter(BPy_IDGroup_View *self)
+{
+  return BPy_IDGroup_IterKeys_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewValues_iter(BPy_IDGroup_View *self)
+{
+  return BPy_IDGroup_IterValues_CreatePyObject(self->group, self->reversed);
+}
+
+static PyObject *BPy_Group_ViewItems_iter(BPy_IDGroup_View *self)
+{
+  return BPy_IDGroup_IterItems_CreatePyObject(self->group, self->reversed);
+}
+
+static Py_ssize_t BPy_Group_View_len(BPy_IDGroup_View *self)
+{
+  if (self->group == NULL) {
+    return 0;
+  }
+  return self->group->prop->len;
+}
+
+static int BPy_Group_ViewKeys_Contains(BPy_IDGroup_View *self, PyObject *value)
+{
+  if (self->group == NULL) {
+    return 0;
+  }
+  return BPy_IDGroup_Contains(self->group, value);
+}
+
+static int BPy_Group_ViewValues_Contains(BPy_IDGroup_View *self, PyObject *value)
+{

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list