Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions Lib/test/test_py3kwarn.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import unittest
import sys
from test.test_support import check_py3k_warnings, CleanImport, run_unittest
from test.script_helper import assert_python_ok
import warnings
import base64
from test import test_support
Expand Down Expand Up @@ -430,6 +431,25 @@ def test_b16encode_warns(self):
base64.b16encode(b'test')
check_py3k_warnings(expected, UserWarning)

def assertExecLocalWritebackWarning(self, recorder, local_name):
self.assertEqual(len(recorder.warnings), 1)
msg = str(recorder.warnings[0].message)
self.assertTrue(msg.startswith(
"exec() modified local '%s'" % local_name))
recorder.reset()

def test_exec_local_writeback_warning(self):
rc, out, err = assert_python_ok(
"-3",
"-c",
"def f(code):\n"
" b = 42\n"
" exec code\n"
" return b\n"
"f('b = 99')\n")
self.assertEqual(rc, 0)
self.assertIn("exec() modified local 'b' which is read later", err)


class TestStdlibRemovals(unittest.TestCase):

Expand Down
269 changes: 269 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,175 @@ _Py3kWarn_NextOpcode(void)
return -1;
}

static PyObject *exec_local_writeback_map = NULL;

static PyObject *
_get_exec_local_writeback_map(void)
{
if (exec_local_writeback_map == NULL) {
exec_local_writeback_map = PyDict_New();
}
return exec_local_writeback_map;
}

static void
_clear_exec_local_writeback_for_frame(PyFrameObject *f)
{
PyObject *frame_key;
PyObject *exc_type, *exc_value, *exc_tb;

if (exec_local_writeback_map == NULL) {
return;
}
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
frame_key = PyLong_FromVoidPtr(f);
if (frame_key == NULL) {
PyErr_Clear();
PyErr_Restore(exc_type, exc_value, exc_tb);
return;
}
if (PyDict_GetItem(exec_local_writeback_map, frame_key) != NULL) {
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
PyErr_Clear();
}
}
Py_DECREF(frame_key);
PyErr_Restore(exc_type, exc_value, exc_tb);
}

static int
_warn_exec_local_writeback(PyFrameObject *f, int oparg)
{
PyObject *frame_key = NULL;
PyObject *frame_dict = NULL;
PyObject *entry = NULL;
PyObject *idx = NULL;
PyObject *msg = NULL;
PyObject *name_obj = NULL;
const char *local_name = NULL;
int read_lineno = 0;
int warn_result;

if (exec_local_writeback_map == NULL) {
return 0;
}
frame_key = PyLong_FromVoidPtr(f);
if (frame_key == NULL) {
PyErr_Clear();
return 0;
}
frame_dict = PyDict_GetItem(exec_local_writeback_map, frame_key);
if (frame_dict == NULL) {
Py_DECREF(frame_key);
return 0;
}
idx = PyInt_FromLong(oparg);
if (idx == NULL) {
Py_DECREF(frame_key);
PyErr_Clear();
return 0;
}
entry = PyDict_GetItem(frame_dict, idx);
if (entry == NULL) {
Py_DECREF(idx);
Py_DECREF(frame_key);
return 0;
}
read_lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
name_obj = PyTuple_GetItem(f->f_code->co_varnames, oparg);
if (name_obj != NULL) {
local_name = PyString_AsString(name_obj);
}
if (local_name == NULL) {
local_name = "<local>";
}
msg = PyString_FromFormat(
"exec() modified local '%s' which is read later in the enclosing function; "
"in 3.x exec() does not reliably write back to function locals without an explicit locals mapping",
local_name);
if (msg == NULL) {
Py_DECREF(idx);
Py_DECREF(frame_key);
PyErr_Clear();
return 0;
}

warn_result = PyErr_WarnExplicit_WithFix(
PyExc_Py3xWarning,
PyString_AsString(msg),
"use exec(code, globals, locals) with an explicit locals mapping",
PyString_AsString(f->f_code->co_filename),
read_lineno,
NULL,
NULL);

Py_DECREF(msg);
if (warn_result < 0) {
Py_DECREF(idx);
Py_DECREF(frame_key);
return -1;
}

if (PyDict_DelItem(frame_dict, idx) < 0) {
PyErr_Clear();
}
Py_DECREF(idx);

if (PyDict_Size(frame_dict) <= 0) {
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
PyErr_Clear();
}
}
Py_DECREF(frame_key);
return 0;
}

static void
_clear_exec_local_writeback_for_local(PyFrameObject *f, int oparg)
{
PyObject *frame_key = NULL;
PyObject *frame_dict = NULL;
PyObject *idx = NULL;
PyObject *exc_type, *exc_value, *exc_tb;

if (exec_local_writeback_map == NULL) {
return;
}
PyErr_Fetch(&exc_type, &exc_value, &exc_tb);
frame_key = PyLong_FromVoidPtr(f);
if (frame_key == NULL) {
PyErr_Clear();
PyErr_Restore(exc_type, exc_value, exc_tb);
return;
}
frame_dict = PyDict_GetItem(exec_local_writeback_map, frame_key);
if (frame_dict == NULL) {
Py_DECREF(frame_key);
PyErr_Restore(exc_type, exc_value, exc_tb);
return;
}
idx = PyInt_FromLong(oparg);
if (idx == NULL) {
Py_DECREF(frame_key);
PyErr_Clear();
PyErr_Restore(exc_type, exc_value, exc_tb);
return;
}
if (PyDict_GetItem(frame_dict, idx) != NULL) {
if (PyDict_DelItem(frame_dict, idx) < 0) {
PyErr_Clear();
}
}
Py_DECREF(idx);
if (PyDict_Size(frame_dict) <= 0) {
if (PyDict_DelItem(exec_local_writeback_map, frame_key) < 0) {
PyErr_Clear();
}
}
Py_DECREF(frame_key);
PyErr_Restore(exc_type, exc_value, exc_tb);
}

