]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Backport fixes for two nested scopes bugs.
authorJeremy Hylton <jeremy@alum.mit.edu>
Sat, 20 Apr 2002 18:21:29 +0000 (18:21 +0000)
committerJeremy Hylton <jeremy@alum.mit.edu>
Sat, 20 Apr 2002 18:21:29 +0000 (18:21 +0000)
frameobject.c: make sure free and cell vars make it into locals, which
    makes eval work.

bltinmodule.c & ceval.c: make sure a code object with free variables
    that is passed to exec or eval raises an exception.

Also duplicate the current trunk test suite in the 2.1 branch, except
for certain necessary changes: different warnings raised by 2.1, need
for __future__.

Include/compile.h
Lib/test/output/test_scope
Lib/test/test_scope.py
Objects/frameobject.c
Python/bltinmodule.c
Python/ceval.c

index e60af59edb63a67c5a02d9d774d1a4918c4e44fb..5450f90cacdf791ccd1e05b1e091c84f6126cce5 100644 (file)
@@ -37,6 +37,7 @@ typedef struct {
 extern DL_IMPORT(PyTypeObject) PyCode_Type;
 
 #define PyCode_Check(op) ((op)->ob_type == &PyCode_Type)
+#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars))
 
 #define CO_MAXBLOCKS 20 /* Max static block nesting within a function */
 
index 1a44bb2d14ba4093b9906a64b5c6186407d65741..a439e441e60aeaf009519badb3e2c19088ee12b7 100644 (file)
@@ -19,3 +19,6 @@ test_scope
 18. verify that locals() works
 19. var is bound and free in class
 20. interaction with trace function
+20. eval and exec with free variables
+21. list comprehension with local variables
+22. eval with free variables
index fb5379067d68c0813f476535acba96f4313d69d9..720a77b5d5be77fc660bda009caa856e1c67a84d 100644 (file)
@@ -1,6 +1,10 @@
 from __future__ import nested_scopes
 
-from test.test_support import verify, TestFailed, check_syntax
+from test_support import verify, TestFailed, check_syntax
+
+import warnings
+warnings.filterwarnings("ignore", r"(import \*|local name|unqualified)",
+                        SyntaxWarning, "<string>")
 
 print "1. simple nesting"
 
@@ -179,7 +183,8 @@ verify(f(6) == 720)
 
 print "11. unoptimized namespaces"
 
-check_syntax("""from __future__ import nested_scopes
+check_syntax("""\
+from __future__ import nested_scopes
 def unoptimized_clash1(strip):
     def f(s):
         from string import *
@@ -187,7 +192,8 @@ def unoptimized_clash1(strip):
     return f
 """)
 
-check_syntax("""from __future__ import nested_scopes
+check_syntax("""\
+from __future__ import nested_scopes
 def unoptimized_clash2():
     from string import *
     def f(s):
@@ -195,7 +201,8 @@ def unoptimized_clash2():
     return f
 """)
 
-check_syntax("""from __future__ import nested_scopes
+check_syntax("""\
+from __future__ import nested_scopes
 def unoptimized_clash2():
     from string import *
     def g():
@@ -205,7 +212,8 @@ def unoptimized_clash2():
 """)
 
 # XXX could allow this for exec with const argument, but what's the point
-check_syntax("""from __future__ import nested_scopes
+check_syntax("""\
+from __future__ import nested_scopes
 def error(y):
     exec "a = 1"
     def f(x):
@@ -213,14 +221,16 @@ def error(y):
     return f
 """)
 
-check_syntax("""from __future__ import nested_scopes
+check_syntax("""\
+from __future__ import nested_scopes
 def f(x):
     def g():
         return x
     del x # can't del name
 """)
 
-check_syntax("""from __future__ import nested_scopes
+check_syntax("""\
+from __future__ import nested_scopes
 def f():
     def g():
          from string import *
@@ -229,6 +239,7 @@ def f():
 
 # and verify a few cases that should work
 
+exec """
 def noproblem1():
     from string import *
     f = lambda x:x
@@ -243,6 +254,7 @@ def noproblem3():
     def f(x):
         global y
         y = x
+"""
 
 print "12. lambdas"
 
@@ -467,3 +479,55 @@ class TestClass:
 sys.settrace(tracer)
 adaptgetter("foo", TestClass, (1, ""))
 sys.settrace(None)
+
+##try: sys.settrace()
+##except TypeError: pass
+##else: raise TestFailed, 'sys.settrace() did not raise TypeError'
+
+print "20. eval and exec with free variables"
+
+def f(x):
+    return lambda: x + 1
+
+g = f(3)
+try:
+    eval(g.func_code)
+except TypeError:
+    pass
+else:
+    print "eval() should have failed, because code contained free vars"
+
+try:
+    exec g.func_code
+except TypeError:
+    pass
+else:
+    print "exec should have failed, because code contained free vars"
+
+print "21. list comprehension with local variables"
+
+try:
+    print bad
+except NameError:
+    pass
+else:
+    print "bad should not be defined"
+
+def x():
+    [bad for s in 'a b' for bad in s.split()]
+
+x()
+try:
+    print bad
+except NameError:
+    pass
+
+print "22. eval with free variables"
+
+def f(free):
+    def g():
+        free
+        eval("free + 1")
+    return g
+
+f(4)()
index 6e66d23b420c5a1b9c55d6a49e61bc8bcf367435..c857b5cbd921a25ecc93b4d6bf041e3b480c2d47 100644 (file)
@@ -312,8 +312,6 @@ PyFrame_FastToLocals(PyFrameObject *f)
                        return;
                }
        }
-       if (f->f_nlocals == 0)
-               return;
        map = f->f_code->co_varnames;
        if (!PyDict_Check(locals) || !PyTuple_Check(map))
                return;
@@ -322,7 +320,8 @@ PyFrame_FastToLocals(PyFrameObject *f)
        j = PyTuple_Size(map);
        if (j > f->f_nlocals)
                j = f->f_nlocals;
-       map_to_dict(map, j, locals, fast, 0);
+       if (f->f_nlocals)
+           map_to_dict(map, j, locals, fast, 0);
        if (f->f_ncells || f->f_nfreevars) {
                if (!(PyTuple_Check(f->f_code->co_cellvars)
                      && PyTuple_Check(f->f_code->co_freevars))) {
@@ -351,7 +350,7 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
                return;
        locals = f->f_locals;
        map = f->f_code->co_varnames;
-       if (locals == NULL || f->f_code->co_nlocals == 0)
+       if (locals == NULL)
                return;
        if (!PyDict_Check(locals) || !PyTuple_Check(map))
                return;
@@ -360,7 +359,8 @@ PyFrame_LocalsToFast(PyFrameObject *f, int clear)
        j = PyTuple_Size(map);
        if (j > f->f_nlocals)
                j = f->f_nlocals;
-       dict_to_map(f->f_code->co_varnames, j, locals, fast, 0, clear);
+       if (f->f_nlocals)
+           dict_to_map(f->f_code->co_varnames, j, locals, fast, 0, clear);
        if (f->f_ncells || f->f_nfreevars) {
                if (!(PyTuple_Check(f->f_code->co_cellvars)
                      && PyTuple_Check(f->f_code->co_freevars)))
index 8814ff5a9fffa2182c7ff1d242d99b20d1b0fde1..ad35a22a5b50aeb6d711af24aa7ef91ff7f5324d 100644 (file)
@@ -756,8 +756,14 @@ builtin_eval(PyObject *self, PyObject *args)
                                         PyEval_GetBuiltins()) != 0)
                        return NULL;
        }
-       if (PyCode_Check(cmd))
+       if (PyCode_Check(cmd)) {
+               if (PyCode_GetNumFree((PyCodeObject *)cmd) > 0) {
+                       PyErr_SetString(PyExc_TypeError,
+               "code object passed to eval() may not contain free variables");
+                       return NULL;
+               }
                return PyEval_EvalCode((PyCodeObject *) cmd, globals, locals);
+       }
        if (!PyString_Check(cmd) &&
            !PyUnicode_Check(cmd)) {
                PyErr_SetString(PyExc_TypeError,
index 27ec15b030b36e1fb473cf07dbc265254054c938..7b25449553f67b64c6ae737997f1bb949d1d49fa 100644 (file)
@@ -3514,6 +3514,11 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
        if (PyDict_GetItemString(globals, "__builtins__") == NULL)
                PyDict_SetItemString(globals, "__builtins__", f->f_builtins);
        if (PyCode_Check(prog)) {
+               if (PyCode_GetNumFree((PyCodeObject *)prog) > 0) {
+                       PyErr_SetString(PyExc_TypeError,
+               "code object passed to exec may not contain free variables");
+                       return -1;
+               }
                v = PyEval_EvalCode((PyCodeObject *) prog, globals, locals);
        }
        else if (PyFile_Check(prog)) {