Skip to content
25 changes: 25 additions & 0 deletions Lib/test/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -1312,6 +1312,31 @@ def test_reversed(self):
self.assertEqual(list(r), list('dcba'))
self.assertRaises(StopIteration, next, r)

def test_reverse_iterator_for_empty_dict(self):
# bpo-38525: revered iterator should work properly

# empty dict is directly used for reference count test
self.assertEqual(list(reversed({})), [])
self.assertEqual(list(reversed({}.items())), [])
self.assertEqual(list(reversed({}.values())), [])
self.assertEqual(list(reversed({}.keys())), [])

# dict() and {} don't trigger the same code path
self.assertEqual(list(reversed(dict())), [])
self.assertEqual(list(reversed(dict().items())), [])
self.assertEqual(list(reversed(dict().values())), [])
self.assertEqual(list(reversed(dict().keys())), [])

def test_reverse_iterator_for_shared_shared_dicts(self):
class A:
def __init__(self, x, y):
if x: self.x = x
if y: self.y = y

self.assertEqual(list(reversed(A(1, 2).__dict__)), ['y', 'x'])
self.assertEqual(list(reversed(A(1, 0).__dict__)), ['x'])
self.assertEqual(list(reversed(A(0, 1).__dict__)), ['y'])

def test_dict_copy_order(self):
# bpo-34320
od = collections.OrderedDict([('a', 1), ('b', 2)])
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a segmentation fault when using reverse iterators of empty ``dict`` objects.
Patch by Dong-hee Na and Inada Naoki.
11 changes: 8 additions & 3 deletions Objects/dictobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3452,10 +3452,15 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
di->di_dict = dict;
di->di_used = dict->ma_used;
di->len = dict->ma_used;
if ((itertype == &PyDictRevIterKey_Type ||
if (itertype == &PyDictRevIterKey_Type ||
itertype == &PyDictRevIterItem_Type ||
itertype == &PyDictRevIterValue_Type) && dict->ma_used) {
itertype == &PyDictRevIterValue_Type) {
if (dict->ma_values) {
di->di_pos = dict->ma_used - 1;
}
else {
di->di_pos = dict->ma_keys->dk_nentries - 1;
}
}
else {
di->di_pos = 0;
Expand Down Expand Up @@ -3826,7 +3831,7 @@ dictreviter_iternext(dictiterobject *di)
PyObject *key, *value, *result;

if (d->ma_values) {
if (i < 0) {
if (i < 0 || i >= d->ma_used) {
goto fail;
}
key = DK_ENTRIES(k)[i].me_key;
Expand Down