summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorRĂ©mi Lapeyre <remi.lapeyre@henki.fr>2018-11-06 00:38:54 (GMT)
committerINADA Naoki <methane@users.noreply.github.com>2018-11-06 00:38:54 (GMT)
commit6531bf6309c8fda1954060a0fb5ea930b1efb656 (patch)
tree64debbc908a8d0dbd043d3114a536a750bb70339 /Objects
parent16c8a53490a22bd4fcde2efaf4694dd06ded882b (diff)
downloadcpython-6531bf6309c8fda1954060a0fb5ea930b1efb656.zip
cpython-6531bf6309c8fda1954060a0fb5ea930b1efb656.tar.gz
cpython-6531bf6309c8fda1954060a0fb5ea930b1efb656.tar.bz2
bpo-33462: Add __reversed__ to dict and dict views (GH-6827)
Diffstat (limited to 'Objects')
-rw-r--r--Objects/clinic/dictobject.c.h20
-rw-r--r--Objects/dictobject.c207
-rw-r--r--Objects/object.c9
3 files changed, 230 insertions, 6 deletions
diff --git a/Objects/clinic/dictobject.c.h b/Objects/clinic/dictobject.c.h
index 58677cd..4f4c9fa 100644
--- a/Objects/clinic/dictobject.c.h
+++ b/Objects/clinic/dictobject.c.h
@@ -103,4 +103,22 @@ dict_setdefault(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=d7508c5091609a23 input=a9049054013a1b77]*/
+
+PyDoc_STRVAR(dict___reversed____doc__,
+"__reversed__($self, /)\n"
+"--\n"
+"\n"
+"Return a reverse iterator over the dict keys.");
+
+#define DICT___REVERSED___METHODDEF \
+ {"__reversed__", (PyCFunction)dict___reversed__, METH_NOARGS, dict___reversed____doc__},
+
+static PyObject *
+dict___reversed___impl(PyDictObject *self);
+
+static PyObject *
+dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return dict___reversed___impl(self);
+}
+/*[clinic end generated code: output=b9923851cbd9213a input=a9049054013a1b77]*/
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index ea564a2..08ec9e2 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -3100,6 +3100,7 @@ static PyMethodDef mapp_methods[] = {
clear__doc__},
{"copy", (PyCFunction)dict_copy, METH_NOARGS,
copy__doc__},
+ DICT___REVERSED___METHODDEF
{NULL, NULL} /* sentinel */
};
@@ -3335,22 +3336,32 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
{
dictiterobject *di;
di = PyObject_GC_New(dictiterobject, itertype);
- if (di == NULL)
+ if (di == NULL) {
return NULL;
+ }
Py_INCREF(dict);
di->di_dict = dict;
di->di_used = dict->ma_used;
- di->di_pos = 0;
di->len = dict->ma_used;
- if (itertype == &PyDictIterItem_Type) {
+ if ((itertype == &PyDictRevIterKey_Type ||
+ itertype == &PyDictRevIterItem_Type ||
+ itertype == &PyDictRevIterValue_Type) && dict->ma_used) {
+ di->di_pos = dict->ma_keys->dk_nentries - 1;
+ }
+ else {
+ di->di_pos = 0;
+ }
+ if (itertype == &PyDictIterItem_Type ||
+ itertype == &PyDictRevIterItem_Type) {
di->di_result = PyTuple_Pack(2, Py_None, Py_None);
if (di->di_result == NULL) {
Py_DECREF(di);
return NULL;
}
}
- else
+ else {
di->di_result = NULL;
+ }
_PyObject_GC_TRACK(di);
return (PyObject *)di;
}
@@ -3664,6 +3675,120 @@ PyTypeObject PyDictIterItem_Type = {
};
+/* dictreviter */
+
+static PyObject *
+dictreviter_iternext(dictiterobject *di)
+{
+ PyDictObject *d = di->di_dict;
+
+ if (d == NULL) {
+ return NULL;
+ }
+ assert (PyDict_Check(d));
+
+ if (di->di_used != d->ma_used) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dictionary changed size during iteration");
+ di->di_used = -1; /* Make this state sticky */
+ return NULL;
+ }
+
+ Py_ssize_t i = di->di_pos;
+ PyDictKeysObject *k = d->ma_keys;
+ PyObject *key, *value, *result;
+
+ if (d->ma_values) {
+ if (i < 0) {
+ goto fail;
+ }
+ key = DK_ENTRIES(k)[i].me_key;
+ value = d->ma_values[i];
+ assert (value != NULL);
+ }
+ else {
+ PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i];
+ while (i >= 0 && entry_ptr->me_value == NULL) {
+ entry_ptr--;
+ i--;
+ }
+ if (i < 0) {
+ goto fail;
+ }
+ key = entry_ptr->me_key;
+ value = entry_ptr->me_value;
+ }
+ di->di_pos = i-1;
+ di->len--;
+
+ if (Py_TYPE(di) == &PyDictRevIterKey_Type) {
+ Py_INCREF(key);
+ return key;
+ }
+ else if (Py_TYPE(di) == &PyDictRevIterValue_Type) {
+ Py_INCREF(value);
+ return value;
+ }
+ else if (Py_TYPE(di) == &PyDictRevIterItem_Type) {
+ Py_INCREF(key);
+ Py_INCREF(value);
+ result = di->di_result;
+ if (Py_REFCNT(result) == 1) {
+ PyObject *oldkey = PyTuple_GET_ITEM(result, 0);
+ PyObject *oldvalue = PyTuple_GET_ITEM(result, 1);
+ PyTuple_SET_ITEM(result, 0, key); /* steals reference */
+ PyTuple_SET_ITEM(result, 1, value); /* steals reference */
+ Py_INCREF(result);
+ Py_DECREF(oldkey);
+ Py_DECREF(oldvalue);
+ }
+ else {
+ result = PyTuple_New(2);
+ if (result == NULL) {
+ return NULL;
+ }
+ PyTuple_SET_ITEM(result, 0, key); /* steals reference */
+ PyTuple_SET_ITEM(result, 1, value); /* steals reference */
+ }
+ return result;
+ }
+ else {
+ Py_UNREACHABLE();
+ }
+
+fail:
+ di->di_dict = NULL;
+ Py_DECREF(d);
+ return NULL;
+}
+
+PyTypeObject PyDictRevIterKey_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "dict_reversekeyiterator",
+ sizeof(dictiterobject),
+ .tp_dealloc = (destructor)dictiter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)dictiter_traverse,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)dictreviter_iternext,
+ .tp_methods = dictiter_methods
+};
+
+
+/*[clinic input]
+dict.__reversed__
+
+Return a reverse iterator over the dict keys.
+[clinic start generated code]*/
+
+static PyObject *
+dict___reversed___impl(PyDictObject *self)
+/*[clinic end generated code: output=e674483336d1ed51 input=23210ef3477d8c4d]*/
+{
+ assert (PyDict_Check(self));
+ return dictiter_new(self, &PyDictRevIterKey_Type);
+}
+
static PyObject *
dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored))
{
@@ -3671,7 +3796,6 @@ dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored))
dictiterobject tmp = *di;
Py_XINCREF(tmp.di_dict);
- /* iterate the temporary into a list */
PyObject *list = PySequence_List((PyObject*)&tmp);
Py_XDECREF(tmp.di_dict);
if (list == NULL) {
@@ -3680,6 +3804,30 @@ dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored))
return Py_BuildValue("N(N)", _PyObject_GetBuiltin("iter"), list);
}
+PyTypeObject PyDictRevIterItem_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "dict_reverseitemiterator",
+ sizeof(dictiterobject),
+ .tp_dealloc = (destructor)dictiter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)dictiter_traverse,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)dictreviter_iternext,
+ .tp_methods = dictiter_methods
+};
+
+PyTypeObject PyDictRevIterValue_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "dict_reversevalueiterator",
+ sizeof(dictiterobject),
+ .tp_dealloc = (destructor)dictiter_dealloc,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_traverse = (traverseproc)dictiter_traverse,
+ .tp_iter = PyObject_SelfIter,
+ .tp_iternext = (iternextfunc)dictreviter_iternext,
+ .tp_methods = dictiter_methods
+};
+
/***********************************************/
/* View objects for keys(), items(), values(). */
/***********************************************/
@@ -4035,9 +4183,16 @@ dictviews_isdisjoint(PyObject *self, PyObject *other)
PyDoc_STRVAR(isdisjoint_doc,
"Return True if the view and the given iterable have a null intersection.");
+static PyObject* dictkeys_reversed(_PyDictViewObject *dv);
+
+PyDoc_STRVAR(reversed_keys_doc,
+"Return a reverse iterator over the dict keys.");
+
static PyMethodDef dictkeys_methods[] = {
{"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O,
isdisjoint_doc},
+ {"__reversed__", (PyCFunction)dictkeys_reversed, METH_NOARGS,
+ reversed_keys_doc},
{NULL, NULL} /* sentinel */
};
@@ -4080,6 +4235,15 @@ dictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored))
return _PyDictView_New(dict, &PyDictKeys_Type);
}
+static PyObject *
+dictkeys_reversed(_PyDictViewObject *dv)
+{
+ if (dv->dv_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return dictiter_new(dv->dv_dict, &PyDictRevIterKey_Type);
+}
+
/*** dict_items ***/
static PyObject *
@@ -4125,9 +4289,16 @@ static PySequenceMethods dictitems_as_sequence = {
(objobjproc)dictitems_contains, /* sq_contains */
};
+static PyObject* dictitems_reversed(_PyDictViewObject *dv);
+
+PyDoc_STRVAR(reversed_items_doc,
+"Return a reverse iterator over the dict items.");
+
static PyMethodDef dictitems_methods[] = {
{"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O,
isdisjoint_doc},
+ {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS,
+ reversed_items_doc},
{NULL, NULL} /* sentinel */
};
@@ -4170,6 +4341,15 @@ dictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored))
return _PyDictView_New(dict, &PyDictItems_Type);
}
+static PyObject *
+dictitems_reversed(_PyDictViewObject *dv)
+{
+ if (dv->dv_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return dictiter_new(dv->dv_dict, &PyDictRevIterItem_Type);
+}
+
/*** dict_values ***/
static PyObject *
@@ -4192,7 +4372,14 @@ static PySequenceMethods dictvalues_as_sequence = {
(objobjproc)0, /* sq_contains */
};
+static PyObject* dictvalues_reversed(_PyDictViewObject *dv);
+
+PyDoc_STRVAR(reversed_values_doc,
+"Return a reverse iterator over the dict values.");
+
static PyMethodDef dictvalues_methods[] = {
+ {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS,
+ reversed_values_doc},
{NULL, NULL} /* sentinel */
};
@@ -4235,6 +4422,16 @@ dictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored))
return _PyDictView_New(dict, &PyDictValues_Type);
}
+static PyObject *
+dictvalues_reversed(_PyDictViewObject *dv)
+{
+ if (dv->dv_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return dictiter_new(dv->dv_dict, &PyDictRevIterValue_Type);
+}
+
+
/* Returns NULL if cannot allocate a new PyDictKeysObject,
but does not set an error */
PyDictKeysObject *
diff --git a/Objects/object.c b/Objects/object.c
index d3a97f6..72e2684 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -1790,6 +1790,15 @@ _Py_ReadyTypes(void)
if (PyType_Ready(&PyDictItems_Type) < 0)
Py_FatalError("Can't initialize dict items type");
+ if (PyType_Ready(&PyDictRevIterKey_Type) < 0)
+ Py_FatalError("Can't initialize reversed dict keys type");
+
+ if (PyType_Ready(&PyDictRevIterValue_Type) < 0)
+ Py_FatalError("Can't initialize reversed dict values type");
+
+ if (PyType_Ready(&PyDictRevIterItem_Type) < 0)
+ Py_FatalError("Can't initialize reversed dict items type");
+
if (PyType_Ready(&PyODict_Type) < 0)
Py_FatalError("Can't initialize OrderedDict type");