Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 42002) +++ Python/ceval.c (working copy) @@ -1793,14 +1793,21 @@ case STORE_GLOBAL: w = GETITEM(names, oparg); v = POP(); - err = PyDict_SetItem(f->f_globals, w, v); + if (PyDict_CheckExact(f->f_globals)) + err = PyDict_SetItem(f->f_globals, w, v); + else + err = PyObject_SetItem(f->f_globals, w, v); Py_DECREF(v); if (err == 0) continue; break; case DELETE_GLOBAL: w = GETITEM(names, oparg); - if ((err = PyDict_DelItem(f->f_globals, w)) != 0) + if (PyDict_CheckExact(f->f_globals)) + err = PyDict_DelItem(f->f_globals, w); + else + err = PyObject_DelItem(f->f_globals, w); + if (err) format_exc_check_arg( PyExc_NameError, GLOBAL_NAME_ERROR_MSG, w); break; @@ -1826,9 +1833,15 @@ } } if (x == NULL) { - x = PyDict_GetItem(f->f_globals, w); + if (PyDict_CheckExact(f->f_globals)) { + x = PyDict_GetItem(f->f_globals, w); + Py_XINCREF(x); + } else { + x = PyObject_GetItem(f->f_globals, w); + } if (x == NULL) { x = PyDict_GetItem(f->f_builtins, w); + Py_XINCREF(x); if (x == NULL) { format_exc_check_arg( PyExc_NameError, @@ -1836,39 +1849,48 @@ break; } } - Py_INCREF(x); } PUSH(x); continue; case LOAD_GLOBAL: w = GETITEM(names, oparg); - if (PyString_CheckExact(w)) { - /* Inline the PyDict_GetItem() calls. - WARNING: this is an extreme speed hack. - Do not try this at home. */ - long hash = ((PyStringObject *)w)->ob_shash; - if (hash != -1) { - PyDictObject *d; - d = (PyDictObject *)(f->f_globals); - x = d->ma_lookup(d, w, hash)->me_value; - if (x != NULL) { - Py_INCREF(x); - PUSH(x); - continue; + if (PyDict_CheckExact(f->f_globals)) { + if (PyString_CheckExact(w)) { + /* Inline the PyDict_GetItem() calls. + WARNING: this is an extreme speed hack. + Do not try this at home. */ + long hash = ((PyStringObject *)w)->ob_shash; + if (hash != -1) { + PyDictObject *d; + d = (PyDictObject *)(f->f_globals); + x = d->ma_lookup(d, w, hash)->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + continue; + } + d = (PyDictObject *)(f->f_builtins); + x = d->ma_lookup(d, w, hash)->me_value; + if (x != NULL) { + Py_INCREF(x); + PUSH(x); + continue; + } + goto load_global_error; } - d = (PyDictObject *)(f->f_builtins); - x = d->ma_lookup(d, w, hash)->me_value; - if (x != NULL) { - Py_INCREF(x); - PUSH(x); - continue; - } - goto load_global_error; } + /* This is the un-inlined version of the code above */ + x = PyDict_GetItem(f->f_globals, w); + Py_XINCREF(x); + } else { + x = PyObject_GetItem(f->f_globals, w); + if (x == NULL) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + break; + PyErr_Clear(); + } } - /* This is the un-inlined version of the code above */ - x = PyDict_GetItem(f->f_globals, w); if (x == NULL) { x = PyDict_GetItem(f->f_builtins, w); if (x == NULL) { @@ -1878,8 +1900,8 @@ GLOBAL_NAME_ERROR_MSG, w); break; } + Py_INCREF(x); } - Py_INCREF(x); PUSH(x); continue; Index: Lib/test/test_dictsubclassexec.py =================================================================== --- Lib/test/test_dictsubclassexec.py (revision 0) +++ Lib/test/test_dictsubclassexec.py (revision 0) @@ -0,0 +1,101 @@ +import unittest +from test import test_support + +class DictSubclass(dict): + get_notify_func = None + def __getitem__(self, key): + if self.get_notify_func: + self.get_notify_func(key) + return dict.__getitem__(self, key) + + set_notify_func = None + def __setitem__(self, key, value): + if self.set_notify_func: + self.set_notify_func(key) + return dict.__setitem__(self, key, value) + + del_notify_func = None + def __delitem__(self, key): + if self.del_notify_func: + self.del_notify_func(key) + return dict.__delitem__(self, key) + +class DictSubclassExecTest(unittest.TestCase): + def test_subclassexec_setlocal(self): + d = DictSubclass() + l = [] + d.set_notify_func = l.append + exec "a = 1" in d + d.set_notify_func = None + self.assert_(d['a'] == 1) + self.assert_('a' in l) + + def test_subclassexec_getlocal(self): + d = DictSubclass() + l = [] + d.get_notify_func = l.append + exec "a = 1; a" in d + d.get_notify_func = None + self.assert_(d['a'] == 1) + self.assert_('a' in l) + + def test_subclassexec_dellocal(self): + d = DictSubclass() + l = [] + d.del_notify_func = l.append + exec "a = 1; del a" in d + d.del_notify_func = None + self.assert_(not d.has_key('a')) + self.assert_('a' in l) + + def test_subclassexec_setglobal(self): + d = DictSubclass() + l = [] + d.set_notify_func = l.append + exec "a = 1\ndef foo():\n\tglobal a\n\tpass" in d + d.set_notify_func = None + self.assert_(d['a'] == 1) + self.assert_('a' in l) + + def test_subclassexec_getglobal(self): + d = DictSubclass() + l = [] + d.get_notify_func = l.append + exec "a = 1; a\ndef foo():\n\tglobal a\n\tpass" in d + d.get_notify_func = None + self.assert_(d['a'] == 1) + self.assert_('a' in l) + + def test_subclassexec_delglobal(self): + d = DictSubclass() + l = [] + d.del_notify_func = l.append + exec "a = 1; del a\ndef foo():\n\tglobal a\n\tpass" in d + d.del_notify_func = None + self.assert_(not d.has_key('a')) + self.assert_('a' in l) + + def test_subclassexec_getfallthrough(self): + ld = DictSubclass() + ll = [] + ld.get_notify_func = ll.append + gd = DictSubclass() + gl = [] + gd.get_notify_func = gl.append + gd['a'] = 1 + exec 'b = a' in gd, ld + gd.del_notify_func = None + ld.del_notify_func = None + self.assert_(ld['b'] == 1) + self.assert_('a' in ll) + self.assert_('a' in gl) + + +def test_main(): + test_support.run_unittest( + DictSubclassExecTest, + ) + +if __name__ == "__main__": + test_main() +