From f599b7424db923cde691f733b79dd24ebe814419 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Sat, 20 Apr 2002 18:21:29 +0000 Subject: [PATCH] Backport fixes for two nested scopes bugs. 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 | 1 + Lib/test/output/test_scope | 3 ++ Lib/test/test_scope.py | 78 ++++++++++++++++++++++++++++++++++---- Objects/frameobject.c | 10 ++--- Python/bltinmodule.c | 8 +++- Python/ceval.c | 5 +++ 6 files changed, 92 insertions(+), 13 deletions(-) diff --git a/Include/compile.h b/Include/compile.h index e60af59edb63..5450f90cacdf 100644 --- a/Include/compile.h +++ b/Include/compile.h @@ -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 */ diff --git a/Lib/test/output/test_scope b/Lib/test/output/test_scope index 1a44bb2d14ba..a439e441e60a 100644 --- a/Lib/test/output/test_scope +++ b/Lib/test/output/test_scope @@ -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 diff --git a/Lib/test/test_scope.py b/Lib/test/test_scope.py index fb5379067d68..720a77b5d5be 100644 --- a/Lib/test/test_scope.py +++ b/Lib/test/test_scope.py @@ -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, "") 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)() diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 6e66d23b420c..c857b5cbd921 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -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))) diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 8814ff5a9fff..ad35a22a5b50 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -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, diff --git a/Python/ceval.c b/Python/ceval.c index 27ec15b030b3..7b25449553f6 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -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)) { -- 2.47.3