#ifndef WITH_TSC

#define READ_TIMESTAMP(var)
Expand Down Expand Up @@ -1273,6 +1442,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
x = GETLOCAL(oparg);
if (x != NULL) {
if (Py_Py3kWarningFlag) {
if (_warn_exec_local_writeback(f, oparg) < 0) {
err = -1;
break;
}
}
Py_INCREF(x);
PUSH(x);
FAST_DISPATCH();
Expand All @@ -1296,6 +1471,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
{
v = POP();
SETLOCAL(oparg, v);
if (Py_Py3kWarningFlag) {
_clear_exec_local_writeback_for_local(f, oparg);
}
FAST_DISPATCH();
}

Expand Down Expand Up @@ -2454,6 +2632,9 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
x = GETLOCAL(oparg);
if (x != NULL) {
SETLOCAL(oparg, NULL);
if (Py_Py3kWarningFlag) {
_clear_exec_local_writeback_for_local(f, oparg);
}
DISPATCH();
}
format_exc_check_arg(
Expand Down Expand Up @@ -3404,6 +3585,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)

/* pop frame */
exit_eval_frame:
_clear_exec_local_writeback_for_frame(f);
Py_LeaveRecursiveCall();
tstate->frame = f->f_back;

Expand Down Expand Up @@ -5086,6 +5268,12 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
int n;
PyObject *v;
int plain = 0;
int track_locals = 0;
int exec_lineno = 0;
int exec_offset = 0;
int nlocals = 0;
PyObject **before = NULL;
int i;

if (PyTuple_Check(prog) && globals == Py_None && locals == Py_None &&
((n = PyTuple_Size(prog)) == 2 || n == 3)) {
Expand Down Expand Up @@ -5131,6 +5319,23 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
}
if (PyDict_GetItemString(globals, "__builtins__") == NULL)
PyDict_SetItemString(globals, "__builtins__", f->f_builtins);

if (plain && Py_Py3kWarningFlag &&
(f->f_code->co_flags & CO_NEWLOCALS) &&
f->f_code->co_nlocals > 0 &&
f->f_localsplus != NULL) {
nlocals = f->f_code->co_nlocals;
before = PyMem_New(PyObject *, nlocals);
if (before != NULL) {
for (i = 0; i < nlocals; i++) {
before[i] = f->f_localsplus[i];
Py_XINCREF(before[i]);
}
track_locals = 1;
exec_offset = f->f_lasti;
exec_lineno = PyCode_Addr2Line(f->f_code, exec_offset);
}
}
if (PyCode_Check(prog)) {
if (PyCode_GetNumFree((PyCodeObject *)prog) > 0) {
PyErr_SetString(PyExc_TypeError,
Expand Down Expand Up @@ -5178,6 +5383,70 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
}
if (plain)
PyFrame_LocalsToFast(f, 0);
if (track_locals && v != NULL) {
PyObject *frame_key = NULL;
PyObject *frame_dict = NULL;
PyObject *map = NULL;
for (i = 0; i < nlocals; i++) {
PyObject *before_obj = before[i];
PyObject *after_obj = f->f_localsplus[i];
if (before_obj == after_obj) {
continue;
}
if (frame_key == NULL) {
frame_key = PyLong_FromVoidPtr(f);
if (frame_key == NULL) {
PyErr_Clear();
break;
}
}
if (map == NULL) {
map = _get_exec_local_writeback_map();
if (map == NULL) {
PyErr_Clear();
break;
}
}
if (frame_dict == NULL) {
frame_dict = PyDict_GetItem(map, frame_key);
if (frame_dict == NULL) {
frame_dict = PyDict_New();
if (frame_dict == NULL) {
PyErr_Clear();
break;
}
if (PyDict_SetItem(map, frame_key, frame_dict) < 0) {
PyErr_Clear();
Py_DECREF(frame_dict);
frame_dict = NULL;
break;
}
Py_DECREF(frame_dict);
frame_dict = PyDict_GetItem(map, frame_key);
}
}
if (frame_dict != NULL) {
PyObject *idx = PyInt_FromLong(i);
PyObject *val = Py_BuildValue("ii", exec_lineno, exec_offset);
if (idx != NULL && val != NULL) {
if (PyDict_SetItem(frame_dict, idx, val) < 0) {
PyErr_Clear();
}
} else {
PyErr_Clear();
}
Py_XDECREF(idx);
Py_XDECREF(val);
}
}
Py_XDECREF(frame_key);
}
if (before != NULL) {
for (i = 0; i < nlocals; i++) {
Py_XDECREF(before[i]);
}
PyMem_Free(before);
}
if (v == NULL)
return -1;
Py_DECREF(v);
Expand Down