Issue #18408: Add a new PyFrame_FastToLocalsWithError() function to handle

exceptions when merging fast locals into f_locals of a frame.
PyEval_GetLocals() now raises an exception and return NULL on failure.
This commit is contained in:
Victor Stinner 2013-10-29 01:19:37 +01:00
parent 28c63f7ffb
commit 41bb43a71e
7 changed files with 87 additions and 46 deletions

View file

@ -21,7 +21,8 @@ static PyMemberDef frame_memberlist[] = {
static PyObject *
frame_getlocals(PyFrameObject *f, void *closure)
{
PyFrame_FastToLocals(f);
if (PyFrame_FastToLocalsWithError(f) < 0)
return NULL;
Py_INCREF(f->f_locals);
return f->f_locals;
}
@ -772,12 +773,9 @@ PyFrame_BlockPop(PyFrameObject *f)
If deref is true, then the values being copied are cell variables
and the value is extracted from the cell variable before being put
in dict.
Exceptions raised while modifying the dict are silently ignored,
because there is no good way to report them.
*/
static void
static int
map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values,
int deref)
{
@ -794,14 +792,19 @@ map_to_dict(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values,
value = PyCell_GET(value);
}
if (value == NULL) {
if (PyObject_DelItem(dict, key) != 0)
PyErr_Clear();
if (PyObject_DelItem(dict, key) != 0) {
if (PyErr_ExceptionMatches(PyExc_KeyError))
PyErr_Clear();
else
return -1;
}
}
else {
if (PyObject_SetItem(dict, key, value) != 0)
PyErr_Clear();
return -1;
}
}
return 0;
}
/* Copy values from the "locals" dict into the fast locals.
@ -858,42 +861,49 @@ dict_to_map(PyObject *map, Py_ssize_t nmap, PyObject *dict, PyObject **values,
}
}
void
PyFrame_FastToLocals(PyFrameObject *f)
int
PyFrame_FastToLocalsWithError(PyFrameObject *f)
{
/* Merge fast locals into f->f_locals */
PyObject *locals, *map;
PyObject **fast;
PyObject *error_type, *error_value, *error_traceback;
PyCodeObject *co;
Py_ssize_t j;
Py_ssize_t ncells, nfreevars;
if (f == NULL)
return;
if (f == NULL) {
PyErr_BadInternalCall();
return -1;
}
locals = f->f_locals;
if (locals == NULL) {
locals = f->f_locals = PyDict_New();
if (locals == NULL) {
PyErr_Clear(); /* Can't report it :-( */
return;
}
if (locals == NULL)
return -1;
}
co = f->f_code;
map = co->co_varnames;
if (!PyTuple_Check(map))
return;
PyErr_Fetch(&error_type, &error_value, &error_traceback);
if (!PyTuple_Check(map)) {
PyErr_Format(PyExc_SystemError,
"co_varnames must be a tuple, not %s",
Py_TYPE(map)->tp_name);
return -1;
}
fast = f->f_localsplus;
j = PyTuple_GET_SIZE(map);
if (j > co->co_nlocals)
j = co->co_nlocals;
if (co->co_nlocals)
map_to_dict(map, j, locals, fast, 0);
if (co->co_nlocals) {
if (map_to_dict(map, j, locals, fast, 0) < 0)
return -1;
}
ncells = PyTuple_GET_SIZE(co->co_cellvars);
nfreevars = PyTuple_GET_SIZE(co->co_freevars);
if (ncells || nfreevars) {
map_to_dict(co->co_cellvars, ncells,
locals, fast + co->co_nlocals, 1);
if (map_to_dict(co->co_cellvars, ncells,
locals, fast + co->co_nlocals, 1))
return -1;
/* If the namespace is unoptimized, then one of the
following cases applies:
1. It does not contain free variables, because it
@ -903,11 +913,24 @@ PyFrame_FastToLocals(PyFrameObject *f)
into the locals dict used by the class.
*/
if (co->co_flags & CO_OPTIMIZED) {
map_to_dict(co->co_freevars, nfreevars,
locals, fast + co->co_nlocals + ncells, 1);
if (map_to_dict(co->co_freevars, nfreevars,
locals, fast + co->co_nlocals + ncells, 1) < 0)
return -1;
}
}
PyErr_Restore(error_type, error_value, error_traceback);
return 0;
}
void
PyFrame_FastToLocals(PyFrameObject *f)
{
int res;
assert(!PyErr_Occurred());
res = PyFrame_FastToLocalsWithError(f);
if (res < 0)
PyErr_Clear();
}
void