Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 66bd233

Browse files
committed
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.
1 parent 32083f6 commit 66bd233

5 files changed

Lines changed: 93 additions & 6 deletions

File tree

Lib/test/test_builtin.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,11 @@ def keys(self):
282282
self.assertEqual(eval('globals()', g, m), g)
283283
self.assertEqual(eval('locals()', g, m), m)
284284
self.assertRaises(TypeError, eval, 'a', m)
285+
class A:
286+
"Non-mapping"
287+
pass
288+
m = A()
289+
self.assertRaises(TypeError, eval, 'a', g, m)
285290

286291
# Verify that dict subclasses work as well
287292
class D(dict):
@@ -336,6 +341,26 @@ def test_execfile(self):
336341
locals['z'] = 0
337342
execfile(TESTFN, globals, locals)
338343
self.assertEqual(locals['z'], 2)
344+
345+
class M:
346+
"Test mapping interface versus possible calls from execfile()."
347+
def __init__(self):
348+
self.z = 10
349+
def __getitem__(self, key):
350+
if key == 'z':
351+
return self.z
352+
raise KeyError
353+
def __setitem__(self, key, value):
354+
if key == 'z':
355+
self.z = value
356+
return
357+
raise KeyError
358+
359+
locals = M()
360+
locals['z'] = 0
361+
execfile(TESTFN, globals, locals)
362+
self.assertEqual(locals['z'], 2)
363+
339364
unlink(TESTFN)
340365
self.assertRaises(TypeError, execfile)
341366
import os

Lib/test/test_compile.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,63 @@ def test_duplicate_global_local(self):
4444
except SyntaxError:
4545
pass
4646

47+
def test_exec_with_general_mapping_for_locals(self):
48+
49+
class M:
50+
"Test mapping interface versus possible calls from eval()."
51+
def __getitem__(self, key):
52+
if key == 'a':
53+
return 12
54+
raise KeyError
55+
def __setitem__(self, key, value):
56+
self.results = (key, value)
57+
def keys(self):
58+
return list('xyz')
59+
60+
m = M()
61+
g = globals()
62+
exec 'z = a' in g, m
63+
self.assertEqual(m.results, ('z', 12))
64+
try:
65+
exec 'z = b' in g, m
66+
except NameError:
67+
pass
68+
else:
69+
self.fail('Did not detect a KeyError')
70+
exec 'z = dir()' in g, m
71+
self.assertEqual(m.results, ('z', list('xyz')))
72+
exec 'z = globals()' in g, m
73+
self.assertEqual(m.results, ('z', g))
74+
exec 'z = locals()' in g, m
75+
self.assertEqual(m.results, ('z', m))
76+
try:
77+
exec 'z = b' in m
78+
except TypeError:
79+
pass
80+
else:
81+
self.fail('Did not validate globals as a real dict')
82+
83+
class A:
84+
"Non-mapping"
85+
pass
86+
m = A()
87+
try:
88+
exec 'z = a' in g, m
89+
except TypeError:
90+
pass
91+
else:
92+
self.fail('Did not validate locals as a mapping')
93+
94+
# Verify that dict subclasses work as well
95+
class D(dict):
96+
def __getitem__(self, key):
97+
if key == 'a':
98+
return 12
99+
return dict.__getitem__(self, key)
100+
d = D()
101+
exec 'z = a' in g, d
102+
self.assertEqual(d['z'], 12)
103+
47104
def test_complex_args(self):
48105

49106
def comp_args((a, b)):

Misc/NEWS

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ Core and builtins
269269
- Bug #951851: Python crashed when reading import table of certain
270270
Windows DLLs.
271271

272-
- Bug #215126. The locals argument to eval() now accepts any mapping type.
272+
- Bug #215126. The locals argument to eval(), execfile(), and exec now
273+
accept any mapping type.
273274

274275
- marshal now shares interned strings. This change introduces
275276
a new .pyc magic.

Python/bltinmodule.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,11 +539,15 @@ builtin_execfile(PyObject *self, PyObject *args)
539539
PyCompilerFlags cf;
540540
int exists;
541541

542-
if (!PyArg_ParseTuple(args, "s|O!O!:execfile",
542+
if (!PyArg_ParseTuple(args, "s|O!O:execfile",
543543
&filename,
544544
&PyDict_Type, &globals,
545-
&PyDict_Type, &locals))
545+
&locals))
546546
return NULL;
547+
if (locals != Py_None && !PyMapping_Check(locals)) {
548+
PyErr_SetString(PyExc_TypeError, "locals must be a mapping");
549+
return NULL;
550+
}
547551
if (globals == Py_None) {
548552
globals = PyEval_GetGlobals();
549553
if (locals == Py_None)

Python/ceval.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,7 +1643,7 @@ PyEval_EvalFrame(PyFrameObject *f)
16431643
w = GETITEM(names, oparg);
16441644
v = POP();
16451645
if ((x = f->f_locals) != NULL) {
1646-
if (PyDict_CheckExact(v))
1646+
if (PyDict_CheckExact(x))
16471647
err = PyDict_SetItem(x, w, v);
16481648
else
16491649
err = PyObject_SetItem(x, w, v);
@@ -4116,9 +4116,9 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
41164116
"exec: arg 2 must be a dictionary or None");
41174117
return -1;
41184118
}
4119-
if (!PyDict_Check(locals)) {
4119+
if (!PyMapping_Check(locals)) {
41204120
PyErr_SetString(PyExc_TypeError,
4121-
"exec: arg 3 must be a dictionary or None");
4121+
"exec: arg 3 must be a mapping or None");
41224122
return -1;
41234123
}
41244124
if (PyDict_GetItemString(globals, "__builtins__") == NULL)

0 commit comments

Comments
 (0)