summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Perl <m@thp.io>2019-04-02 09:30:10 (GMT)
committerInada Naoki <songofacandy@gmail.com>2019-04-02 09:30:10 (GMT)
commitb8311cf5e5d72f8a8aa688b7da1760d6a74a4d72 (patch)
tree8b27ad7389e9fb15f38f8b96be1f3f7423f6a8b2
parent04694a306b8f4ab54ef5fc4ba673c26fa53b0ac1 (diff)
downloadcpython-b8311cf5e5d72f8a8aa688b7da1760d6a74a4d72.zip
cpython-b8311cf5e5d72f8a8aa688b7da1760d6a74a4d72.tar.gz
cpython-b8311cf5e5d72f8a8aa688b7da1760d6a74a4d72.tar.bz2
bpo-36473: add maximum iteration check for dict .values() and .items() (GH-12619)
-rw-r--r--Lib/test/test_dict.py20
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst6
-rw-r--r--Objects/dictobject.c12
3 files changed, 36 insertions, 2 deletions
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index eecdc8b..13be857 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -477,7 +477,25 @@ class DictTest(unittest.TestCase):
with self.assertRaises(RuntimeError):
for i in d:
del d[0]
- d[1] = 1
+ d[0] = 0
+
+ def test_mutating_iteration_delete_over_values(self):
+ # change dict content during iteration
+ d = {}
+ d[0] = 0
+ with self.assertRaises(RuntimeError):
+ for i in d.values():
+ del d[0]
+ d[0] = 0
+
+ def test_mutating_iteration_delete_over_items(self):
+ # change dict content during iteration
+ d = {}
+ d[0] = 0
+ with self.assertRaises(RuntimeError):
+ for i in d.items():
+ del d[0]
+ d[0] = 0
def test_mutating_lookup(self):
# changing dict during a lookup (issue #14417)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst b/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst
index 37c0c50..26d8568 100644
--- a/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst
+++ b/Misc/NEWS.d/next/Core and Builtins/2019-03-27-23-53-00.bpo-36452.xhK2lT.rst
@@ -1 +1,5 @@
-Changing `dict` keys during iteration will now be detected in certain corner cases where the number of keys isn't changed (but they keys themselves are), and a `RuntimeError` will be raised. \ No newline at end of file
+Changing ``dict`` keys during iteration of the dict itself, ``keys()``,
+``values()``, or ``items()`` will now be detected in certain corner cases where
+keys are deleted/added so that the number of keys isn't changed.
+A `RuntimeError` will be raised after ``len(dict)`` iterations.
+Contributed by Thomas Perl.
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 7ea979c..bba27dd 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -3630,6 +3630,12 @@ dictiter_iternextvalue(dictiterobject *di)
goto fail;
value = entry_ptr->me_value;
}
+ // We found an element, but did not expect it
+ if (di->len == 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dictionary keys changed during iteration");
+ goto fail;
+ }
di->di_pos = i+1;
di->len--;
Py_INCREF(value);
@@ -3713,6 +3719,12 @@ dictiter_iternextitem(dictiterobject *di)
key = entry_ptr->me_key;
value = entry_ptr->me_value;
}
+ // We found an element, but did not expect it
+ if (di->len == 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dictionary keys changed during iteration");
+ goto fail;
+ }
di->di_pos = i+1;
di->len--;
Py_INCREF(key);