Index: Python/ceval.c =================================================================== --- Python/ceval.c (revision 42206) +++ 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; @@ -1819,16 +1826,27 @@ } else { x = PyObject_GetItem(v, w); - if (x == NULL && PyErr_Occurred()) { + if (x == NULL) { if (!PyErr_ExceptionMatches(PyExc_KeyError)) break; PyErr_Clear(); } } 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) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + break; + PyErr_Clear(); + } + } if (x == NULL) { x = PyDict_GetItem(f->f_builtins, w); + Py_XINCREF(x); if (x == NULL) { format_exc_check_arg( PyExc_NameError, @@ -1836,39 +1854,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 +1905,8 @@ GLOBAL_NAME_ERROR_MSG, w); break; } + Py_INCREF(x); } - Py_INCREF(x); PUSH(x); continue; @@ -3279,6 +3306,39 @@ return result; } +int +_PyEval_SetGlobalBuiltins(PyObject *globals, PyObject *bulitins) { + int ret = 0; + if (PyDict_CheckExact(globals)) { + if (PyDict_GetItemString(globals, "__builtins__") == NULL) { + if (bulitins == NULL) + bulitins = PyEval_GetBuiltins(); + if (PyDict_SetItemString(globals, "__builtins__", + bulitins) != 0) + ret = 1; + } + } else { + PyObject *builtins_str, *x; + builtins_str = PyString_InternFromString("__builtins__"); + x = PyObject_GetItem(globals, builtins_str); + if (x != NULL) { + Py_DECREF(x); + } else if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + if (bulitins == NULL) + bulitins = PyEval_GetBuiltins(); + ret = PyObject_SetItem \ + (globals, + builtins_str, + bulitins) != 0; + } else { + ret = 1; + } + Py_DECREF(builtins_str); + } + return ret; +} + void PyEval_SetProfile(Py_tracefunc func, PyObject *arg) { @@ -4166,9 +4226,9 @@ "exec: arg 1 must be a string, file, or code object"); return -1; } - if (!PyDict_Check(globals)) { + if (!PyMapping_Check(globals)) { PyErr_SetString(PyExc_TypeError, - "exec: arg 2 must be a dictionary or None"); + "exec: arg 2 must be a mapping or None"); return -1; } if (!PyMapping_Check(locals)) { @@ -4176,8 +4236,8 @@ "exec: arg 3 must be a mapping or None"); return -1; } - if (PyDict_GetItemString(globals, "__builtins__") == NULL) - PyDict_SetItemString(globals, "__builtins__", f->f_builtins); + if (_PyEval_SetGlobalBuiltins(globals, f->f_builtins) != 0) + return -1; if (PyCode_Check(prog)) { if (PyCode_GetNumFree((PyCodeObject *)prog) > 0) { PyErr_SetString(PyExc_TypeError, Index: Python/import.c =================================================================== --- Python/import.c (revision 42206) +++ Python/import.c (working copy) @@ -1982,7 +1982,7 @@ static PyObject *pathstr = NULL; PyObject *modname, *modpath, *modules, *parent; - if (globals == NULL || !PyDict_Check(globals)) + if (globals == NULL || !PyMapping_Check(globals)) return Py_None; if (namestr == NULL) { @@ -1998,11 +1998,21 @@ *buf = '\0'; *p_buflen = 0; - modname = PyDict_GetItem(globals, namestr); + if (PyDict_CheckExact(globals)) { + modname = PyDict_GetItem(globals, namestr); + } else { + modname = PyObject_GetItem(globals, namestr); + Py_XDECREF(modname); + } if (modname == NULL || !PyString_Check(modname)) return Py_None; - modpath = PyDict_GetItem(globals, pathstr); + if (PyDict_CheckExact(globals)) { + modpath = PyDict_GetItem(globals, pathstr); + Py_XINCREF(modpath); + } else { + modpath = PyObject_GetItem(globals, pathstr); + } if (modpath != NULL) { int len = PyString_GET_SIZE(modname); if (len > MAXPATHLEN) { @@ -2012,6 +2022,7 @@ } strcpy(buf, PyString_AS_STRING(modname)); *p_buflen = len; + Py_DECREF(modpath); } else { char *start = PyString_AS_STRING(modname); Index: Python/bltinmodule.c =================================================================== --- Python/bltinmodule.c (revision 42206) +++ Python/bltinmodule.c (working copy) @@ -528,10 +528,8 @@ PyErr_SetString(PyExc_TypeError, "locals must be a mapping"); return NULL; } - if (globals != Py_None && !PyDict_Check(globals)) { - PyErr_SetString(PyExc_TypeError, PyMapping_Check(globals) ? - "globals must be a real dict; try eval(expr, {}, mapping)" - : "globals must be a dict"); + if (globals != Py_None && !PyMapping_Check(globals)) { + PyErr_SetString(PyExc_TypeError, "globals must be a mapping"); return NULL; } if (globals == Py_None) { @@ -549,11 +547,8 @@ return NULL; } - if (PyDict_GetItemString(globals, "__builtins__") == NULL) { - if (PyDict_SetItemString(globals, "__builtins__", - PyEval_GetBuiltins()) != 0) - return NULL; - } + if (_PyEval_SetGlobalBuiltins(globals, NULL) != 0) + return NULL; if (PyCode_Check(cmd)) { if (PyCode_GetNumFree((PyCodeObject *)cmd) > 0) { @@ -600,7 +595,7 @@ Evaluate the source in the context of globals and locals.\n\ The source may be a string representing a Python expression\n\ or a code object as returned by compile().\n\ -The globals must be a dictionary and locals can be any mappping,\n\ +The globals and locals can be any mappping,\n\ defaulting to the current globals and locals.\n\ If only globals is given, locals defaults to it.\n"); @@ -615,15 +610,19 @@ PyCompilerFlags cf; int exists; - if (!PyArg_ParseTuple(args, "s|O!O:execfile", + if (!PyArg_ParseTuple(args, "s|OO:execfile", &filename, - &PyDict_Type, &globals, + &globals, &locals)) return NULL; if (locals != Py_None && !PyMapping_Check(locals)) { PyErr_SetString(PyExc_TypeError, "locals must be a mapping"); return NULL; } + if (globals != Py_None && !PyMapping_Check(globals)) { + PyErr_SetString(PyExc_TypeError, "globals must be a mapping"); + return NULL; + } if (globals == Py_None) { globals = PyEval_GetGlobals(); if (locals == Py_None) @@ -631,12 +630,10 @@ } else if (locals == Py_None) locals = globals; - if (PyDict_GetItemString(globals, "__builtins__") == NULL) { - if (PyDict_SetItemString(globals, "__builtins__", - PyEval_GetBuiltins()) != 0) - return NULL; - } + if (_PyEval_SetGlobalBuiltins(globals, NULL) != 0) + return NULL; + exists = 0; /* Test for existence or directory. */ #if defined(PLAN9) @@ -1091,11 +1088,10 @@ str++; globals = PyEval_GetGlobals(); locals = PyEval_GetLocals(); - if (PyDict_GetItemString(globals, "__builtins__") == NULL) { - if (PyDict_SetItemString(globals, "__builtins__", - PyEval_GetBuiltins()) != 0) - return NULL; - } + + if (_PyEval_SetGlobalBuiltins(globals, NULL) != 0) + return NULL; + cf.cf_flags = 0; PyEval_MergeCompilerFlags(&cf); res = PyRun_StringFlags(str, Py_eval_input, globals, locals, &cf); Index: Include/eval.h =================================================================== --- Include/eval.h (revision 42206) +++ Include/eval.h (working copy) @@ -19,6 +19,9 @@ PyAPI_FUNC(PyObject *) _PyEval_CallTracing(PyObject *func, PyObject *args); +PyAPI_FUNC(int) _PyEval_SetGlobalBuiltins(PyObject *globals, PyObject *bulitins); + + #ifdef __cplusplus } #endif Index: Objects/frameobject.c =================================================================== --- Objects/frameobject.c (revision 42206) +++ Objects/frameobject.c (working copy) @@ -543,7 +543,7 @@ int extras, ncells, nfrees, i; #ifdef Py_DEBUG - if (code == NULL || globals == NULL || !PyDict_Check(globals) || + if (code == NULL || globals == NULL || !PyMapping_Check(globals) || (locals != NULL && !PyMapping_Check(locals))) { PyErr_BadInternalCall(); return NULL; @@ -553,14 +553,28 @@ nfrees = PyTuple_GET_SIZE(code->co_freevars); extras = code->co_stacksize + code->co_nlocals + ncells + nfrees; if (back == NULL || back->f_globals != globals) { - builtins = PyDict_GetItem(globals, builtin_object); + if (PyDict_CheckExact(globals)) { + builtins = PyDict_GetItem(globals, builtin_object); + Py_XINCREF(builtins); + } else { + builtins = PyObject_GetItem \ + (globals, builtin_object); + if (builtins == NULL && PyErr_Occurred()) { + if (!PyErr_ExceptionMatches(PyExc_KeyError)) + return NULL; + PyErr_Clear(); + } + } if (builtins) { if (PyModule_Check(builtins)) { builtins = PyModule_GetDict(builtins); + Py_XINCREF(builtins); assert(!builtins || PyDict_Check(builtins)); } - else if (!PyDict_Check(builtins)) + else if (!PyDict_Check(builtins)) { + Py_DECREF(builtins); builtins = NULL; + } } if (builtins == NULL) { /* No builtins! Make up a minimal one @@ -571,8 +585,6 @@ builtins, "None", Py_None) < 0) return NULL; } - else - Py_INCREF(builtins); } else { Index: Objects/funcobject.c =================================================================== --- Objects/funcobject.c (revision 42206) +++ Objects/funcobject.c (working copy) @@ -48,9 +48,21 @@ return NULL; } } - module = PyDict_GetItem(globals, __name__); + if (PyDict_CheckExact(globals)) { + module = PyDict_GetItem(globals, __name__); + Py_XINCREF(module); + } else { + module = PyObject_GetItem(globals, __name__); + if (module == NULL) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + } else { + _PyObject_GC_Del(op); + return NULL; + } + } + } if (module) { - Py_INCREF(module); op->func_module = module; } } @@ -367,12 +379,18 @@ static const char *kwlist[] = {"code", "globals", "name", "argdefs", "closure", 0}; - if (!PyArg_ParseTupleAndKeywords(args, kw, "O!O!|OOO:function", + if (!PyArg_ParseTupleAndKeywords(args, kw, "O!O|OOO:function", kwlist, &PyCode_Type, &code, - &PyDict_Type, &globals, + &globals, &name, &defaults, &closure)) return NULL; + /* TODO(crutcher): should this Py_None case be removed? */ + if (globals != Py_None && !PyMapping_Check(globals)) { + PyErr_SetString(PyExc_TypeError, + "arg 2 (globals) must be a mapping"); + return NULL; + } if (name != Py_None && !PyString_Check(name)) { PyErr_SetString(PyExc_TypeError, "arg 3 (name) must be None or string"); Index: Objects/classobject.c =================================================================== --- Objects/classobject.c (revision 42206) +++ Objects/classobject.c (working copy) @@ -55,10 +55,21 @@ if (PyDict_GetItem(dict, modstr) == NULL) { PyObject *globals = PyEval_GetGlobals(); if (globals != NULL) { - PyObject *modname = PyDict_GetItem(globals, namestr); + PyObject *modname; + if (PyDict_CheckExact(globals)) { + modname = PyDict_GetItem(globals, namestr); + Py_XINCREF(modname); + } else { + modname = PyObject_GetItem(globals, namestr); + if (modname == NULL) + PyErr_Clear(); + } if (modname != NULL) { - if (PyDict_SetItem(dict, modstr, modname) < 0) + if (PyDict_SetItem(dict, modstr, modname) < 0) { + Py_DECREF(modname); return NULL; + } + Py_DECREF(modname); } } } Index: Lib/test/test_descrtut.py =================================================================== --- Lib/test/test_descrtut.py (revision 42206) +++ Lib/test/test_descrtut.py (working copy) @@ -20,6 +20,8 @@ try: return dict.__getitem__(self, key) except KeyError: + if key == '__builtins__': + raise return self.default def get(self, key, *args): Index: Lib/test/test_compile.py =================================================================== --- Lib/test/test_compile.py (revision 42206) +++ Lib/test/test_compile.py (working copy) @@ -75,10 +75,10 @@ self.assertEqual(m.results, ('z', m)) try: exec 'z = b' in m - except TypeError: + except NameError: pass else: - self.fail('Did not validate globals as a real dict') + self.fail('Did not detect a KeyError') class A: "Non-mapping" Index: Lib/test/test_mappingexec.py =================================================================== --- Lib/test/test_mappingexec.py (revision 0) +++ Lib/test/test_mappingexec.py (revision 0) @@ -0,0 +1,126 @@ +import unittest +from test import test_support + +class Mapping: + def __init__(self): + self.dict = {} + + get_notify_func = None + def __getitem__(self, key): + if self.get_notify_func: + self.get_notify_func(key) + return self.dict[key] + + set_notify_func = None + def __setitem__(self, key, value): + if self.set_notify_func: + self.set_notify_func(key) + self.dict[key] = value + + del_notify_func = None + def __delitem__(self, key): + if self.del_notify_func: + self.del_notify_func(key) + return self.dict.__delitem__(key) + + def has_key(self, key): + return self.dict.has_key(key) + +class ErrorMappingException(Exception): + pass + +class ErrorMapping(dict): + error_keys = [] + def __getitem__(self, key): + if key in self.error_keys: + raise ErrorMappingException + return dict.__getitem__(self, key) + +class MappingExecTest(unittest.TestCase): + def test_mappingexec_setlocal(self): + d = Mapping() + 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_mappingexec_getlocal(self): + d = Mapping() + 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_mappingexec_dellocal(self): + d = Mapping() + 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_mappingexec_setglobal(self): + d = Mapping() + 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_mappingexec_getglobal(self): + d = Mapping() + 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_mappingexec_delglobal(self): + d = Mapping() + 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_mappingexec_getfallthrough(self): + ld = Mapping() + ll = [] + ld.get_notify_func = ll.append + gd = Mapping() + 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_mappingexec_globalexceptions(self): + gd = ErrorMapping() + gd.error_keys = ['min'] + ld = {'local': 1} + try: + exec 'local; max; min' in gd, ld + self.fail() + except ErrorMappingException: + pass + +def test_main(): + test_support.run_unittest( + MappingExecTest, + ) + +if __name__ == "__main__": + test_main() + Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 42206) +++ Lib/test/test_builtin.py (working copy) @@ -317,6 +317,8 @@ def __getitem__(self, key): if key == 'a': return 12 + if key == '__builtins__': + return __builtins__ raise KeyError def keys(self): return list('xyz') @@ -328,7 +330,7 @@ self.assertEqual(eval('dir()', g, m), list('xyz')) self.assertEqual(eval('globals()', g, m), g) self.assertEqual(eval('locals()', g, m), m) - self.assertRaises(TypeError, eval, 'a', m) + self.assertEqual(eval('a', m), 12) class A: "Non-mapping" pass