Skip to content

Commit 0eb8470

Browse files
author
Eddy Xu
committed
add warning for exec scope issue
1 parent 6af2ec3 commit 0eb8470

2 files changed

Lines changed: 46 additions & 87 deletions

File tree

Lib/test/test_py3kwarn.py

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import unittest
22
import sys
33
from test.test_support import check_py3k_warnings, CleanImport, run_unittest
4+
from test.script_helper import assert_python_ok
45
import warnings
56
import base64
67
from test import test_support
@@ -438,43 +439,16 @@ def assertExecLocalWritebackWarning(self, recorder, local_name):
438439
recorder.reset()
439440

440441
def test_exec_local_writeback_warning(self):
441-
def f_exec(code):
442-
b = 42
443-
exec code
444-
return b
445-
446-
with check_py3k_warnings() as w:
447-
f_exec("b = 99")
448-
self.assertExecLocalWritebackWarning(w, "b")
449-
450-
def f_exec_no_write(code):
451-
b = 42
452-
exec code
453-
return b
454-
455-
with check_py3k_warnings() as w:
456-
f_exec_no_write("print b")
457-
self.assertEqual(len(w.warnings), 0)
458-
459-
def f_exec_overwrite(code):
460-
b = 42
461-
exec code
462-
b = 7
463-
return b
464-
465-
with check_py3k_warnings() as w:
466-
f_exec_overwrite("b = 99")
467-
self.assertEqual(len(w.warnings), 0)
468-
469-
def f_exec_explicit(code):
470-
b = 42
471-
ns = {'b': b}
472-
exec code in globals(), ns
473-
return b
474-
475-
with check_py3k_warnings() as w:
476-
f_exec_explicit("b = 99")
477-
self.assertEqual(len(w.warnings), 0)
442+
rc, out, err = assert_python_ok(
443+
"-3",
444+
"-c",
445+
"def f(code):\n"
446+
" b = 42\n"
447+
" exec code\n"
448+
" return b\n"
449+
"f('b = 99')\n")
450+
self.assertEqual(rc, 0)
451+
self.assertIn("exec() modified local 'b' which is read later", err)
478452

479453

480454
class TestStdlibRemovals(unittest.TestCase):

Python/ceval.c

Lines changed: 35 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,28 @@ static void
8181
_clear_exec_local_writeback_for_frame(PyFrameObject *f)
8282
{
8383
PyObject *frame_key;
84+
PyObject *exc_type, *exc_value, *exc_tb;
8485

8586
if (exec_local_writeback_map == NULL) {
8687
return;
8788
}
89+
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
8890
frame_key = PyLong_FromVoidPtr(f);
8991
if (frame_key == NULL) {
9092
PyErr_Clear();
93+
PyErr_Restore(exc_type, exc_value, exc_tb);
9194
return;
9295
}
93-
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
96+
if (PyDict_GetItem(exec_local_writeback_map, frame_key) != NULL &&
97+
PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
9498
PyErr_Clear();
9599
}
96100
Py_DECREF(frame_key);
101+
PyErr_Restore(exc_type, exc_value, exc_tb);
97102
}
98103

