]> granicus.if.org Git - python/commitdiff
Completed the patch for Bug #215126.
authorRaymond Hettinger <python@rcn.com>
Mon, 2 Aug 2004 08:30:07 +0000 (08:30 +0000)
committerRaymond Hettinger <python@rcn.com>
Mon, 2 Aug 2004 08:30:07 +0000 (08:30 +0000)
* Fixes an incorrect variable in a PyDict_CheckExact.
* Allow general mapping locals arguments for the execfile() function
  and exec statement.
* Add tests.

Lib/test/test_builtin.py
Lib/test/test_compile.py
Misc/NEWS
Python/bltinmodule.c
Python/ceval.c

index fdbbdfcb8506cd61e328691a2f2efa9dbe270b18..6654f565374c39d4ae5e55ad82db319b4fc0a7b0 100644 (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
index 5b7b7170dfcd528135db3bed537875e85e5e181a..b1644cb35e642ad3b4e8ecbbafc1ddd39ee2c32b 100644 (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)):
index 15713e945b70a71acc339984195619913ba794a8..edd59896503fdf65e88f6220d223facf4d89bd02 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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.
index 4143681b8a318ecfa88ab9dd869cf06647606e95..b76f3739fa6b351f7292e0f45e1bbab00ca7e9a9 100644 (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)
index 152b9421283ebbd84f93f221d8247decc6878be9..3a462afbed0723b507d0c0a9f7cdc1ec9e781794 100644 (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)