Completed the patch for Bug #215126.

* Fixes an incorrect variable in a PyDict_CheckExact.
* Allow general mapping locals arguments for the execfile() function
  and exec statement.
* Add tests.
This commit is contained in:
Raymond Hettinger 2004-08-02 08:30:07 +00:00
parent 32083f64a7
commit 66bd233225
5 changed files with 93 additions and 6 deletions

View File

@ -282,6 +282,11 @@ class BuiltinTest(unittest.TestCase):
self.assertEqual(eval('globals()', g, m), g)
self.assertEqual(eval('locals()', g, m), m)
self.assertRaises(TypeError, eval, 'a', m)
class A:
"Non-mapping"
pass
m = A()
self.assertRaises(TypeError, eval, 'a', g, m)
# Verify that dict subclasses work as well
class D(dict):
@ -336,6 +341,26 @@ class BuiltinTest(unittest.TestCase):
locals['z'] = 0
execfile(TESTFN, globals, locals)
self.assertEqual(locals['z'], 2)
class M:
"Test mapping interface versus possible calls from execfile()."
def __init__(self):
self.z = 10
def __getitem__(self, key):
if key == 'z':
return self.z
raise KeyError
def __setitem__(self, key, value):
if key == 'z':
self.z = value
return
raise KeyError
locals = M()
locals['z'] = 0
execfile(TESTFN, globals, locals)
self.assertEqual(locals['z'], 2)
unlink(TESTFN)
self.assertRaises(TypeError, execfile)
import os

View File

@ -44,6 +44,63 @@ class TestSpecifics(unittest.TestCase):
except SyntaxError:
pass
def test_exec_with_general_mapping_for_locals(self):
class M:
"Test mapping interface versus possible calls from eval()."
def __getitem__(self, key):
if key == 'a':
return 12
raise KeyError
def __setitem__(self, key, value):
self.results = (key, value)
def keys(self):
return list('xyz')
m = M()
g = globals()
exec 'z = a' in g, m
self.assertEqual(m.results, ('z', 12))
try:
exec 'z = b' in g, m
except NameError:
pass
else:
self.fail('Did not detect a KeyError')
exec 'z = dir()' in g, m
self.assertEqual(m.results, ('z', list('xyz')))
exec 'z = globals()' in g, m
self.assertEqual(m.results, ('z', g))
exec 'z = locals()' in g, m
self.assertEqual(m.results, ('z', m))
try:
exec 'z = b' in m
except TypeError:
pass
else:
self.fail('Did not validate globals as a real dict')
class A:
"Non-mapping"
pass
m = A()
try:
exec 'z = a' in g, m
except TypeError:
pass
else:
self.fail('Did not validate locals as a mapping')
# Verify that dict subclasses work as well
class D(dict):
def __getitem__(self, key):
if key == 'a':
return 12
return dict.__getitem__(self, key)
d = D()
exec 'z = a' in g, d
self.assertEqual(d['z'], 12)
def test_complex_args(self):
def comp_args((a, b)):

View File

@ -269,7 +269,8 @@ Core and builtins
- Bug #951851: Python crashed when reading import table of certain
Windows DLLs.
- Bug #215126. The locals argument to eval() now accepts any mapping type.
- Bug #215126. The locals argument to eval(), execfile(), and exec now
accept any mapping type.
- marshal now shares interned strings. This change introduces
a new .pyc magic.

View File

@ -539,11 +539,15 @@ builtin_execfile(PyObject *self, PyObject *args)
PyCompilerFlags cf;
int exists;
if (!PyArg_ParseTuple(args, "s|O!O!:execfile",
if (!PyArg_ParseTuple(args, "s|O!O:execfile",
&filename,
&PyDict_Type, &globals,
&PyDict_Type, &locals))
&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) {
globals = PyEval_GetGlobals();
if (locals == Py_None)

View File

@ -1643,7 +1643,7 @@ PyEval_EvalFrame(PyFrameObject *f)
w = GETITEM(names, oparg);
v = POP();
if ((x = f->f_locals) != NULL) {
if (PyDict_CheckExact(v))
if (PyDict_CheckExact(x))
err = PyDict_SetItem(x, w, v);
else
err = PyObject_SetItem(x, w, v);
@ -4116,9 +4116,9 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
"exec: arg 2 must be a dictionary or None");
return -1;
}
if (!PyDict_Check(locals)) {
if (!PyMapping_Check(locals)) {
PyErr_SetString(PyExc_TypeError,
"exec: arg 3 must be a dictionary or None");
"exec: arg 3 must be a mapping or None");
return -1;
}
if (PyDict_GetItemString(globals, "__builtins__") == NULL)