Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 104d7b7

Browse files
authored
gh-105340: include hidden fast-locals in locals() (#105715)
* gh-105340: include hidden fast-locals in locals()
1 parent 838406b commit 104d7b7

File tree

8 files changed

+158
-42
lines changed

8 files changed

+158
-42
lines changed

Include/internal/pycore_ceval.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ extern PyObject* _Py_MakeCoro(PyFunctionObject *func);
156156
and asynchronous exception */
157157
extern int _Py_HandlePending(PyThreadState *tstate);
158158

159+
extern PyObject * _PyEval_GetFrameLocals(void);
159160

160161

161162
#ifdef __cplusplus

Include/internal/pycore_frame.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame * frame);
234234
int
235235
_PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg);
236236

237+
PyObject *
238+
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden);
239+
237240
int
238241
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame);
239242

Lib/test/test_listcomps.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,28 @@ def b():
539539
self._check_in_scopes(code, {"x": True, "y": ["b"]}, scopes=["function"])
540540
self._check_in_scopes(code, raises=NameError, scopes=["class"])
541541

542+
def test_iter_var_available_in_locals(self):
543+
code = """
544+
l = [1, 2]
545+
y = 0
546+
items = [locals()["x"] for x in l]
547+
items2 = [vars()["x"] for x in l]
548+
items3 = [("x" in dir()) for x in l]
549+
items4 = [eval("x") for x in l]
550+
# x is available, and does not overwrite y
551+
[exec("y = x") for x in l]
552+
"""
553+
self._check_in_scopes(
554+
code,
555+
{
556+
"items": [1, 2],
557+
"items2": [1, 2],
558+
"items3": [True, True],
559+
"items4": [1, 2],
560+
"y": 0
561+
}
562+
)
563+
542564

543565
__test__ = {'doctests' : doctests}
544566

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Include the comprehension iteration variable in ``locals()`` inside a
2+
module- or class-scope comprehension.

Objects/frameobject.c

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1200,15 +1200,28 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
12001200
return 1;
12011201
}
12021202

1203-
int
1204-
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
1203+
1204+
PyObject *
1205+
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden)
12051206
{
12061207
/* Merge fast locals into f->f_locals */
12071208
PyObject *locals = frame->f_locals;
12081209
if (locals == NULL) {
12091210
locals = frame->f_locals = PyDict_New();
12101211
if (locals == NULL) {
1211-
return -1;
1212+
return NULL;
1213+
}
1214+
}
1215+
PyObject *hidden = NULL;
1216+
1217+
/* If include_hidden, "hidden" fast locals (from inlined comprehensions in
1218+
module/class scopes) will be included in the returned dict, but not in
1219+
frame->f_locals; the returned dict will be a modified copy. Non-hidden
1220+
locals will still be updated in frame->f_locals. */
1221+
if (include_hidden) {
1222+
hidden = PyDict_New();
1223+
if (hidden == NULL) {
1224+
return NULL;
12121225
}
12131226
}
12141227

@@ -1224,6 +1237,11 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
12241237
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
12251238
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
12261239
if (kind & CO_FAST_HIDDEN) {
1240+
if (include_hidden && value != NULL) {
1241+
if (PyObject_SetItem(hidden, name, value) != 0) {
1242+
goto error;
1243+
}
1244+
}
12271245
continue;
12281246
}
12291247
if (value == NULL) {
@@ -1232,16 +1250,53 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
12321250
PyErr_Clear();
12331251
}
12341252
else {
1235-
return -1;
1253+
goto error;
12361254
}
12371255
}
12381256
}
12391257
else {
12401258
if (PyObject_SetItem(locals, name, value) != 0) {
1241-
return -1;
1259+
goto error;
12421260
}
12431261
}
12441262
}
1263+
1264+
if (include_hidden && PyDict_Size(hidden)) {
1265+
PyObject *innerlocals = PyDict_New();
1266+
if (innerlocals == NULL) {
1267+
goto error;
1268+
}
1269+
if (PyDict_Merge(innerlocals, locals, 1) != 0) {
1270+
Py_DECREF(innerlocals);
1271+
goto error;
1272+
}
1273+
if (PyDict_Merge(innerlocals, hidden, 1) != 0) {
1274+
Py_DECREF(innerlocals);
1275+
goto error;
1276+
}
1277+
locals = innerlocals;
1278+
}
1279+
else {
1280+
Py_INCREF(locals);
1281+
}
1282+
Py_CLEAR(hidden);
1283+
1284+
return locals;
1285+
1286+
error:
1287+
Py_XDECREF(hidden);
1288+
return NULL;
1289+
}
1290+
1291+
1292+
int
1293+
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
1294+
{
1295+
PyObject *locals = _PyFrame_GetLocals(frame, 0);
1296+
if (locals == NULL) {
1297+
return -1;
1298+
}
1299+
Py_DECREF(locals);
12451300
return 0;
12461301
}
12471302

