[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