Skip to content

Commit 6af2ec3

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

2 files changed

Lines changed: 324 additions & 0 deletions

File tree

Lib/test/test_py3kwarn.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,52 @@ def test_b16encode_warns(self):
430430
base64.b16encode(b'test')
431431
check_py3k_warnings(expected, UserWarning)
432432

433+
def assertExecLocalWritebackWarning(self, recorder, local_name):
434+
self.assertEqual(len(recorder.warnings), 1)
435+
msg = str(recorder.warnings[0].message)
436+
self.assertTrue(msg.startswith(
437+
"exec() modified local '%s'" % local_name))
438+
recorder.reset()
439+
440+
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)
478+
433479

434480
class TestStdlibRemovals(unittest.TestCase):
435481

Python/ceval.c

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,184 @@ _Py3kWarn_NextOpcode(void)
6666
return -1;
6767
}
6868

69+
static PyObject *exec_local_writeback_map = NULL;
70+
71+
static PyObject *
72+
_get_exec_local_writeback_map(void)
73+
{
74+
if (exec_local_writeback_map == NULL) {
75+
exec_local_writeback_map = PyDict_New();
76+
}
77+
return exec_local_writeback_map;
78+
}
79+
80+
static void
81+
_clear_exec_local_writeback_for_frame(PyFrameObject *f)
82+
{
83+
PyObject *frame_key;
84+
85+
if (exec_local_writeback_map == NULL) {
86+
return;
87+
}
88+
frame_key = PyLong_FromVoidPtr(f);
89+
if (frame_key == NULL) {
90+
PyErr_Clear();
91+
return;
92+
}
93+
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
94+
PyErr_Clear();
95+
}
96+
Py_DECREF(frame_key);
97+
}
98+
99+
static int
100+
_warn_exec_local_writeback(PyFrameObject *f, int oparg, int opcode)
101+
{
102+
PyObject *frame_key = NULL;
103+
PyObject *frame_dict = NULL;
104+
PyObject *entry = NULL;
105+
PyObject *idx = NULL;
106+
PyObject *msg = NULL;
107+
PyObject *name_obj = NULL;
108+
const char *local_name = NULL;
109+
const char *func_name = NULL;
110+
int exec_lineno = 0;
111+
int exec_offset = 0;
112+
int read_lineno = 0;
113+
int read_offset = f->f_lasti;
114+
int warn_result;
115+
116+
if (exec_local_writeback_map == NULL) {
117+
return 0;
118+
}
119+
frame_key = PyLong_FromVoidPtr(f);
120+
if (frame_key == NULL) {
121+
PyErr_Clear();
122+
return 0;
123+
}
124+
frame_dict = PyDict_GetItem(exec_local_writeback_map, frame_key);
125+
if (frame_dict == NULL) {
126+
Py_DECREF(frame_key);
127+
return 0;
128+
}
129+
idx = PyInt_FromLong(oparg);
130+
if (idx == NULL) {
131+
Py_DECREF(frame_key);
132+
PyErr_Clear();
133+
return 0;
134+
}
135+
entry = PyDict_GetItem(frame_dict, idx);
136+
if (entry == NULL) {
137+
Py_DECREF(idx);
138+
Py_DECREF(frame_key);
139+
return 0;
140+
}
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+
}
148+
read_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
149+
name_obj = PyTuple_GetItem(f->f_code->co_varnames, oparg);
150+
if (name_obj != NULL) {
151+
local_name = PyString_AsString(name_obj);
152+
}
153+
if (local_name == NULL) {
154+
local_name = "<local>";
155+
}
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+
}
162+
msg = PyString_FromFormat(
163+
"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);
172+
if (msg == NULL) {
173+
Py_DECREF(idx);
174+
Py_DECREF(frame_key);
175+
PyErr_Clear();
176+
return 0;
177+
}
178+
179+
warn_result = PyErr_WarnExplicit_WithFix(
180+
PyExc_Py3xWarning,
181+
PyString_AsString(msg),
182+
"use exec(code, globals, locals) with an explicit locals mapping",
183+
PyString_AsString(f->f_code->co_filename),
184+
read_lineno,
185+
NULL,
186+
NULL);
187+
188+
Py_DECREF(msg);
189+
if (warn_result < 0) {
190+
Py_DECREF(idx);
191+
Py_DECREF(frame_key);
192+
return -1;
193+
}
194+
195+
if (PyDict_DelItem(frame_dict, idx) < 0) {
196+
PyErr_Clear();
197+
}
198+
Py_DECREF(idx);
199+
200+
if (PyDict_Size(frame_dict) <= 0) {
201+
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
202+
PyErr_Clear();
203+
}
204+
}
205+
Py_DECREF(frame_key);
206+
return 0;
207+
}
208+
209+
static void
210+
_clear_exec_local_writeback_for_local(PyFrameObject *f, int oparg)
211+
{
212+
PyObject *frame_key = NULL;
213+
PyObject *frame_dict = NULL;
214+
PyObject *idx = NULL;
215+
216+
if (exec_local_writeback_map == NULL) {
217+
return;
218+
}
219+
frame_key = PyLong_FromVoidPtr(f);
220+
if (frame_key == NULL) {
221+
PyErr_Clear();
222+
return;
223+
}
224+
frame_dict = PyDict_GetItem(exec_local_writeback_map, frame_key);
225+
if (frame_dict == NULL) {
226+
Py_DECREF(frame_key);
227+
return;
228+
}
229+
idx = PyInt_FromLong(oparg);
230+
if (idx == NULL) {
231+
Py_DECREF(frame_key);
232+
PyErr_Clear();
233+
return;
234+
}
235+
if (PyDict_DelItem(frame_dict, idx) < 0) {
236+
PyErr_Clear();
237+
}
238+
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+
}
243+
}
244+
Py_DECREF(frame_key);
245+
}
246+
69247
#ifndef WITH_TSC
70248