Objects/object.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1690,13 +1690,15 @@ _dir_locals(void)
16901690
PyObject *names;
16911691
PyObject *locals;
16921692

1693-
locals = PyEval_GetLocals();
1693+
locals = _PyEval_GetFrameLocals();
16941694
if (locals == NULL)
16951695
return NULL;
16961696

16971697
names = PyMapping_Keys(locals);
1698-
if (!names)
1698+
Py_DECREF(locals);
1699+
if (!names) {
16991700
return NULL;
1701+
}
17001702
if (!PyList_Check(names)) {
17011703
PyErr_Format(PyExc_TypeError,
17021704
"dir(): expected keys() of locals to be a list, "
@@ -1708,7 +1710,6 @@ _dir_locals(void)
17081710
Py_DECREF(names);
17091711
return NULL;
17101712
}
1711-
/* the locals don't need to be DECREF'd */
17121713
return names;
17131714
}
17141715

Python/bltinmodule.c

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,7 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
908908
PyObject *locals)
909909
/*[clinic end generated code: output=0a0824aa70093116 input=11ee718a8640e527]*/
910910
{
911-
PyObject *result, *source_copy;
911+
PyObject *result = NULL, *source_copy;
912912
const char *str;
913913

914914
if (locals != Py_None && !PyMapping_Check(locals)) {
@@ -924,54 +924,64 @@ builtin_eval_impl(PyObject *module, PyObject *source, PyObject *globals,
924924
if (globals == Py_None) {
925925
globals = PyEval_GetGlobals();
926926
if (locals == Py_None) {
927-
locals = PyEval_GetLocals();
927+
locals = _PyEval_GetFrameLocals();
928928
if (locals == NULL)
929929
return NULL;
930930
}
931+
else {
932+
Py_INCREF(locals);
933+
}
931934
}
932935
else if (locals == Py_None)
933-
locals = globals;
936+
locals = Py_NewRef(globals);
937+
else {
938+
Py_INCREF(locals);
939+
}
934940

935941
if (globals == NULL || locals == NULL) {
936942
PyErr_SetString(PyExc_TypeError,
937943
"eval must be given globals and locals "
938944
"when called without a frame");
939-
return NULL;
945+
goto error;
940946
}
941947

942948
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
943949
if (r == 0) {
944950
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
945951
}
946952
if (r < 0) {
947-
return NULL;
953+
goto error;
948954
}
949955

950956
if (PyCode_Check(source)) {
951957
if (PySys_Audit("exec", "O", source) < 0) {
952-
return NULL;
958+
goto error;
953959
}
954960

955961
if (PyCode_GetNumFree((PyCodeObject *)source) > 0) {
956962
PyErr_SetString(PyExc_TypeError,
957963
"code object passed to eval() may not contain free variables");
958-
return NULL;
964+
goto error;
959965
}
960-
return PyEval_EvalCode(source, globals, locals);
966+
result = PyEval_EvalCode(source, globals, locals);
961967
}
968+
else {
969+
PyCompilerFlags cf = _PyCompilerFlags_INIT;
970+
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
971+
str = _Py_SourceAsString(source, "eval", "string, bytes or code", &cf, &source_copy);
972+
if (str == NULL)
973+
goto error;
962974

963-
PyCompilerFlags cf = _PyCompilerFlags_INIT;
964-
cf.cf_flags = PyCF_SOURCE_IS_UTF8;
965-
str = _Py_SourceAsString(source, "eval", "string, bytes or code", &cf, &source_copy);
966-
if (str == NULL)
967-
return NULL;
975+
while (*str == ' ' || *str == '\t')
976+
str++;
968977

969-
while (*str == ' ' || *str == '\t')
970-
str++;
978+
(void)PyEval_MergeCompilerFlags(&cf);
979+
result = PyRun_StringFlags(str, Py_eval_input, globals, locals, &cf);
980+
Py_XDECREF(source_copy);
981+
}
971982

972-
(void)PyEval_MergeCompilerFlags(&cf);
973-
result = PyRun_StringFlags(str, Py_eval_input, globals, locals, &cf);
974-
Py_XDECREF(source_copy);
983+
error:
984+
Py_XDECREF(locals);
975985
return result;
976986
}
977987

@@ -1006,36 +1016,43 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
10061016
if (globals == Py_None) {
10071017
globals = PyEval_GetGlobals();
10081018
if (locals == Py_None) {
1009-
locals = PyEval_GetLocals();
1019+
locals = _PyEval_GetFrameLocals();
10101020
if (locals == NULL)
10111021
return NULL;
10121022
}
1023+
else {
1024+
Py_INCREF(locals);
1025+
}
10131026
if (!globals || !locals) {
10141027
PyErr_SetString(PyExc_SystemError,
10151028
"globals and locals cannot be NULL");
10161029
return NULL;
10171030
}
10181031
}
1019-
else if (locals == Py_None)
1020-
locals = globals;
1032+
else if (locals == Py_None) {
1033+
locals = Py_NewRef(globals);
1034+
}
1035+
else {
1036+
Py_INCREF(locals);
1037+
}
10211038

10221039
if (!PyDict_Check(globals)) {
10231040
PyErr_Format(PyExc_TypeError, "exec() globals must be a dict, not %.100s",
10241041
Py_TYPE(globals)->tp_name);
1025-
return NULL;
1042+
goto error;
10261043
}
10271044
if (!PyMapping_Check(locals)) {
10281045
PyErr_Format(PyExc_TypeError,
10291046
"locals must be a mapping or None, not %.100s",
10301047
Py_TYPE(locals)->tp_name);
1031-
return NULL;
1048+
goto error;
10321049
}
10331050
int r = PyDict_Contains(globals, &_Py_ID(__builtins__));
10341051
if (r == 0) {
10351052
r = PyDict_SetItem(globals, &_Py_ID(__builtins__), PyEval_GetBuiltins());
10361053
}
10371054
if (r < 0) {
1038-
return NULL;
1055+
goto error;
10391056
}
10401057

10411058
if (closure == Py_None) {
@@ -1048,7 +1065,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
10481065
if (closure) {
10491066
PyErr_SetString(PyExc_TypeError,
10501067
"cannot use a closure with this code object");
1051-
return NULL;
1068+
goto error;
10521069
}
10531070
} else {
10541071
int closure_is_ok =
@@ -1068,12 +1085,12 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
10681085
PyErr_Format(PyExc_TypeError,
10691086
"code object requires a closure of exactly length %zd",
10701087
num_free);
1071-
return NULL;
1088+
goto error;
10721089
}
10731090
}
10741091

10751092
if (PySys_Audit("exec", "O", source) < 0) {
1076-
return NULL;
1093+
goto error;
10771094
}
10781095

10791096
if (!closure) {
@@ -1100,7 +1117,7 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
11001117
"string, bytes or code", &cf,
11011118
&source_copy);
11021119
if (str == NULL)
1103-
return NULL;
1120+
goto error;
11041121
if (PyEval_MergeCompilerFlags(&cf))
11051122
v = PyRun_StringFlags(str, Py_file_input, globals,
11061123
locals, &cf);
@@ -1109,9 +1126,14 @@ builtin_exec_impl(PyObject *module, PyObject *source, PyObject *globals,
11091126
Py_XDECREF(source_copy);
11101127
}
11111128
if (v == NULL)
1112-
return NULL;
1129+
goto error;
1130+
Py_DECREF(locals);
11131131
Py_DECREF(v);
11141132
Py_RETURN_NONE;
1133+
1134+
error:
1135+
Py_XDECREF(locals);
1136+
return NULL;
11151137
}
11161138

11171139

@@ -1721,10 +1743,7 @@ static PyObject *
17211743
builtin_locals_impl(PyObject *module)
17221744
/*[clinic end generated code: output=b46c94015ce11448 input=7874018d478d5c4b]*/
17231745
{
1724-
PyObject *d;
1725-
1726-
d = PyEval_GetLocals();
1727-
return Py_XNewRef(d);
1746+
return _PyEval_GetFrameLocals();
17281747
}
17291748

17301749

@@ -2442,7 +2461,7 @@ builtin_vars_impl(PyObject *module, PyObject *object)
24422461
PyObject *d;
24432462

24442463
if (object == NULL) {
2445-
d = Py_XNewRef(PyEval_GetLocals());
2464+
d = _PyEval_GetFrameLocals();
24462465
}
24472466
else {
24482467
if (_PyObject_LookupAttr(object, &_Py_ID(__dict__), &d) == 0) {

0 commit comments

Comments
 (0)