forked from bartvdbraak/blender
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.
This commit is contained in:
parent
65f9550813
commit
265d97556a
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
if (self->group == NULL) {
|
||||
return 0;
|
||||
}
|
||||
/* TODO: implement this without first converting to a list. */
|
||||
PyObject *list = PySequence_List((PyObject *)self);
|
||||
const int result = PySequence_Contains(list, value);
|
||||
Py_DECREF(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int BPy_Group_ViewItems_Contains(BPy_IDGroup_View *self, PyObject *value)
|
||||
{
|
||||
if (self->group == NULL) {
|
||||
return 0;
|
||||
}
|
||||
/* TODO: implement this without first converting to a list. */
|
||||
PyObject *list = PySequence_List((PyObject *)self);
|
||||
const int result = PySequence_Contains(list, value);
|
||||
Py_DECREF(list);
|
||||
return result;
|
||||
}
|
||||
|
||||
static PySequenceMethods BPy_IDGroup_ViewKeys_as_sequence = {
|
||||
(lenfunc)BPy_Group_View_len, /* sq_length */
|
||||
0, /* sq_concat */
|
||||
0, /* sq_repeat */
|
||||
0, /* sq_item */
|
||||
0, /* sq_slice */
|
||||
0, /* sq_ass_item */
|
||||
0, /* sq_ass_slice */
|
||||
(objobjproc)BPy_Group_ViewKeys_Contains, /* sq_contains */
|
||||
};
|
||||
|
||||
static PySequenceMethods BPy_IDGroup_ViewValues_as_sequence = {
|
||||
(lenfunc)BPy_Group_View_len, /* sq_length */
|
||||
0, /* sq_concat */
|
||||
0, /* sq_repeat */
|
||||
0, /* sq_item */
|
||||
0, /* sq_slice */
|
||||
0, /* sq_ass_item */
|
||||
0, /* sq_ass_slice */
|
||||
(objobjproc)BPy_Group_ViewValues_Contains, /* sq_contains */
|
||||
};
|
||||
|
||||
static PySequenceMethods BPy_IDGroup_ViewItems_as_sequence = {
|
||||
(lenfunc)BPy_Group_View_len, /* sq_length */
|
||||
0, /* sq_concat */
|
||||
0, /* sq_repeat */
|
||||
0, /* sq_item */
|
||||
0, /* sq_slice */
|
||||
0, /* sq_ass_item */
|
||||
0, /* sq_ass_slice */
|
||||
(objobjproc)BPy_Group_ViewItems_Contains, /* sq_contains */
|
||||
};
|
||||
|
||||
/* Methods. */
|
||||
|
||||
PyDoc_STRVAR(BPy_IDGroup_View_reversed_doc,
|
||||
"Return a reverse iterator over the ID Property keys values or items.");
|
||||
|
||||
static PyObject *BPy_IDGroup_View_reversed(BPy_IDGroup_View *self, PyObject *UNUSED(ignored))
|
||||
{
|
||||
BPy_IDGroup_View *result = IDGroup_View_New_WithType(self->group, Py_TYPE(self));
|
||||
result->reversed = !self->reversed;
|
||||
return (PyObject *)result;
|
||||
}
|
||||
|
||||
static PyMethodDef BPy_IDGroup_View_methods[] = {
|
||||
{"__reversed__",
|
||||
(PyCFunction)(void (*)(void))BPy_IDGroup_View_reversed,
|
||||
METH_NOARGS,
|
||||
BPy_IDGroup_View_reversed_doc},
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
PyTypeObject BPy_IDGroup_ViewKeys_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
|
||||
PyTypeObject BPy_IDGroup_ViewValues_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
|
||||
PyTypeObject BPy_IDGroup_ViewItems_Type = {PyVarObject_HEAD_INIT(NULL, 0)};
|
||||
|
||||
/* ID Property Group View. */
|
||||
static void IDGroup_View_init_type(void)
|
||||
{
|
||||
PyTypeObject *k_ty = &BPy_IDGroup_ViewKeys_Type;
|
||||
PyTypeObject *v_ty = &BPy_IDGroup_ViewValues_Type;
|
||||
PyTypeObject *i_ty = &BPy_IDGroup_ViewItems_Type;
|
||||
|
||||
/* Unique members. */
|
||||
k_ty->tp_name = "IDPropertyGroupViewKeys";
|
||||
v_ty->tp_name = "IDPropertyGroupViewValues";
|
||||
i_ty->tp_name = "IDPropertyGroupViewItems";
|
||||
|
||||
k_ty->tp_iter = (getiterfunc)BPy_Group_ViewKeys_iter;
|
||||
v_ty->tp_iter = (getiterfunc)BPy_Group_ViewValues_iter;
|
||||
i_ty->tp_iter = (getiterfunc)BPy_Group_ViewItems_iter;
|
||||
|
||||
k_ty->tp_as_sequence = &BPy_IDGroup_ViewKeys_as_sequence;
|
||||
v_ty->tp_as_sequence = &BPy_IDGroup_ViewValues_as_sequence;
|
||||
i_ty->tp_as_sequence = &BPy_IDGroup_ViewItems_as_sequence;
|
||||
|
||||
/* Shared members. */
|
||||
#define SHARED_MEMBER_SET(member, value) \
|
||||
{ \
|
||||
k_ty->member = v_ty->member = i_ty->member = value; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
SHARED_MEMBER_SET(tp_basicsize, sizeof(BPy_IDGroup_View));
|
||||
SHARED_MEMBER_SET(tp_dealloc, (destructor)BPy_IDGroup_View_dealloc);
|
||||
SHARED_MEMBER_SET(tp_repr, (reprfunc)BPy_IDGroup_View_repr);
|
||||
SHARED_MEMBER_SET(tp_flags, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC);
|
||||
SHARED_MEMBER_SET(tp_traverse, (traverseproc)BPy_IDGroup_View_traverse);
|
||||
SHARED_MEMBER_SET(tp_clear, (inquiry)BPy_IDGroup_View_clear);
|
||||
SHARED_MEMBER_SET(tp_methods, BPy_IDGroup_View_methods);
|
||||
|
||||
#undef SHARED_MEMBER_SET
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ID-Property Group Methods
|
||||
* \{ */
|
||||
@ -923,22 +1293,6 @@ static PyObject *BPy_IDGroup_pop(BPy_IDProperty *self, PyObject *args)
|
||||
return pyform;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
BPy_IDGroup_iter_items_doc,
|
||||
".. method:: iteritems()\n"
|
||||
"\n"
|
||||
" Iterate through the items in the dict; behaves like dictionary method iteritems.\n");
|
||||
static PyObject *BPy_IDGroup_iter_items(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_ITEMS;
|
||||
iter->cur = self->prop->data.group.first;
|
||||
PyObject_GC_Track(iter);
|
||||
return (PyObject *)iter;
|
||||
}
|
||||
|
||||
/* utility function */
|
||||
static void BPy_IDGroup_CorrectListLen(IDProperty *prop, PyObject *seq, int len, const char *func)
|
||||
{
|
||||
@ -1023,13 +1377,37 @@ PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop)
|
||||
return seq;
|
||||
}
|
||||
|
||||
PyObject *BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop)
|
||||
{
|
||||
PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
|
||||
PyObject *ret = BPy_IDGroup_ViewKeys_CreatePyObject((BPy_IDProperty *)self);
|
||||
Py_XDECREF(self); /* Owned by `ret`. */
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop)
|
||||
{
|
||||
PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
|
||||
PyObject *ret = BPy_IDGroup_ViewValues_CreatePyObject((BPy_IDProperty *)self);
|
||||
Py_XDECREF(self); /* Owned by `ret`. */
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop)
|
||||
{
|
||||
PyObject *self = prop ? idprop_py_from_idp_group(id, prop, NULL) : NULL;
|
||||
PyObject *ret = BPy_IDGroup_ViewItems_CreatePyObject((BPy_IDProperty *)self);
|
||||
Py_XDECREF(self); /* Owned by `ret`. */
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(BPy_IDGroup_keys_doc,
|
||||
".. method:: keys()\n"
|
||||
"\n"
|
||||
" Return the keys associated with this group as a list of strings.\n");
|
||||
static PyObject *BPy_IDGroup_keys(BPy_IDProperty *self)
|
||||
{
|
||||
return BPy_Wrap_GetKeys(self->prop);
|
||||
return BPy_IDGroup_ViewKeys_CreatePyObject(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(BPy_IDGroup_values_doc,
|
||||
@ -1038,16 +1416,16 @@ PyDoc_STRVAR(BPy_IDGroup_values_doc,
|
||||
" Return the values associated with this group.\n");
|
||||
static PyObject *BPy_IDGroup_values(BPy_IDProperty *self)
|
||||
{
|
||||
return BPy_Wrap_GetValues(self->id, self->prop);
|
||||
return BPy_IDGroup_ViewValues_CreatePyObject(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(BPy_IDGroup_items_doc,
|
||||
".. method:: items()\n"
|
||||
"\n"
|
||||
" Return the items associated with this group.\n");
|
||||
" Iterate through the items in the dict; behaves like dictionary method items.\n");
|
||||
static PyObject *BPy_IDGroup_items(BPy_IDProperty *self)
|
||||
{
|
||||
return BPy_Wrap_GetItems(self->id, self->prop);
|
||||
return BPy_IDGroup_ViewItems_CreatePyObject(self);
|
||||
}
|
||||
|
||||
static int BPy_IDGroup_Contains(BPy_IDProperty *self, PyObject *value)
|
||||
@ -1148,7 +1526,6 @@ static PyObject *BPy_IDGroup_get(BPy_IDProperty *self, PyObject *args)
|
||||
|
||||
static struct PyMethodDef BPy_IDGroup_methods[] = {
|
||||
{"pop", (PyCFunction)BPy_IDGroup_pop, METH_VARARGS, BPy_IDGroup_pop_doc},
|
||||
{"iteritems", (PyCFunction)BPy_IDGroup_iter_items, METH_NOARGS, BPy_IDGroup_iter_items_doc},
|
||||
{"keys", (PyCFunction)BPy_IDGroup_keys, METH_NOARGS, BPy_IDGroup_keys_doc},
|
||||
{"values", (PyCFunction)BPy_IDGroup_values, METH_NOARGS, BPy_IDGroup_values_doc},
|
||||
{"items", (PyCFunction)BPy_IDGroup_items, METH_NOARGS, BPy_IDGroup_items_doc},
|
||||
@ -1678,120 +2055,59 @@ PyTypeObject BPy_IDArray_Type = {
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ID-Property Group Iterator Type
|
||||
/** \name Initialize Types
|
||||
* \{ */
|
||||
|
||||
static PyObject *IDGroup_Iter_repr(BPy_IDGroup_Iter *self)
|
||||
{
|
||||
return PyUnicode_FromFormat("(ID Property Group Iter \"%s\")", self->group->prop->name);
|
||||
}
|
||||
|
||||
static void BPy_IDGroup_Iter_dealloc(BPy_IDGroup_Iter *self)
|
||||
{
|
||||
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 PyObject *BPy_Group_Iter_Next(BPy_IDGroup_Iter *self)
|
||||
{
|
||||
|
||||
if (self->cur) {
|
||||
PyObject *ret;
|
||||
IDProperty *cur;
|
||||
|
||||
cur = self->cur;
|
||||
self->cur = self->cur->next;
|
||||
|
||||
if (self->mode == IDPROP_ITER_ITEMS) {
|
||||
ret = PyTuple_New(2);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyUnicode_FromString(cur->name),
|
||||
BPy_IDGroup_WrapData(self->group->id, cur, self->group->prop));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return PyUnicode_FromString(cur->name);
|
||||
}
|
||||
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyTypeObject BPy_IDGroup_Iter_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
/* For printing, in format "<module>.<name>" */
|
||||
"IDPropertyGroupIter", /* char *tp_name; */
|
||||
sizeof(BPy_IDGroup_Iter), /* int tp_basicsize; */
|
||||
0, /* tp_itemsize; For allocation */
|
||||
|
||||
/* Methods to implement standard operations */
|
||||
|
||||
(destructor)BPy_IDGroup_Iter_dealloc, /* tp_dealloc */
|
||||
0, /* tp_vectorcall_offset */
|
||||
NULL, /* getattrfunc tp_getattr; */
|
||||
NULL, /* setattrfunc tp_setattr; */
|
||||
NULL, /* cmpfunc tp_compare; */
|
||||
(reprfunc)IDGroup_Iter_repr, /* reprfunc tp_repr; */
|
||||
|
||||
/* Method suites for standard classes */
|
||||
|
||||
NULL, /* PyNumberMethods *tp_as_number; */
|
||||
NULL, /* PySequenceMethods *tp_as_sequence; */
|
||||
NULL, /* PyMappingMethods *tp_as_mapping; */
|
||||
|
||||
/* More standard operations (here for binary compatibility) */
|
||||
|
||||
NULL, /* hashfunc tp_hash; */
|
||||
NULL, /* ternaryfunc tp_call; */
|
||||
NULL, /* reprfunc tp_str; */
|
||||
NULL, /* getattrofunc tp_getattro; */
|
||||
NULL, /* setattrofunc tp_setattro; */
|
||||
|
||||
/* Functions to access object as input/output buffer */
|
||||
NULL, /* PyBufferProcs *tp_as_buffer; */
|
||||
|
||||
/*** Flags to define presence of optional/expanded features ***/
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* long tp_flags; */
|
||||
|
||||
NULL, /* char *tp_doc; Documentation string */
|
||||
/*** Assigned meaning in release 2.0 ***/
|
||||
/* call function for all accessible objects */
|
||||
(traverseproc)BPy_IDGroup_Iter_traverse, /* traverseproc tp_traverse; */
|
||||
|
||||
/* delete references to contained objects */
|
||||
(inquiry)BPy_IDGroup_Iter_clear, /* inquiry tp_clear; */
|
||||
|
||||
/*** Assigned meaning in release 2.1 ***/
|
||||
/*** rich comparisons ***/
|
||||
NULL, /* richcmpfunc tp_richcompare; */
|
||||
|
||||
/*** weak reference enabler ***/
|
||||
0, /* long tp_weaklistoffset; */
|
||||
|
||||
/*** Added in release 2.2 ***/
|
||||
/* Iterators */
|
||||
PyObject_SelfIter, /* getiterfunc tp_iter; */
|
||||
(iternextfunc)BPy_Group_Iter_Next, /* iternextfunc tp_iternext; */
|
||||
};
|
||||
|
||||
void IDProp_Init_Types(void)
|
||||
{
|
||||
IDGroup_Iter_init_type();
|
||||
IDGroup_View_init_type();
|
||||
|
||||
PyType_Ready(&BPy_IDGroup_Type);
|
||||
PyType_Ready(&BPy_IDGroup_Iter_Type);
|
||||
PyType_Ready(&BPy_IDArray_Type);
|
||||
|
||||
PyType_Ready(&BPy_IDGroup_IterKeys_Type);
|
||||
PyType_Ready(&BPy_IDGroup_IterValues_Type);
|
||||
PyType_Ready(&BPy_IDGroup_IterItems_Type);
|
||||
|
||||
PyType_Ready(&BPy_IDGroup_ViewKeys_Type);
|
||||
PyType_Ready(&BPy_IDGroup_ViewValues_Type);
|
||||
PyType_Ready(&BPy_IDGroup_ViewItems_Type);
|
||||
}
|
||||
|
||||
/**
|
||||
* \note `group` may be NULL, unlike most other uses of this argument.
|
||||
* This is supported so RNA keys/values/items methods returns an iterator with the expected type:
|
||||
* - Without having ID-properties.
|
||||
* - Without supporting #BPy_IDProperty.prop being NULL, which would incur many more checks.
|
||||
* Python's own dictionary-views also works this way too.
|
||||
*/
|
||||
static BPy_IDGroup_View *IDGroup_View_New_WithType(BPy_IDProperty *group, PyTypeObject *type)
|
||||
{
|
||||
BLI_assert(group ? group->prop->type == IDP_GROUP : true);
|
||||
BPy_IDGroup_View *iter = PyObject_GC_New(BPy_IDGroup_View, type);
|
||||
iter->reversed = false;
|
||||
iter->group = group;
|
||||
if (group != NULL) {
|
||||
Py_INCREF(group);
|
||||
PyObject_GC_Track(iter);
|
||||
}
|
||||
return iter;
|
||||
}
|
||||
|
||||
static PyObject *BPy_IDGroup_ViewKeys_CreatePyObject(BPy_IDProperty *group)
|
||||
{
|
||||
return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewKeys_Type);
|
||||
}
|
||||
|
||||
static PyObject *BPy_IDGroup_ViewValues_CreatePyObject(BPy_IDProperty *group)
|
||||
{
|
||||
return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewValues_Type);
|
||||
}
|
||||
|
||||
static PyObject *BPy_IDGroup_ViewItems_CreatePyObject(BPy_IDProperty *group)
|
||||
{
|
||||
return (PyObject *)IDGroup_View_New_WithType(group, &BPy_IDGroup_ViewItems_Type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@ -1822,7 +2138,15 @@ static PyObject *BPyInit_idprop_types(void)
|
||||
|
||||
/* bmesh_py_types.c */
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_Type);
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_Iter_Type);
|
||||
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_ViewKeys_Type);
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_ViewValues_Type);
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_ViewItems_Type);
|
||||
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_IterKeys_Type);
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_IterValues_Type);
|
||||
PyModule_AddType(submodule, &BPy_IDGroup_IterItems_Type);
|
||||
|
||||
PyModule_AddType(submodule, &BPy_IDArray_Type);
|
||||
|
||||
return submodule;
|
||||
|
@ -25,16 +25,35 @@ struct ID;
|
||||
struct IDProperty;
|
||||
|
||||
extern PyTypeObject BPy_IDArray_Type;
|
||||
extern PyTypeObject BPy_IDGroup_Iter_Type;
|
||||
extern PyTypeObject BPy_IDGroup_Type;
|
||||
|
||||
extern PyTypeObject BPy_IDGroup_ViewKeys_Type;
|
||||
extern PyTypeObject BPy_IDGroup_ViewValues_Type;
|
||||
extern PyTypeObject BPy_IDGroup_ViewItems_Type;
|
||||
|
||||
extern PyTypeObject BPy_IDGroup_IterKeys_Type;
|
||||
extern PyTypeObject BPy_IDGroup_IterValues_Type;
|
||||
extern PyTypeObject BPy_IDGroup_IterItems_Type;
|
||||
|
||||
#define BPy_IDArray_Check(v) (PyObject_TypeCheck(v, &BPy_IDArray_Type))
|
||||
#define BPy_IDArray_CheckExact(v) (Py_TYPE(v) == &BPy_IDArray_Type)
|
||||
#define BPy_IDGroup_Iter_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Iter_Type))
|
||||
#define BPy_IDGroup_Iter_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Iter_Type)
|
||||
#define BPy_IDGroup_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Type))
|
||||
#define BPy_IDGroup_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Type)
|
||||
|
||||
#define BPy_IDGroup_ViewKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewKeys_Type))
|
||||
#define BPy_IDGroup_ViewKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewKeys_Type)
|
||||
#define BPy_IDGroup_ViewValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewValues_Type))
|
||||
#define BPy_IDGroup_ViewValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewValues_Type)
|
||||
#define BPy_IDGroup_ViewItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewItems_Type))
|
||||
#define BPy_IDGroup_ViewItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewItems_Type)
|
||||
|
||||
#define BPy_IDGroup_IterKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterKeys_Type))
|
||||
#define BPy_IDGroup_IterKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterKeys_Type)
|
||||
#define BPy_IDGroup_IterValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterValues_Type))
|
||||
#define BPy_IDGroup_IterValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterValues_Type)
|
||||
#define BPy_IDGroup_IterItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterItems_Type))
|
||||
#define BPy_IDGroup_IterItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterItems_Type)
|
||||
|
||||
typedef struct BPy_IDProperty {
|
||||
PyObject_VAR_HEAD
|
||||
struct ID *id; /* can be NULL */
|
||||
@ -52,12 +71,28 @@ typedef struct BPy_IDGroup_Iter {
|
||||
PyObject_VAR_HEAD
|
||||
BPy_IDProperty *group;
|
||||
struct IDProperty *cur;
|
||||
int mode;
|
||||
/** Use for detecting manipulation during iteration (which is not allowed). */
|
||||
int len_init;
|
||||
/** Iterate in the reverse direction. */
|
||||
bool reversed;
|
||||
} BPy_IDGroup_Iter;
|
||||
|
||||
/** Use to implement `IDPropertyGroup.keys/values/items` */
|
||||
typedef struct BPy_IDGroup_View {
|
||||
PyObject_VAR_HEAD
|
||||
/** This will be NULL when accessing keys on data that has no ID properties. */
|
||||
BPy_IDProperty *group;
|
||||
bool reversed;
|
||||
} BPy_IDGroup_View;
|
||||
|
||||
PyObject *BPy_Wrap_GetKeys(struct IDProperty *prop);
|
||||
PyObject *BPy_Wrap_GetValues(struct ID *id, struct IDProperty *prop);
|
||||
PyObject *BPy_Wrap_GetItems(struct ID *id, struct IDProperty *prop);
|
||||
|
||||
PyObject *BPy_Wrap_GetKeys_View_WithID(struct ID *id, struct IDProperty *prop);
|
||||
PyObject *BPy_Wrap_GetValues_View_WithID(struct ID *id, struct IDProperty *prop);
|
||||
PyObject *BPy_Wrap_GetItems_View_WithID(struct ID *id, struct IDProperty *prop);
|
||||
|
||||
int BPy_Wrap_SetMapItem(struct IDProperty *prop, PyObject *key, PyObject *val);
|
||||
|
||||
PyObject *BPy_IDGroup_MapDataToPy(struct IDProperty *prop);
|
||||
@ -67,6 +102,3 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key, struct IDProperty *grou
|
||||
void IDProp_Init_Types(void);
|
||||
|
||||
PyObject *BPyInit_idprop(void);
|
||||
|
||||
#define IDPROP_ITER_KEYS 0
|
||||
#define IDPROP_ITER_ITEMS 1
|
||||
|
@ -3562,7 +3562,7 @@ PyDoc_STRVAR(pyrna_struct_keys_doc,
|
||||
" dictionary function of the same name).\n"
|
||||
"\n"
|
||||
" :return: custom property keys.\n"
|
||||
" :rtype: list of strings\n"
|
||||
" :rtype: :class:`idprop.type.IDPropertyGroupViewKeys`\n"
|
||||
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
|
||||
static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
|
||||
{
|
||||
@ -3573,13 +3573,9 @@ static PyObject *pyrna_struct_keys(BPy_PropertyRNA *self)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* `group` may be NULL. */
|
||||
group = RNA_struct_idprops(&self->ptr, 0);
|
||||
|
||||
if (group == NULL) {
|
||||
return PyList_New(0);
|
||||
}
|
||||
|
||||
return BPy_Wrap_GetKeys(group);
|
||||
return BPy_Wrap_GetKeys_View_WithID(self->ptr.owner_id, group);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pyrna_struct_items_doc,
|
||||
@ -3589,7 +3585,7 @@ PyDoc_STRVAR(pyrna_struct_items_doc,
|
||||
" dictionary function of the same name).\n"
|
||||
"\n"
|
||||
" :return: custom property key, value pairs.\n"
|
||||
" :rtype: list of key, value tuples\n"
|
||||
" :rtype: :class:`idprop.type.IDPropertyGroupViewItems`\n"
|
||||
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
|
||||
static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
|
||||
{
|
||||
@ -3600,13 +3596,9 @@ static PyObject *pyrna_struct_items(BPy_PropertyRNA *self)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* `group` may be NULL. */
|
||||
group = RNA_struct_idprops(&self->ptr, 0);
|
||||
|
||||
if (group == NULL) {
|
||||
return PyList_New(0);
|
||||
}
|
||||
|
||||
return BPy_Wrap_GetItems(self->ptr.owner_id, group);
|
||||
return BPy_Wrap_GetItems_View_WithID(self->ptr.owner_id, group);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pyrna_struct_values_doc,
|
||||
@ -3616,7 +3608,7 @@ PyDoc_STRVAR(pyrna_struct_values_doc,
|
||||
" dictionary function of the same name).\n"
|
||||
"\n"
|
||||
" :return: custom property values.\n"
|
||||
" :rtype: list\n"
|
||||
" :rtype: :class:`idprop.type.IDPropertyGroupViewValues`\n"
|
||||
"\n" BPY_DOC_ID_PROP_TYPE_NOTE);
|
||||
static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
|
||||
{
|
||||
@ -3628,13 +3620,9 @@ static PyObject *pyrna_struct_values(BPy_PropertyRNA *self)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* `group` may be NULL. */
|
||||
group = RNA_struct_idprops(&self->ptr, 0);
|
||||
|
||||
if (group == NULL) {
|
||||
return PyList_New(0);
|
||||
}
|
||||
|
||||
return BPy_Wrap_GetValues(self->ptr.owner_id, group);
|
||||
return BPy_Wrap_GetValues_View_WithID(self->ptr.owner_id, group);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pyrna_struct_is_property_set_doc,
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop.py -- --verbose
|
||||
import bpy
|
||||
import idprop
|
||||
import unittest
|
||||
import numpy as np
|
||||
from array import array
|
||||
@ -139,6 +140,51 @@ class TestIdPropertyCreation(TestHelper, unittest.TestCase):
|
||||
with self.assertRaises(TypeError):
|
||||
self.id["a"] = self
|
||||
|
||||
class TestIdPropertyGroupView(TestHelper, unittest.TestCase):
|
||||
|
||||
def test_type(self):
|
||||
self.assertEqual(type(self.id.keys()), idprop.types.IDPropertyGroupViewKeys)
|
||||
self.assertEqual(type(self.id.values()), idprop.types.IDPropertyGroupViewValues)
|
||||
self.assertEqual(type(self.id.items()), idprop.types.IDPropertyGroupViewItems)
|
||||
|
||||
self.assertEqual(type(iter(self.id.keys())), idprop.types.IDPropertyGroupIterKeys)
|
||||
self.assertEqual(type(iter(self.id.values())), idprop.types.IDPropertyGroupIterValues)
|
||||
self.assertEqual(type(iter(self.id.items())), idprop.types.IDPropertyGroupIterItems)
|
||||
|
||||
def test_basic(self):
|
||||
text = ["A", "B", "C"]
|
||||
for i, ch in enumerate(text):
|
||||
self.id[ch] = i
|
||||
self.assertEqual(len(self.id.keys()), len(text))
|
||||
self.assertEqual(list(self.id.keys()), text)
|
||||
self.assertEqual(list(reversed(self.id.keys())), list(reversed(text)))
|
||||
|
||||
self.assertEqual(len(self.id.values()), len(text))
|
||||
self.assertEqual(list(self.id.values()), list(range(len(text))))
|
||||
self.assertEqual(list(reversed(self.id.values())), list(reversed(range(len(text)))))
|
||||
|
||||
self.assertEqual(len(self.id.items()), len(text))
|
||||
self.assertEqual(list(self.id.items()), [(k, v) for v, k in enumerate(text)])
|
||||
self.assertEqual(list(reversed(self.id.items())), list(reversed([(k, v) for v, k in enumerate(text)])))
|
||||
|
||||
def test_contains(self):
|
||||
# Check `idprop.types.IDPropertyGroupView{Keys/Values/Items}.__contains__`
|
||||
text = ["A", "B", "C"]
|
||||
for i, ch in enumerate(text):
|
||||
self.id[ch] = i
|
||||
|
||||
self.assertIn("A", self.id)
|
||||
self.assertNotIn("D", self.id)
|
||||
|
||||
self.assertIn("A", self.id.keys())
|
||||
self.assertNotIn("D", self.id.keys())
|
||||
|
||||
self.assertIn(2, self.id.values())
|
||||
self.assertNotIn(3, self.id.values())
|
||||
|
||||
self.assertIn(("A", 0), self.id.items())
|
||||
self.assertNotIn(("D", 3), self.id.items())
|
||||
|
||||
|
||||
class TestBufferProtocol(TestHelper, unittest.TestCase):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user