71249
#define READ_TIMESTAMP(var)
@@ -1273,6 +1451,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
12731451
{
12741452
x = GETLOCAL(oparg);
12751453
if (x != NULL) {
1454+
if (Py_Py3kWarningFlag) {
1455+
if (_warn_exec_local_writeback(f, oparg, opcode) < 0) {
1456+
err = -1;
1457+
break;
1458+
}
1459+
}
12761460
Py_INCREF(x);
12771461
PUSH(x);
12781462
FAST_DISPATCH();
@@ -1296,6 +1480,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
12961480
{
12971481
v = POP();
12981482
SETLOCAL(oparg, v);
1483+
if (Py_Py3kWarningFlag) {
1484+
_clear_exec_local_writeback_for_local(f, oparg);
1485+
}
12991486
FAST_DISPATCH();
13001487
}
13011488

@@ -2454,6 +2641,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
24542641
x = GETLOCAL(oparg);
24552642
if (x != NULL) {
24562643
SETLOCAL(oparg, NULL);
2644+
if (Py_Py3kWarningFlag) {
2645+
_clear_exec_local_writeback_for_local(f, oparg);
2646+
}
24572647
DISPATCH();
24582648
}
24592649
format_exc_check_arg(
@@ -3404,6 +3594,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
34043594

34053595
/* pop frame */
34063596
exit_eval_frame:
3597+
_clear_exec_local_writeback_for_frame(f);
34073598
Py_LeaveRecursiveCall();
34083599
tstate->frame = f->f_back;
34093600

@@ -5086,6 +5277,12 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
50865277
int n;
50875278
PyObject *v;
50885279
int plain = 0;
5280+
int track_locals = 0;
5281+
int exec_lineno = 0;
5282+
int exec_offset = 0;
5283+
int nlocals = 0;
5284+
PyObject **before = NULL;
5285+
int i;
50895286

50905287
if (PyTuple_Check(prog) && globals == Py_None && locals == Py_None &&
50915288
((n = PyTuple_Size(prog)) == 2 || n == 3)) {
@@ -5131,6 +5328,23 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
51315328
}
51325329
if (PyDict_GetItemString(globals, "__builtins__") == NULL)
51335330
PyDict_SetItemString(globals, "__builtins__", f->f_builtins);
5331+
5332+
if (plain && Py_Py3kWarningFlag &&
5333+
(f->f_code->co_flags & CO_NEWLOCALS) &&
5334+
f->f_code->co_nlocals > 0 &&
5335+
f->f_localsplus != NULL) {
5336+
nlocals = f->f_code->co_nlocals;
5337+
before = PyMem_New(PyObject *, nlocals);
5338+
if (before != NULL) {
5339+
for (i = 0; i < nlocals; i++) {
5340+
before[i] = f->f_localsplus[i];
5341+
Py_XINCREF(before[i]);
5342+
}
5343+
track_locals = 1;
5344+
exec_offset = f->f_lasti;
5345+
exec_lineno = PyCode_Addr2Line(f->f_code, exec_offset);
5346+
}
5347+
}
51345348
if (PyCode_Check(prog)) {
51355349
if (PyCode_GetNumFree((PyCodeObject *)prog) > 0) {
51365350
PyErr_SetString(PyExc_TypeError,
@@ -5178,6 +5392,70 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
51785392
}
51795393
if (plain)
51805394
PyFrame_LocalsToFast(f, 0);
5395+
if (track_locals && v != NULL) {
5396+
PyObject *frame_key = NULL;
5397+
PyObject *frame_dict = NULL;
5398+
PyObject *map = NULL;
5399+
for (i = 0; i < nlocals; i++) {
5400+
PyObject *before_obj = before[i];
5401+
PyObject *after_obj = f->f_localsplus[i];
5402+
if (before_obj == after_obj) {
5403+
continue;
5404+
}
5405+
if (frame_key == NULL) {
5406+
frame_key = PyLong_FromVoidPtr(f);
5407+
if (frame_key == NULL) {
5408+
PyErr_Clear();
5409+
break;
5410+
}
5411+
}
5412+
if (map == NULL) {
5413+
map = _get_exec_local_writeback_map();
5414+
if (map == NULL) {
5415+
PyErr_Clear();
5416+
break;
5417+
}
5418+
}
5419+
if (frame_dict == NULL) {
5420+
frame_dict = PyDict_GetItem(map, frame_key);
5421+
if (frame_dict == NULL) {
5422+
frame_dict = PyDict_New();
5423+
if (frame_dict == NULL) {
5424+
PyErr_Clear();
5425+
break;
5426+
}
5427+
if (PyDict_SetItem(map, frame_key, frame_dict) < 0) {
5428+
PyErr_Clear();
5429+
Py_DECREF(frame_dict);
5430+
frame_dict = NULL;
5431+
break;
5432+
}
5433+
Py_DECREF(frame_dict);
5434+
frame_dict = PyDict_GetItem(map, frame_key);
5435+
}
5436+
}
5437+
if (frame_dict != NULL) {
5438+
PyObject *idx = PyInt_FromLong(i);
5439+
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 {
5445+
PyErr_Clear();
5446+
}
5447+
Py_XDECREF(idx);
5448+
Py_XDECREF(val);
5449+
}
5450+
}
5451+
Py_XDECREF(frame_key);
5452+
}
5453+
if (before != NULL) {
5454+
for (i = 0; i < nlocals; i++) {
5455+
Py_XDECREF(before[i]);
5456+
}
5457+
PyMem_Free(before);
5458+
}
51815459
if (v == NULL)
51825460
return -1;
51835461
Py_DECREF(v);

0 commit comments

Comments
 (0)