99104
static int
100-
_warn_exec_local_writeback(PyFrameObject *f, int oparg, int opcode)
105+
_warn_exec_local_writeback(PyFrameObject *f, int oparg)
101106
{
102107
PyObject *frame_key = NULL;
103108
PyObject *frame_dict = NULL;
@@ -106,11 +111,7 @@ _warn_exec_local_writeback(PyFrameObject *f, int oparg, int opcode)
106111
PyObject *msg = NULL;
107112
PyObject *name_obj = NULL;
108113
const char *local_name = NULL;
109-
const char *func_name = NULL;
110-
int exec_lineno = 0;
111-
int exec_offset = 0;
112114
int read_lineno = 0;
113-
int read_offset = f->f_lasti;
114115
int warn_result;
115116

116117
if (exec_local_writeback_map == NULL) {
@@ -138,37 +139,18 @@ _warn_exec_local_writeback(PyFrameObject *f, int oparg, int opcode)
138139
Py_DECREF(frame_key);
139140
return 0;
140141
}
141-
if (PyTuple_Check(entry) && PyTuple_GET_SIZE(entry) == 2) {
142-
exec_lineno = (int)PyInt_AsLong(PyTuple_GET_ITEM(entry, 0));
143-
exec_offset = (int)PyInt_AsLong(PyTuple_GET_ITEM(entry, 1));
144-
}
145-
if (exec_lineno < 0) {
146-
exec_lineno = 0;
147-
}
148142
read_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
149143
name_obj = PyTuple_GetItem(f->f_code->co_varnames, oparg);
150144
if (name_obj != NULL) {
151145
local_name = PyString_AsString(name_obj);
152146
}
153-
if (local_name == NULL) {
147+
else if (local_name == NULL) {
154148
local_name = "<local>";
155149
}
156-
if (f->f_code->co_name != NULL) {
157-
func_name = PyString_AsString(f->f_code->co_name);
158-
}
159-
if (func_name == NULL) {
160-
func_name = "<function>";
161-
}
162150
msg = PyString_FromFormat(
163151
"exec() modified local '%s' which is read later in the enclosing function; "
164-
"in 3.x exec() does not reliably write back to function locals without an explicit locals mapping"
165-
"\nPYGRATE_META: {\"warning_type\":\"EXEC_LOCAL_WRITEBACK_WARNING\","
166-
"\"scope_kind\":\"function\",\"has_explicit_globals\":false,"
167-
"\"has_explicit_locals\":false,\"local_name\":\"%s\","
168-
"\"exec_lineno\":%d,\"read_lineno\":%d,\"read_opcode\":%d,"
169-
"\"exec_offset\":%d,\"read_offset\":%d,\"function_name\":\"%s\"}",
170-
local_name, local_name, exec_lineno, read_lineno, opcode,
171-
exec_offset, read_offset, func_name);
152+
"in 3.x exec() does not reliably write back to function locals without an explicit locals mapping",
153+
local_name);
172154
if (msg == NULL) {
173155
Py_DECREF(idx);
174156
Py_DECREF(frame_key);
@@ -197,10 +179,9 @@ _warn_exec_local_writeback(PyFrameObject *f, int oparg, int opcode)
197179
}
198180
Py_DECREF(idx);
199181

200-
if (PyDict_Size(frame_dict) <= 0) {
201-
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
202-
PyErr_Clear();
203-
}
182+
if (PyDict_Size(frame_dict) <= 0 &&
183+
PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
184+
PyErr_Clear();
204185
}
205186
Py_DECREF(frame_key);
206187
return 0;
@@ -212,36 +193,42 @@ _clear_exec_local_writeback_for_local(PyFrameObject *f, int oparg)
212193
PyObject *frame_key = NULL;
213194
PyObject *frame_dict = NULL;
214195
PyObject *idx = NULL;
196+
PyObject *exc_type, *exc_value, *exc_tb;
215197

216198
if (exec_local_writeback_map == NULL) {
217199
return;
218200
}
201+
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
219202
frame_key = PyLong_FromVoidPtr(f);
220203
if (frame_key == NULL) {
221204
PyErr_Clear();
205+
PyErr_Restore(exc_type, exc_value, exc_tb);
222206
return;
223207
}
224208
frame_dict = PyDict_GetItem(exec_local_writeback_map, frame_key);
225209
if (frame_dict == NULL) {
226210
Py_DECREF(frame_key);
211+
PyErr_Restore(exc_type, exc_value, exc_tb);
227212
return;
228213
}
229214
idx = PyInt_FromLong(oparg);
230215
if (idx == NULL) {
231216
Py_DECREF(frame_key);
232217
PyErr_Clear();
218+
PyErr_Restore(exc_type, exc_value, exc_tb);
233219
return;
234220
}
235-
if (PyDict_DelItem(frame_dict, idx) < 0) {
221+
if (PyDict_GetItem(frame_dict, idx) != NULL &&
222+
PyDict_DelItem(frame_dict, idx) < 0) {
236223
PyErr_Clear();
237224
}
238225
Py_DECREF(idx);
239-
if (PyDict_Size(frame_dict) <= 0) {
240-
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
241-
PyErr_Clear();
242-
}
226+
if (PyDict_Size(frame_dict) <= 0 &&
227+
PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
228+
PyErr_Clear();
243229
}
244230
Py_DECREF(frame_key);
231+
PyErr_Restore(exc_type, exc_value, exc_tb);
245232
}
246233

247234
#ifndef WITH_TSC
@@ -1451,11 +1438,10 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
14511438
{
14521439
x = GETLOCAL(oparg);
14531440
if (x != NULL) {
1454-
if (Py_Py3kWarningFlag) {
1455-
if (_warn_exec_local_writeback(f, oparg, opcode) < 0) {
1456-
err = -1;
1457-
break;
1458-
}
1441+
if (Py_Py3kWarningFlag &&
1442+
_warn_exec_local_writeback(f, oparg) < 0) {
1443+
err = -1;
1444+
break;
14591445
}
14601446
Py_INCREF(x);
14611447
PUSH(x);
@@ -5277,7 +5263,7 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
52775263
int n;
52785264
PyObject *v;
52795265
int plain = 0;
5280-
int track_locals = 0;
5266+
int track_exec_writeback = 0;
52815267
int exec_lineno = 0;
52825268
int exec_offset = 0;
52835269
int nlocals = 0;
@@ -5329,6 +5315,8 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
53295315
if (PyDict_GetItemString(globals, "__builtins__") == NULL)
53305316
PyDict_SetItemString(globals, "__builtins__", f->f_builtins);
53315317

5318+
/* Snapshot fast locals for plain exec so later LOAD_FAST warns only
5319+
about bindings that exec actually wrote back. */
53325320
if (plain && Py_Py3kWarningFlag &&
53335321
(f->f_code->co_flags & CO_NEWLOCALS) &&
53345322
f->f_code->co_nlocals > 0 &&
@@ -5340,7 +5328,7 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
53405328
before[i] = f->f_localsplus[i];
53415329
Py_XINCREF(before[i]);
53425330
}
5343-
track_locals = 1;
5331+
track_exec_writeback = 1;
53445332
exec_offset = f->f_lasti;
53455333
exec_lineno = PyCode_Addr2Line(f->f_code, exec_offset);
53465334
}
@@ -5392,7 +5380,7 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
53925380
}
53935381
if (plain)
53945382
PyFrame_LocalsToFast(f, 0);
5395-
if (track_locals && v != NULL) {
5383+
if (track_exec_writeback && v != NULL) {
53965384
PyObject *frame_key = NULL;
53975385
PyObject *frame_dict = NULL;
53985386
PyObject *map = NULL;
@@ -5437,11 +5425,8 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
54375425
if (frame_dict != NULL) {
54385426
PyObject *idx = PyInt_FromLong(i);
54395427
PyObject *val = Py_BuildValue("ii", exec_lineno, exec_offset);
5440-
if (idx != NULL && val != NULL) {
5441-
if (PyDict_SetItem(frame_dict, idx, val) < 0) {
5442-
PyErr_Clear();
5443-
}
5444-
} else {
5428+
if (idx == NULL || val == NULL ||
5429+
PyDict_SetItem(frame_dict, idx, val) < 0) {
54455430
PyErr_Clear();
54465431
}
54475432
Py_XDECREF(idx);

0 commit comments

Comments
 (0)