mirror of
https://github.com/python/cpython.git
synced 2025-08-03 16:39:00 +00:00
[3.12] gh-105340: include hidden fast-locals in locals() (GH-105715) (#106470)
gh-105340: include hidden fast-locals in locals() (GH-105715)
* gh-105340: include hidden fast-locals in locals()
(cherry picked from commit 104d7b760f
)
Co-authored-by: Carl Meyer <carl@oddbird.net>
This commit is contained in:
parent
a49a29f22b
commit
bb17e6f5de
8 changed files with 160 additions and 44 deletions
|
@ -154,6 +154,7 @@ extern PyObject* _Py_MakeCoro(PyFunctionObject *func);
|
||||||
|
|
||||||
extern int _Py_HandlePending(PyThreadState *tstate);
|
extern int _Py_HandlePending(PyThreadState *tstate);
|
||||||
|
|
||||||
|
extern PyObject * _PyEval_GetFrameLocals(void);
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
|
@ -226,6 +226,9 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame * frame);
|
||||||
int
|
int
|
||||||
_PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg);
|
_PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg);
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden);
|
||||||
|
|
||||||
int
|
int
|
||||||
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame);
|
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame);
|
||||||
|
|
||||||
|
|
|
@ -539,6 +539,28 @@ class ListComprehensionTest(unittest.TestCase):
|
||||||
self._check_in_scopes(code, {"x": True, "y": ["b"]}, scopes=["function"])
|
self._check_in_scopes(code, {"x": True, "y": ["b"]}, scopes=["function"])
|
||||||
self._check_in_scopes(code, raises=NameError, scopes=["class"])
|
self._check_in_scopes(code, raises=NameError, scopes=["class"])
|
||||||
|
|
||||||
|
def test_iter_var_available_in_locals(self):
|
||||||
|
code = """
|
||||||
|
l = [1, 2]
|
||||||
|
y = 0
|
||||||
|
items = [locals()["x"] for x in l]
|
||||||
|
items2 = [vars()["x"] for x in l]
|
||||||
|
items3 = [("x" in dir()) for x in l]
|
||||||
|
items4 = [eval("x") for x in l]
|
||||||
|
# x is available, and does not overwrite y
|
||||||
|
[exec("y = x") for x in l]
|
||||||
|
"""
|
||||||
|
self._check_in_scopes(
|
||||||
|
code,
|
||||||
|
{
|
||||||
|
"items": [1, 2],
|
||||||
|
"items2": [1, 2],
|
||||||
|
"items3": [True, True],
|
||||||
|
"items4": [1, 2],
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
__test__ = {'doctests' : doctests}
|
__test__ = {'doctests' : doctests}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Include the comprehension iteration variable in ``locals()`` inside a
|
||||||
|
module- or class-scope comprehension.
|
|
@ -1199,15 +1199,28 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
|
PyObject *
|
||||||
|
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden)
|
||||||
{
|
{
|
||||||
/* Merge fast locals into f->f_locals */
|
/* Merge fast locals into f->f_locals */
|
||||||
PyObject *locals = frame->f_locals;
|
PyObject *locals = frame->f_locals;
|
||||||
if (locals == NULL) {
|
if (locals == NULL) {
|
||||||
locals = frame->f_locals = PyDict_New();
|
locals = frame->f_locals = PyDict_New();
|
||||||
if (locals == NULL) {
|
if (locals == NULL) {
|
||||||
return -1;
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PyObject *hidden = NULL;
|
||||||
|
|
||||||
|
/* If include_hidden, "hidden" fast locals (from inlined comprehensions in
|
||||||
|
module/class scopes) will be included in the returned dict, but not in
|
||||||
|
frame->f_locals; the returned dict will be a modified copy. Non-hidden
|
||||||
|
locals will still be updated in frame->f_locals. */
|
||||||
|
if (include_hidden) {
|
||||||
|
hidden = PyDict_New();
|
||||||
|
if (hidden == NULL) {
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,6 +1236,11 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
|
||||||
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
|
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
|
||||||
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
|
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
|
||||||
if (kind & CO_FAST_HIDDEN) {
|
if (kind & CO_FAST_HIDDEN) {
|
||||||
|
if (include_hidden && value != NULL) {
|
||||||
|
if (PyObject_SetItem(hidden, name, value) != 0) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
|
@ -1231,16 +1249,53 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (PyObject_SetItem(locals, name, value) != 0) {
|
if (PyObject_SetItem(locals, name, value) != 0) {
|
||||||
return -1;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (include_hidden && PyDict_Size(hidden)) {
|
||||||
|
PyObject *innerlocals = PyDict_New();
|
||||||
|
if (innerlocals == NULL) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyDict_Merge(innerlocals, locals, 1) != 0) {
|
||||||
|
Py_DECREF(innerlocals);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (PyDict_Merge(innerlocals, hidden, 1) != 0) {
|
||||||
|
Py_DECREF(innerlocals);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
locals = innerlocals;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_INCREF(locals);
|
||||||
|
}
|
||||||
|
Py_CLEAR(hidden);
|
||||||
|
|
||||||
|
return locals;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(hidden);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
|
||||||
|
{
|
||||||
|
PyObject *locals = _PyFrame_GetLocals(frame, 0);
|
||||||
|
if (locals == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Py_DECREF(locals);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1704,13 +1704,15 @@ _dir_locals(void)
|
||||||
PyObject *names;
|
PyObject *names;
|
||||||
PyObject *locals;
|
PyObject *locals;
|
||||||
|
|
||||||
locals = PyEval_GetLocals();
|
locals = _PyEval_GetFrameLocals();
|
||||||
if (locals == NULL)
|
if (locals == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
names = PyMapping_Keys(locals);
|
names = PyMapping_Keys(locals);
|
||||||
if (!names)
|
Py_DECREF(locals);
|
||||||
|
if (!names) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
if (!PyList_Check(names)) {
|
if (!PyList_Check(names)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"dir(): expected keys() of locals to be a list, "
|
"dir(): expected keys() of locals to be a list, "
|
||||||
|
@ -1722,7 +1724,6 @@ _dir_locals(void)
|
||||||
Py_DECREF(names);
|
Py_DECREF(names);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* the locals don't need to be DECREF'd */
|
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -907,7 +907,7 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
PyObject *locals)
|
PyObject *locals)
|
||||||
/*[clinic end generated code: output=0a0824aa70093116 input=11ee718a8640e527]*/
|
/*[clinic end generated code: output=0a0824aa70093116 input=11ee718a8640e527]*/
|
||||||
{
|
{
|
||||||
PyObject *result, *source_copy;
|
PyObject *result = NULL, *source_copy;
|
||||||
const char *str;
|
const char *str;
|
||||||
|
|
||||||
if (locals != Py_None && !PyMapping_Check(locals)) {
|
if (locals != Py_None && !PyMapping_Check(locals)) {
|
||||||
|
@ -923,19 +923,25 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
if (globals == Py_None) {
|
if (globals == Py_None) {
|
||||||
globals = PyEval_GetGlobals();
|
globals = PyEval_GetGlobals();
|
||||||
if (locals == Py_None) {
|
if (locals == Py_None) {
|
||||||
locals = PyEval_GetLocals();
|
locals = _PyEval_GetFrameLocals();
|
||||||
if (locals == NULL)
|
if (locals == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Py_INCREF(locals);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (locals == Py_None)
|
else if (locals == Py_None)
|
||||||
locals = globals;
|
locals = Py_NewRef(globals);
|
||||||
|
else {
|
||||||
|
Py_INCREF(locals);
|
||||||
|
}
|
||||||
|
|
||||||
if (globals == NULL || locals == NULL) {
|
if (globals == NULL || locals == NULL) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"eval must be given globals and locals "
|
"eval must be given globals and locals "
|
||||||
"when called without a frame");
|
"when called without a frame");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
|
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
|
||||||
|
@ -943,34 +949,38 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
|
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
|
||||||
}
|
}
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyCode_Check(source)) {
|
if (PyCode_Check(source)) {
|
||||||
if (PySys_Audit("exec", "O", source) < 0) {
|
if (PySys_Audit("exec", "O", source) < 0) {
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PyCode_GetNumFree((PyCodeObject *)source) > 0) {
|
if (PyCode_GetNumFree((PyCodeObject *)source) > 0) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"code object passed to eval() may not contain free variables");
|
"code object passed to eval() may not contain free variables");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
return PyEval_EvalCode(source, globals, locals);
|
result = PyEval_EvalCode(source, globals, locals);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
PyCompilerFlags cf = _PyCompilerFlags_INIT;
|
||||||
|
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
||||||
|
str = _Py_SourceAsString(source, "eval", "string, bytes or code", &cf, &source_copy);
|
||||||
|
if (str == NULL)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
while (*str == ' ' || *str == '\t')
|
||||||
|
str++;
|
||||||
|
|
||||||
|
(void)PyEval_MergeCompilerFlags(&cf);
|
||||||
|
result = PyRun_StringFlags(str, Py_eval_input, globals, locals, &cf);
|
||||||
|
Py_XDECREF(source_copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyCompilerFlags cf = _PyCompilerFlags_INIT;
|
error:
|
||||||
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
|
Py_XDECREF(locals);
|
||||||
str = _Py_SourceAsString(source, "eval", "string, bytes or code", &cf, &source_copy);
|
|
||||||
if (str == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
while (*str == ' ' || *str == '\t')
|
|
||||||
str++;
|
|
||||||
|
|
||||||
(void)PyEval_MergeCompilerFlags(&cf);
|
|
||||||
result = PyRun_StringFlags(str, Py_eval_input, globals, locals, &cf);
|
|
||||||
Py_XDECREF(source_copy);
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1005,36 +1015,43 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
if (globals == Py_None) {
|
if (globals == Py_None) {
|
||||||
globals = PyEval_GetGlobals();
|
globals = PyEval_GetGlobals();
|
||||||
if (locals == Py_None) {
|
if (locals == Py_None) {
|
||||||
locals = PyEval_GetLocals();
|
locals = _PyEval_GetFrameLocals();
|
||||||
if (locals == NULL)
|
if (locals == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Py_INCREF(locals);
|
||||||
|
}
|
||||||
if (!globals || !locals) {
|
if (!globals || !locals) {
|
||||||
PyErr_SetString(PyExc_SystemError,
|
PyErr_SetString(PyExc_SystemError,
|
||||||
"globals and locals cannot be NULL");
|
"globals and locals cannot be NULL");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (locals == Py_None)
|
else if (locals == Py_None) {
|
||||||
locals = globals;
|
locals = Py_NewRef(globals);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_INCREF(locals);
|
||||||
|
}
|
||||||
|
|
||||||
if (!PyDict_Check(globals)) {
|
if (!PyDict_Check(globals)) {
|
||||||
PyErr_Format(PyExc_TypeError, "exec() globals must be a dict, not %.100s",
|
PyErr_Format(PyExc_TypeError, "exec() globals must be a dict, not %.100s",
|
||||||
Py_TYPE(globals)->tp_name);
|
Py_TYPE(globals)->tp_name);
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
if (!PyMapping_Check(locals)) {
|
if (!PyMapping_Check(locals)) {
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"locals must be a mapping or None, not %.100s",
|
"locals must be a mapping or None, not %.100s",
|
||||||
Py_TYPE(locals)->tp_name);
|
Py_TYPE(locals)->tp_name);
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
|
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
|
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
|
||||||
}
|
}
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (closure == Py_None) {
|
if (closure == Py_None) {
|
||||||
|
@ -1047,7 +1064,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
if (closure) {
|
if (closure) {
|
||||||
PyErr_SetString(PyExc_TypeError,
|
PyErr_SetString(PyExc_TypeError,
|
||||||
"cannot use a closure with this code object");
|
"cannot use a closure with this code object");
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
int closure_is_ok =
|
int closure_is_ok =
|
||||||
|
@ -1067,12 +1084,12 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
PyErr_Format(PyExc_TypeError,
|
PyErr_Format(PyExc_TypeError,
|
||||||
"code object requires a closure of exactly length %zd",
|
"code object requires a closure of exactly length %zd",
|
||||||
num_free);
|
num_free);
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PySys_Audit("exec", "O", source) < 0) {
|
if (PySys_Audit("exec", "O", source) < 0) {
|
||||||
return NULL;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!closure) {
|
if (!closure) {
|
||||||
|
@ -1099,7 +1116,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
"string, bytes or code", &cf,
|
"string, bytes or code", &cf,
|
||||||
&source_copy);
|
&source_copy);
|
||||||
if (str == NULL)
|
if (str == NULL)
|
||||||
return NULL;
|
goto error;
|
||||||
if (PyEval_MergeCompilerFlags(&cf))
|
if (PyEval_MergeCompilerFlags(&cf))
|
||||||
v = PyRun_StringFlags(str, Py_file_input, globals,
|
v = PyRun_StringFlags(str, Py_file_input, globals,
|
||||||
locals, &cf);
|
locals, &cf);
|
||||||
|
@ -1108,9 +1125,14 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
|
||||||
Py_XDECREF(source_copy);
|
Py_XDECREF(source_copy);
|
||||||
}
|
}
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
return NULL;
|
goto error;
|
||||||
|
Py_DECREF(locals);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(locals);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1720,10 +1742,7 @@ static PyObject *
|
||||||
builtin_locals_impl(PyObject *module)
|
builtin_locals_impl(PyObject *module)
|
||||||
/*[clinic end generated code: output=b46c94015ce11448 input=7874018d478d5c4b]*/
|
/*[clinic end generated code: output=b46c94015ce11448 input=7874018d478d5c4b]*/
|
||||||
{
|
{
|
||||||
PyObject *d;
|
return _PyEval_GetFrameLocals();
|
||||||
|
|
||||||
d = PyEval_GetLocals();
|
|
||||||
return Py_XNewRef(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2443,7 +2462,7 @@ builtin_vars_impl(PyObject *module, PyObject *object)
|
||||||
PyObject *d;
|
PyObject *d;
|
||||||
|
|
||||||
if (object == NULL) {
|
if (object == NULL) {
|
||||||
d = Py_XNewRef(PyEval_GetLocals());
|
d = _PyEval_GetFrameLocals();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (_PyObject_LookupAttr(object, &_Py_ID(__dict__), &d) == 0) {
|
if (_PyObject_LookupAttr(object, &_Py_ID(__dict__), &d) == 0) {
|
||||||
|
|
|
@ -2312,6 +2312,19 @@ PyEval_GetLocals(void)
|
||||||
return locals;
|
return locals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
_PyEval_GetFrameLocals(void)
|
||||||
|
{
|
||||||
|
PyThreadState *tstate = _PyThreadState_GET();
|
||||||
|
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
|
||||||
|
if (current_frame == NULL) {
|
||||||
|
_PyErr_SetString(tstate, PyExc_SystemError, "frame does not exist");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _PyFrame_GetLocals(current_frame, 1);
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
PyEval_GetGlobals(void)
|
PyEval_GetGlobals(void)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue