gh-74929: Implement PEP 667 (GH-115153)

This commit is contained in:
Tian Gao 2024-05-04 04:12:10 -07:00 committed by GitHub
parent 1ab6356ebe
commit b034f14a4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 921 additions and 257 deletions

View File

@ -188,6 +188,9 @@ function,PyEval_EvalFrame,3.2,,
function,PyEval_EvalFrameEx,3.2,, function,PyEval_EvalFrameEx,3.2,,
function,PyEval_GetBuiltins,3.2,, function,PyEval_GetBuiltins,3.2,,
function,PyEval_GetFrame,3.2,, function,PyEval_GetFrame,3.2,,
function,PyEval_GetFrameBuiltins,3.13,,
function,PyEval_GetFrameGlobals,3.13,,
function,PyEval_GetFrameLocals,3.13,,
function,PyEval_GetFuncDesc,3.2,, function,PyEval_GetFuncDesc,3.2,,
function,PyEval_GetFuncName,3.2,, function,PyEval_GetFuncName,3.2,,
function,PyEval_GetGlobals,3.2,, function,PyEval_GetGlobals,3.2,,

View File

@ -22,6 +22,10 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void);
PyAPI_FUNC(PyObject *) PyEval_GetLocals(void); PyAPI_FUNC(PyObject *) PyEval_GetLocals(void);
PyAPI_FUNC(PyFrameObject *) PyEval_GetFrame(void); PyAPI_FUNC(PyFrameObject *) PyEval_GetFrame(void);
PyAPI_FUNC(PyObject *) PyEval_GetFrameBuiltins(void);
PyAPI_FUNC(PyObject *) PyEval_GetFrameGlobals(void);
PyAPI_FUNC(PyObject *) PyEval_GetFrameLocals(void);
PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg); PyAPI_FUNC(int) Py_AddPendingCall(int (*func)(void *), void *arg);
PyAPI_FUNC(int) Py_MakePendingCalls(void); PyAPI_FUNC(int) Py_MakePendingCalls(void);

View File

@ -27,3 +27,9 @@ PyAPI_FUNC(int) _PyFrame_IsEntryFrame(PyFrameObject *frame);
PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f); PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f);
PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
typedef struct {
PyObject_HEAD
PyFrameObject* frame;
} PyFrameLocalsProxyObject;

View File

@ -3,8 +3,10 @@
#endif #endif
PyAPI_DATA(PyTypeObject) PyFrame_Type; PyAPI_DATA(PyTypeObject) PyFrame_Type;
PyAPI_DATA(PyTypeObject) PyFrameLocalsProxy_Type;
#define PyFrame_Check(op) Py_IS_TYPE((op), &PyFrame_Type) #define PyFrame_Check(op) Py_IS_TYPE((op), &PyFrame_Type)
#define PyFrameLocalsProxy_Check(op) Py_IS_TYPE((op), &PyFrameLocalsProxy_Type)
PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame); PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame);

View File

@ -25,7 +25,7 @@ struct _frame {
int f_lineno; /* Current line number. Only valid if non-zero */ int f_lineno; /* Current line number. Only valid if non-zero */
char f_trace_lines; /* Emit per-line trace events? */ char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */
char f_fast_as_locals; /* Have the fast locals of this frame been converted to a dict? */ PyObject *f_extra_locals; /* Dict for locals set by users using f_locals, could be NULL */
/* The frame data, if this frame object owns the frame */ /* The frame data, if this frame object owns the frame */
PyObject *_f_frame_data[1]; PyObject *_f_frame_data[1];
}; };
@ -245,14 +245,11 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame * frame);
int int
_PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg); _PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg);
bool
_PyFrame_HasHiddenLocals(_PyInterpreterFrame *frame);
PyObject * PyObject *
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden); _PyFrame_GetLocals(_PyInterpreterFrame *frame);
int
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame);
void
_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear);
static inline bool static inline bool
_PyThreadState_HasStackSpace(PyThreadState *tstate, int size) _PyThreadState_HasStackSpace(PyThreadState *tstate, int size)

View File

@ -1,3 +1,4 @@
import copy
import gc import gc
import operator import operator
import re import re
@ -13,7 +14,7 @@ except ImportError:
_testcapi = None _testcapi = None
from test import support from test import support
from test.support import threading_helper, Py_GIL_DISABLED from test.support import import_helper, threading_helper, Py_GIL_DISABLED
from test.support.script_helper import assert_python_ok from test.support.script_helper import assert_python_ok
@ -198,14 +199,6 @@ class FrameAttrsTest(unittest.TestCase):
tb = tb.tb_next tb = tb.tb_next
return frames return frames
def test_locals(self):
f, outer, inner = self.make_frames()
outer_locals = outer.f_locals
self.assertIsInstance(outer_locals.pop('inner'), types.FunctionType)
self.assertEqual(outer_locals, {'x': 5, 'y': 6})
inner_locals = inner.f_locals
self.assertEqual(inner_locals, {'x': 5, 'z': 7})
def test_clear_locals(self): def test_clear_locals(self):
# Test f_locals after clear() (issue #21897) # Test f_locals after clear() (issue #21897)
f, outer, inner = self.make_frames() f, outer, inner = self.make_frames()
@ -217,8 +210,8 @@ class FrameAttrsTest(unittest.TestCase):
def test_locals_clear_locals(self): def test_locals_clear_locals(self):
# Test f_locals before and after clear() (to exercise caching) # Test f_locals before and after clear() (to exercise caching)
f, outer, inner = self.make_frames() f, outer, inner = self.make_frames()
outer.f_locals self.assertNotEqual(outer.f_locals, {})
inner.f_locals self.assertNotEqual(inner.f_locals, {})
outer.clear() outer.clear()
inner.clear() inner.clear()
self.assertEqual(outer.f_locals, {}) self.assertEqual(outer.f_locals, {})
@ -269,6 +262,177 @@ class ReprTest(unittest.TestCase):
r"^<frame at 0x[0-9a-fA-F]+, file %s, line %d, code inner>$" r"^<frame at 0x[0-9a-fA-F]+, file %s, line %d, code inner>$"
% (file_repr, offset + 5)) % (file_repr, offset + 5))
class TestFrameLocals(unittest.TestCase):
def test_scope(self):
class A:
x = 1
sys._getframe().f_locals['x'] = 2
sys._getframe().f_locals['y'] = 2
self.assertEqual(A.x, 2)
self.assertEqual(A.y, 2)
def f():
x = 1
sys._getframe().f_locals['x'] = 2
sys._getframe().f_locals['y'] = 2
self.assertEqual(x, 2)
self.assertEqual(locals()['y'], 2)
f()
def test_closure(self):
x = 1
y = 2
def f():
z = x + y
d = sys._getframe().f_locals
self.assertEqual(d['x'], 1)
self.assertEqual(d['y'], 2)
d['x'] = 2
d['y'] = 3
f()
self.assertEqual(x, 2)
self.assertEqual(y, 3)
def test_as_dict(self):
x = 1
y = 2
d = sys._getframe().f_locals
# self, x, y, d
self.assertEqual(len(d), 4)
self.assertIs(d['d'], d)
self.assertEqual(set(d.keys()), set(['x', 'y', 'd', 'self']))
self.assertEqual(len(d.values()), 4)
self.assertIn(1, d.values())
self.assertEqual(len(d.items()), 4)
self.assertIn(('x', 1), d.items())
self.assertEqual(d.__getitem__('x'), 1)
d.__setitem__('x', 2)
self.assertEqual(d['x'], 2)
self.assertEqual(d.get('x'), 2)
self.assertIs(d.get('non_exist', None), None)
self.assertEqual(d.__len__(), 4)
self.assertEqual(set([key for key in d]), set(['x', 'y', 'd', 'self']))
self.assertIn('x', d)
self.assertTrue(d.__contains__('x'))
self.assertEqual(reversed(d), list(reversed(d.keys())))
d.update({'x': 3, 'z': 4})
self.assertEqual(d['x'], 3)
self.assertEqual(d['z'], 4)
with self.assertRaises(TypeError):
d.update([1, 2])
self.assertEqual(d.setdefault('x', 5), 3)
self.assertEqual(d.setdefault('new', 5), 5)
self.assertEqual(d['new'], 5)
with self.assertRaises(KeyError):
d['non_exist']
def test_as_number(self):
x = 1
y = 2
d = sys._getframe().f_locals
self.assertIn('z', d | {'z': 3})
d |= {'z': 3}
self.assertEqual(d['z'], 3)
d |= {'y': 3}
self.assertEqual(d['y'], 3)
with self.assertRaises(TypeError):
d |= 3
with self.assertRaises(TypeError):
_ = d | [3]
def test_non_string_key(self):
d = sys._getframe().f_locals
d[1] = 2
self.assertEqual(d[1], 2)
def test_write_with_hidden(self):
def f():
f_locals = [sys._getframe().f_locals for b in [0]][0]
f_locals['b'] = 2
f_locals['c'] = 3
self.assertEqual(b, 2)
self.assertEqual(c, 3)
b = 0
c = 0
f()
def test_repr(self):
x = 1
# Introduce a reference cycle
frame = sys._getframe()
self.assertEqual(repr(frame.f_locals), repr(dict(frame.f_locals)))
def test_delete(self):
x = 1
d = sys._getframe().f_locals
with self.assertRaises(TypeError):
del d['x']
with self.assertRaises(AttributeError):
d.clear()
with self.assertRaises(AttributeError):
d.pop('x')
@support.cpython_only
def test_sizeof(self):
proxy = sys._getframe().f_locals
support.check_sizeof(self, proxy, support.calcobjsize("P"))
def test_unsupport(self):
x = 1
d = sys._getframe().f_locals
with self.assertRaises(AttributeError):
d.copy()
with self.assertRaises(TypeError):
copy.copy(d)
with self.assertRaises(TypeError):
copy.deepcopy(d)
class TestFrameCApi(unittest.TestCase):
def test_basic(self):
x = 1
ctypes = import_helper.import_module('ctypes')
PyEval_GetFrameLocals = ctypes.pythonapi.PyEval_GetFrameLocals
PyEval_GetFrameLocals.restype = ctypes.py_object
frame_locals = PyEval_GetFrameLocals()
self.assertTrue(type(frame_locals), dict)
self.assertEqual(frame_locals['x'], 1)
frame_locals['x'] = 2
self.assertEqual(x, 1)
PyEval_GetFrameGlobals = ctypes.pythonapi.PyEval_GetFrameGlobals
PyEval_GetFrameGlobals.restype = ctypes.py_object
frame_globals = PyEval_GetFrameGlobals()
self.assertTrue(type(frame_globals), dict)
self.assertIs(frame_globals, globals())
PyEval_GetFrameBuiltins = ctypes.pythonapi.PyEval_GetFrameBuiltins
PyEval_GetFrameBuiltins.restype = ctypes.py_object
frame_builtins = PyEval_GetFrameBuiltins()
self.assertEqual(frame_builtins, __builtins__)
PyFrame_GetLocals = ctypes.pythonapi.PyFrame_GetLocals
PyFrame_GetLocals.argtypes = [ctypes.py_object]
PyFrame_GetLocals.restype = ctypes.py_object
frame = sys._getframe()
f_locals = PyFrame_GetLocals(frame)
self.assertTrue(f_locals['x'], 1)
f_locals['x'] = 2
self.assertEqual(x, 2)
class TestIncompleteFrameAreInvisible(unittest.TestCase): class TestIncompleteFrameAreInvisible(unittest.TestCase):
def test_issue95818(self): def test_issue95818(self):

View File

@ -622,9 +622,14 @@ class ListComprehensionTest(unittest.TestCase):
def test_frame_locals(self): def test_frame_locals(self):
code = """ code = """
val = [sys._getframe().f_locals for a in [0]][0]["a"] val = "a" in [sys._getframe().f_locals for a in [0]][0]
""" """
import sys import sys
self._check_in_scopes(code, {"val": False}, ns={"sys": sys})
code = """
val = [sys._getframe().f_locals["a"] for a in [0]][0]
"""
self._check_in_scopes(code, {"val": 0}, ns={"sys": sys}) self._check_in_scopes(code, {"val": 0}, ns={"sys": sys})
def _recursive_replace(self, maybe_code): def _recursive_replace(self, maybe_code):

View File

@ -933,23 +933,6 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
self.assertNotInBytecode(f, "LOAD_FAST_CHECK") self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
return f return f
def test_deleting_local_warns_and_assigns_none(self):
f = self.make_function_with_no_checks()
co_code = f.__code__.co_code
def trace(frame, event, arg):
if event == 'line' and frame.f_lineno == 4:
del frame.f_locals["x"]
sys.settrace(None)
return None
return trace
e = r"assigning None to unbound local 'x'"
with self.assertWarnsRegex(RuntimeWarning, e):
sys.settrace(trace)
f()
self.assertInBytecode(f, "LOAD_FAST")
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
self.assertEqual(f.__code__.co_code, co_code)
def test_modifying_local_does_not_add_check(self): def test_modifying_local_does_not_add_check(self):
f = self.make_function_with_no_checks() f = self.make_function_with_no_checks()
def trace(frame, event, arg): def trace(frame, event, arg):

View File

@ -227,6 +227,9 @@ SYMBOL_NAMES = (
"PyEval_EvalFrameEx", "PyEval_EvalFrameEx",
"PyEval_GetBuiltins", "PyEval_GetBuiltins",
"PyEval_GetFrame", "PyEval_GetFrame",
"PyEval_GetFrameBuiltins",
"PyEval_GetFrameGlobals",
"PyEval_GetFrameLocals",
"PyEval_GetFuncDesc", "PyEval_GetFuncDesc",
"PyEval_GetFuncName", "PyEval_GetFuncName",
"PyEval_GetGlobals", "PyEval_GetGlobals",

View File

@ -1561,7 +1561,7 @@ class SizeofTest(unittest.TestCase):
def func(): def func():
return sys._getframe() return sys._getframe()
x = func() x = func()
check(x, size('3Pi3c7P2ic??2P')) check(x, size('3Pi2cP7P2ic??2P'))
# function # function
def func(): pass def func(): pass
check(func, size('15Pi')) check(func, size('15Pi'))

View File

@ -0,0 +1 @@
Implement PEP 667 - converted ``frame.f_locals`` to a write through proxy

View File

@ -2501,3 +2501,9 @@
added = '3.13' added = '3.13'
[function.PyType_GetModuleByDef] [function.PyType_GetModuleByDef]
added = '3.13' added = '3.13'
[function.PyEval_GetFrameBuiltins]
added = '3.13'
[function.PyEval_GetFrameGlobals]
added = '3.13'
[function.PyEval_GetFrameLocals]
added = '3.13'

View File

@ -16,12 +16,633 @@
#define OFF(x) offsetof(PyFrameObject, x) #define OFF(x) offsetof(PyFrameObject, x)
// Returns borrowed reference or NULL
static PyObject *
framelocalsproxy_getval(_PyInterpreterFrame *frame, PyCodeObject *co, int i)
{
PyObject **fast = _PyFrame_GetLocalsArray(frame);
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
PyObject *value = fast[i];
PyObject *cell = NULL;
if (value == NULL) {
return NULL;
}
if (kind == CO_FAST_FREE || kind & CO_FAST_CELL) {
// The cell was set when the frame was created from
// the function's closure.
assert(PyCell_Check(value));
cell = value;
}
if (cell != NULL) {
value = PyCell_GET(cell);
}
if (value == NULL) {
return NULL;
}
return value;
}
static int
framelocalsproxy_getkeyindex(PyFrameObject *frame, PyObject* key, bool read)
{
/*
* Returns the fast locals index of the key
* - if read == true, returns the index if the value is not NULL
* - if read == false, returns the index if the value is not hidden
*/
assert(PyUnicode_CheckExact(key));
PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
int found_key = false;
// We do 2 loops here because it's highly possible the key is interned
// and we can do a pointer comparison.
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
if (name == key) {
found_key = true;
if (read) {
if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) {
return i;
}
} else {
if (!(_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_HIDDEN)) {
return i;
}
}
}
}
if (!found_key) {
// This is unlikely, but we need to make sure. This means the key
// is not interned.
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
if (_PyUnicode_EQ(name, key)) {
if (read) {
if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) {
return i;
}
} else {
if (!(_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_HIDDEN)) {
return i;
}
}
}
}
}
return -1;
}
static PyObject *
framelocalsproxy_getitem(PyObject *self, PyObject *key)
{
PyFrameObject* frame = ((PyFrameLocalsProxyObject*)self)->frame;
PyCodeObject* co = _PyFrame_GetCode(frame->f_frame);
if (PyUnicode_CheckExact(key)) {
int i = framelocalsproxy_getkeyindex(frame, key, true);
if (i >= 0) {
PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i);
assert(value != NULL);
return Py_NewRef(value);
}
}
// Okay not in the fast locals, try extra locals
PyObject *extra = frame->f_extra_locals;
if (extra != NULL) {
PyObject *value = PyDict_GetItem(extra, key);
if (value != NULL) {
return Py_NewRef(value);
}
}
PyErr_Format(PyExc_KeyError, "local variable '%R' is not defined", key);
return NULL;
}
static int
framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value)
{
/* Merge locals into fast locals */
PyFrameObject* frame = ((PyFrameLocalsProxyObject*)self)->frame;
PyObject** fast = _PyFrame_GetLocalsArray(frame->f_frame);
PyCodeObject* co = _PyFrame_GetCode(frame->f_frame);
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "cannot remove variables from FrameLocalsProxy");
return -1;
}
if (PyUnicode_CheckExact(key)) {
int i = framelocalsproxy_getkeyindex(frame, key, false);
if (i >= 0) {
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
PyObject *oldvalue = fast[i];
PyObject *cell = NULL;
if (kind == CO_FAST_FREE) {
// The cell was set when the frame was created from
// the function's closure.
assert(oldvalue != NULL && PyCell_Check(oldvalue));
cell = oldvalue;
} else if (kind & CO_FAST_CELL && oldvalue != NULL) {
if (PyCell_Check(oldvalue)) {
cell = oldvalue;
}
}
if (cell != NULL) {
oldvalue = PyCell_GET(cell);
if (value != oldvalue) {
PyCell_SET(cell, Py_XNewRef(value));
Py_XDECREF(oldvalue);
}
} else if (value != oldvalue) {
Py_XSETREF(fast[i], Py_NewRef(value));
}
Py_XDECREF(value);
return 0;
}
}
// Okay not in the fast locals, try extra locals
PyObject *extra = frame->f_extra_locals;
if (extra == NULL) {
extra = PyDict_New();
if (extra == NULL) {
return -1;
}
frame->f_extra_locals = extra;
}
assert(PyDict_Check(extra));
return PyDict_SetItem(extra, key, value) < 0;
}
static int
framelocalsproxy_merge(PyObject* self, PyObject* other)
{
if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) {
return -1;
}
PyObject *keys = PyMapping_Keys(other);
PyObject *iter = NULL;
PyObject *key = NULL;
PyObject *value = NULL;
assert(keys != NULL);
iter = PyObject_GetIter(keys);
Py_DECREF(keys);
if (iter == NULL) {
return -1;
}
while ((key = PyIter_Next(iter)) != NULL) {
value = PyObject_GetItem(other, key);
if (value == NULL) {
Py_DECREF(key);
Py_DECREF(iter);
return -1;
}
if (framelocalsproxy_setitem(self, key, value) < 0) {
Py_DECREF(key);
Py_DECREF(value);
Py_DECREF(iter);
return -1;
}
Py_DECREF(key);
Py_DECREF(value);
}
return 0;
}
static PyObject *
framelocalsproxy_keys(PyObject *self, PyObject *__unused)
{
PyObject *names = PyList_New(0);
PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *val = framelocalsproxy_getval(frame->f_frame, co, i);
if (val) {
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
PyList_Append(names, name);
}
}
// Iterate through the extra locals
Py_ssize_t i = 0;
PyObject *key = NULL;
PyObject *value = NULL;
if (frame->f_extra_locals) {
assert(PyDict_Check(frame->f_extra_locals));
while (PyDict_Next(frame->f_extra_locals, &i, &key, &value)) {
PyList_Append(names, key);
}
}
return names;
}
static void
framelocalsproxy_dealloc(PyObject *self)
{
PyObject_GC_UnTrack(self);
Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame);
Py_TYPE(self)->tp_free(self);
}
static PyObject *
framelocalsproxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
PyFrameLocalsProxyObject *self = (PyFrameLocalsProxyObject *)type->tp_alloc(type, 0);
if (self == NULL) {
return NULL;
}
PyFrameObject *frame = (PyFrameObject*)PyTuple_GET_ITEM(args, 0);
assert(PyFrame_Check(frame));
((PyFrameLocalsProxyObject*)self)->frame = (PyFrameObject*)Py_NewRef(frame);
return (PyObject *)self;
}
static int
framelocalsproxy_tp_clear(PyObject *self)
{
Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame);
return 0;
}
static int
framelocalsproxy_visit(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(((PyFrameLocalsProxyObject*)self)->frame);
return 0;
}
static PyObject *
framelocalsproxy_iter(PyObject *self)
{
return PyObject_GetIter(framelocalsproxy_keys(self, NULL));
}
static PyObject *
framelocalsproxy_richcompare(PyObject *self, PyObject *other, int op)
{
if (PyFrameLocalsProxy_Check(other)) {
bool result = ((PyFrameLocalsProxyObject*)self)->frame == ((PyFrameLocalsProxyObject*)other)->frame;
if (op == Py_EQ) {
return PyBool_FromLong(result);
} else if (op == Py_NE) {
return PyBool_FromLong(!result);
}
} else if (PyDict_Check(other)) {
PyObject *dct = PyDict_New();
PyObject *result = NULL;
PyDict_Update(dct, self);
result = PyObject_RichCompare(dct, other, op);
Py_DECREF(dct);
return result;
}
Py_RETURN_NOTIMPLEMENTED;
}
static PyObject *
framelocalsproxy_repr(PyObject *self)
{
int i = Py_ReprEnter(self);
if (i != 0) {
return i > 0 ? PyUnicode_FromString("{...}") : NULL;
}
PyObject *dct = PyDict_New();
PyObject *repr = NULL;
if (PyDict_Update(dct, self) == 0) {
repr = PyObject_Repr(dct);
}
Py_ReprLeave(self);
Py_DECREF(dct);
return repr;
}
static PyObject*
framelocalsproxy_or(PyObject *self, PyObject *other)
{
if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) {
Py_RETURN_NOTIMPLEMENTED;
}
PyObject *result = PyDict_New();
if (PyDict_Update(result, self) < 0) {
Py_DECREF(result);
return NULL;
}
if (PyDict_Update(result, other) < 0) {
Py_DECREF(result);
return NULL;
}
return result;
}
static PyObject*
framelocalsproxy_inplace_or(PyObject *self, PyObject *other)
{
if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) {
Py_RETURN_NOTIMPLEMENTED;
}
if (framelocalsproxy_merge(self, other) < 0) {
Py_RETURN_NOTIMPLEMENTED;
}
return Py_NewRef(self);
}
static PyObject*
framelocalsproxy_values(PyObject *self, PyObject *__unused)
{
PyObject *values = PyList_New(0);
PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i);
if (value) {
PyList_Append(values, value);
}
}
// Iterate through the extra locals
Py_ssize_t j = 0;
PyObject *key = NULL;
PyObject *value = NULL;
if (frame->f_extra_locals) {
while (PyDict_Next(frame->f_extra_locals, &j, &key, &value)) {
PyList_Append(values, value);
}
}
return values;
}
static PyObject *
framelocalsproxy_items(PyObject *self, PyObject *__unused)
{
PyObject *items = PyList_New(0);
PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i);
if (value) {
PyObject *pair = PyTuple_Pack(2, name, value);
PyList_Append(items, pair);
Py_DECREF(pair);
}
}
// Iterate through the extra locals
Py_ssize_t j = 0;
PyObject *key = NULL;
PyObject *value = NULL;
if (frame->f_extra_locals) {
while (PyDict_Next(frame->f_extra_locals, &j, &key, &value)) {
PyObject *pair = PyTuple_Pack(2, key, value);
PyList_Append(items, pair);
Py_DECREF(pair);
}
}
return items;
}
static Py_ssize_t
framelocalsproxy_length(PyObject *self)
{
PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
Py_ssize_t size = 0;
if (frame->f_extra_locals != NULL) {
assert(PyDict_Check(frame->f_extra_locals));
size += PyDict_Size(frame->f_extra_locals);
}
for (int i = 0; i < co->co_nlocalsplus; i++) {
if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) {
size++;
}
}
return size;
}
static PyObject*
framelocalsproxy_contains(PyObject *self, PyObject *key)
{
PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
if (PyUnicode_CheckExact(key)) {
int i = framelocalsproxy_getkeyindex(frame, key, true);
if (i >= 0) {
Py_RETURN_TRUE;
}
}
PyObject *extra = ((PyFrameObject*)frame)->f_extra_locals;
if (extra != NULL) {
int result = PyDict_Contains(extra, key);
if (result < 0) {
return NULL;
} else if (result > 0) {
Py_RETURN_TRUE;
}
}
Py_RETURN_FALSE;
}
static PyObject*
framelocalsproxy_update(PyObject *self, PyObject *other)
{
if (framelocalsproxy_merge(self, other) < 0) {
PyErr_SetString(PyExc_TypeError, "update() argument must be dict or another FrameLocalsProxy");
return NULL;
}
Py_RETURN_NONE;
}
static PyObject*
framelocalsproxy_get(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
{
if (nargs < 1 || nargs > 2) {
PyErr_SetString(PyExc_TypeError, "get expected 1 or 2 arguments");
return NULL;
}
PyObject *key = args[0];
PyObject *default_value = Py_None;
if (nargs == 2) {
default_value = args[1];
}
PyObject *result = framelocalsproxy_getitem(self, key);
if (result == NULL) {
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
PyErr_Clear();
return Py_XNewRef(default_value);
}
return NULL;
}
return result;
}
static PyObject*
framelocalsproxy_setdefault(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
{
if (nargs < 1 || nargs > 2) {
PyErr_SetString(PyExc_TypeError, "setdefault expected 1 or 2 arguments");
return NULL;
}
PyObject *key = args[0];
PyObject *default_value = Py_None;
if (nargs == 2) {
default_value = args[1];
}
PyObject *result = framelocalsproxy_getitem(self, key);
if (result == NULL) {
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
PyErr_Clear();
if (framelocalsproxy_setitem(self, key, default_value) < 0) {
return NULL;
}
return Py_XNewRef(default_value);
}
return NULL;
}
return result;
}
static PyObject*
framelocalsproxy_reversed(PyObject *self, PyObject *__unused)
{
PyObject *result = framelocalsproxy_keys(self, NULL);
if (PyList_Reverse(result) < 0) {
Py_DECREF(result);
return NULL;
}
return result;
}
static PyNumberMethods framelocalsproxy_as_number = {
.nb_or = framelocalsproxy_or,
.nb_inplace_or = framelocalsproxy_inplace_or,
};
static PyMappingMethods framelocalsproxy_as_mapping = {
framelocalsproxy_length, // mp_length
framelocalsproxy_getitem, // mp_subscript
framelocalsproxy_setitem, // mp_ass_subscript
};
static PyMethodDef framelocalsproxy_methods[] = {
{"__contains__", framelocalsproxy_contains, METH_O | METH_COEXIST,
NULL},
{"__getitem__", framelocalsproxy_getitem, METH_O | METH_COEXIST,
NULL},
{"__reversed__", framelocalsproxy_reversed, METH_NOARGS,
NULL},
{"keys", framelocalsproxy_keys, METH_NOARGS,
NULL},
{"values", framelocalsproxy_values, METH_NOARGS,
NULL},
{"items", framelocalsproxy_items, METH_NOARGS,
NULL},
{"update", framelocalsproxy_update, METH_O,
NULL},
{"get", _PyCFunction_CAST(framelocalsproxy_get), METH_FASTCALL,
NULL},
{"setdefault", _PyCFunction_CAST(framelocalsproxy_setdefault), METH_FASTCALL,
NULL},
{NULL, NULL} /* sentinel */
};
PyTypeObject PyFrameLocalsProxy_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "FrameLocalsProxy",
.tp_basicsize = sizeof(PyFrameLocalsProxyObject),
.tp_dealloc = (destructor)framelocalsproxy_dealloc,
.tp_repr = &framelocalsproxy_repr,
.tp_as_number = &framelocalsproxy_as_number,
.tp_as_mapping = &framelocalsproxy_as_mapping,
.tp_getattro = PyObject_GenericGetAttr,
.tp_setattro = PyObject_GenericSetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = framelocalsproxy_visit,
.tp_clear = framelocalsproxy_tp_clear,
.tp_richcompare = framelocalsproxy_richcompare,
.tp_iter = framelocalsproxy_iter,
.tp_methods = framelocalsproxy_methods,
.tp_alloc = PyType_GenericAlloc,
.tp_new = framelocalsproxy_new,
.tp_free = PyObject_GC_Del,
};
PyObject *
_PyFrameLocalsProxy_New(PyFrameObject *frame)
{
PyObject* args = PyTuple_Pack(1, frame);
PyObject* proxy = (PyObject*)framelocalsproxy_new(&PyFrameLocalsProxy_Type, args, NULL);
Py_DECREF(args);
return proxy;
}
static PyMemberDef frame_memberlist[] = { static PyMemberDef frame_memberlist[] = {
{"f_trace_lines", Py_T_BOOL, OFF(f_trace_lines), 0}, {"f_trace_lines", Py_T_BOOL, OFF(f_trace_lines), 0},
{NULL} /* Sentinel */ {NULL} /* Sentinel */
}; };
static PyObject * static PyObject *
frame_getlocals(PyFrameObject *f, void *closure) frame_getlocals(PyFrameObject *f, void *closure)
{ {
@ -30,11 +651,14 @@ frame_getlocals(PyFrameObject *f, void *closure)
return NULL; return NULL;
} }
assert(!_PyFrame_IsIncomplete(f->f_frame)); assert(!_PyFrame_IsIncomplete(f->f_frame));
PyObject *locals = _PyFrame_GetLocals(f->f_frame, 1);
if (locals) { PyCodeObject *co = _PyFrame_GetCode(f->f_frame);
f->f_fast_as_locals = 1;
if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(f->f_frame)) {
return Py_NewRef(f->f_frame->f_locals);
} }
return locals;
return _PyFrameLocalsProxy_New(f);
} }
int int
@ -595,20 +1219,6 @@ first_line_not_before(int *lines, int len, int line)
return result; return result;
} }
static bool
frame_is_cleared(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
if (frame->f_frame->stacktop == 0) {
return true;
}
if (frame->f_frame->owner == FRAME_OWNED_BY_GENERATOR) {
PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame);
return gen->gi_frame_state == FRAME_CLEARED;
}
return false;
}
static bool frame_is_suspended(PyFrameObject *frame) static bool frame_is_suspended(PyFrameObject *frame)
{ {
assert(!_PyFrame_IsIncomplete(frame->f_frame)); assert(!_PyFrame_IsIncomplete(frame->f_frame));
@ -900,6 +1510,7 @@ frame_dealloc(PyFrameObject *f)
} }
Py_CLEAR(f->f_back); Py_CLEAR(f->f_back);
Py_CLEAR(f->f_trace); Py_CLEAR(f->f_trace);
Py_CLEAR(f->f_extra_locals);
PyObject_GC_Del(f); PyObject_GC_Del(f);
Py_XDECREF(co); Py_XDECREF(co);
Py_TRASHCAN_END; Py_TRASHCAN_END;
@ -910,6 +1521,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
{ {
Py_VISIT(f->f_back); Py_VISIT(f->f_back);
Py_VISIT(f->f_trace); Py_VISIT(f->f_trace);
Py_VISIT(f->f_extra_locals);
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) { if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
return 0; return 0;
} }
@ -921,6 +1533,7 @@ static int
frame_tp_clear(PyFrameObject *f) frame_tp_clear(PyFrameObject *f)
{ {
Py_CLEAR(f->f_trace); Py_CLEAR(f->f_trace);
Py_CLEAR(f->f_extra_locals);
/* locals and stack */ /* locals and stack */
PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame); PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame);
@ -1056,8 +1669,8 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
f->f_trace = NULL; f->f_trace = NULL;
f->f_trace_lines = 1; f->f_trace_lines = 1;
f->f_trace_opcodes = 0; f->f_trace_opcodes = 0;
f->f_fast_as_locals = 0;
f->f_lineno = 0; f->f_lineno = 0;
f->f_extra_locals = NULL;
return f; return f;
} }
@ -1204,103 +1817,45 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
} }
PyObject * bool
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden) _PyFrame_HasHiddenLocals(_PyInterpreterFrame *frame)
{ {
/* Merge fast locals into f->f_locals */ /*
PyObject *locals = frame->f_locals; * This function returns if there are hidden locals introduced by PEP 709,
if (locals == NULL) { * which are the isolated fast locals for inline comprehensions
locals = frame->f_locals = PyDict_New(); */
if (locals == NULL) { PyCodeObject* co = _PyFrame_GetCode(frame);
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;
}
}
frame_init_get_vars(frame);
PyCodeObject *co = _PyFrame_GetCode(frame);
for (int i = 0; i < co->co_nlocalsplus; i++) { for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *value; // borrowed reference
if (!frame_get_var(frame, co, i, &value)) {
continue;
}
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) { PyObject* value = framelocalsproxy_getval(frame, co, i);
if (PyObject_SetItem(hidden, name, value) != 0) {
goto error; if (value != NULL) {
} return true;
}
continue;
}
if (value == NULL) {
if (PyObject_DelItem(locals, name) != 0) {
if (PyErr_ExceptionMatches(PyExc_KeyError)) {
PyErr_Clear();
}
else {
goto error;
}
}
}
else {
if (PyObject_SetItem(locals, name, value) != 0) {
goto error;
} }
} }
} }
if (include_hidden && PyDict_Size(hidden)) { return false;
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 PyObject *
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) _PyFrame_GetLocals(_PyInterpreterFrame *frame)
{ {
PyObject *locals = _PyFrame_GetLocals(frame, 0); // We should try to avoid creating the FrameObject if possible.
if (locals == NULL) { // So we check if the frame is a module or class level scope
return -1; PyCodeObject *co = _PyFrame_GetCode(frame);
if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(frame)) {
return Py_NewRef(frame->f_locals);
} }
Py_DECREF(locals);
return 0; PyFrameObject* f = _PyFrame_GetFrameObject(frame);
return _PyFrameLocalsProxy_New(f);
} }
@ -1354,112 +1909,19 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name)
int int
PyFrame_FastToLocalsWithError(PyFrameObject *f) PyFrame_FastToLocalsWithError(PyFrameObject *f)
{ {
if (f == NULL) { return 0;
PyErr_BadInternalCall();
return -1;
}
assert(!_PyFrame_IsIncomplete(f->f_frame));
int err = _PyFrame_FastToLocalsWithError(f->f_frame);
if (err == 0) {
f->f_fast_as_locals = 1;
}
return err;
} }
void void
PyFrame_FastToLocals(PyFrameObject *f) PyFrame_FastToLocals(PyFrameObject *f)
{ {
int res; return;
assert(!_PyFrame_IsIncomplete(f->f_frame));
assert(!PyErr_Occurred());
res = PyFrame_FastToLocalsWithError(f);
if (res < 0)
PyErr_Clear();
}
void
_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
{
/* Merge locals into fast locals */
PyObject *locals;
PyObject **fast;
PyCodeObject *co;
locals = frame->f_locals;
if (locals == NULL) {
return;
}
fast = _PyFrame_GetLocalsArray(frame);
co = _PyFrame_GetCode(frame);
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < co->co_nlocalsplus; i++) {
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
/* Same test as in PyFrame_FastToLocals() above. */
if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) {
continue;
}
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
PyObject *value = PyObject_GetItem(locals, name);
/* We only care about NULLs if clear is true. */
if (value == NULL) {
PyErr_Clear();
if (!clear) {
continue;
}
}
PyObject *oldvalue = fast[i];
PyObject *cell = NULL;
if (kind == CO_FAST_FREE) {
// The cell was set when the frame was created from
// the function's closure.
assert(oldvalue != NULL && PyCell_Check(oldvalue));
cell = oldvalue;
}
else if (kind & CO_FAST_CELL && oldvalue != NULL) {
/* Same test as in PyFrame_FastToLocals() above. */
if (PyCell_Check(oldvalue) &&
_PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) {
// (likely) MAKE_CELL must have executed already.
cell = oldvalue;
}
// (unlikely) Otherwise, it must have been set to some
// initial value by an earlier call to PyFrame_LocalsToFast().
}
if (cell != NULL) {
oldvalue = PyCell_GET(cell);
if (value != oldvalue) {
PyCell_SET(cell, Py_XNewRef(value));
Py_XDECREF(oldvalue);
}
}
else if (value != oldvalue) {
if (value == NULL) {
// Probably can't delete this, since the compiler's flow
// analysis may have already "proven" that it exists here:
const char *e = "assigning None to unbound local %R";
if (PyErr_WarnFormat(PyExc_RuntimeWarning, 0, e, name)) {
// It's okay if frame_obj is NULL, just try anyways:
PyErr_WriteUnraisable((PyObject *)frame->frame_obj);
}
value = Py_NewRef(Py_None);
}
Py_XSETREF(fast[i], Py_NewRef(value));
}
Py_XDECREF(value);
}
PyErr_SetRaisedException(exc);
} }
void void
PyFrame_LocalsToFast(PyFrameObject *f, int clear) PyFrame_LocalsToFast(PyFrameObject *f, int clear)
{ {
assert(!_PyFrame_IsIncomplete(f->f_frame)); return;
if (f && f->f_fast_as_locals && !frame_is_cleared(f)) {
_PyFrame_LocalsToFast(f->f_frame, clear);
f->f_fast_as_locals = 0;
}
} }
int int

View File

@ -2235,6 +2235,7 @@ static PyTypeObject* static_types[] = {
&PyFilter_Type, &PyFilter_Type,
&PyFloat_Type, &PyFloat_Type,
&PyFrame_Type, &PyFrame_Type,
&PyFrameLocalsProxy_Type,
&PyFrozenSet_Type, &PyFrozenSet_Type,
&PyFunction_Type, &PyFunction_Type,
&PyGen_Type, &PyGen_Type,

3
PC/python3dll.c generated
View File

@ -253,6 +253,9 @@ EXPORT_FUNC(PyEval_EvalFrame)
EXPORT_FUNC(PyEval_EvalFrameEx) EXPORT_FUNC(PyEval_EvalFrameEx)
EXPORT_FUNC(PyEval_GetBuiltins) EXPORT_FUNC(PyEval_GetBuiltins)
EXPORT_FUNC(PyEval_GetFrame) EXPORT_FUNC(PyEval_GetFrame)
EXPORT_FUNC(PyEval_GetFrameBuiltins)
EXPORT_FUNC(PyEval_GetFrameGlobals)
EXPORT_FUNC(PyEval_GetFrameLocals)
EXPORT_FUNC(PyEval_GetFuncDesc) EXPORT_FUNC(PyEval_GetFuncDesc)
EXPORT_FUNC(PyEval_GetFuncName) EXPORT_FUNC(PyEval_GetFuncName)
EXPORT_FUNC(PyEval_GetGlobals) EXPORT_FUNC(PyEval_GetGlobals)

View File

@ -2475,12 +2475,7 @@ PyEval_GetLocals(void)
return NULL; return NULL;
} }
if (_PyFrame_FastToLocalsWithError(current_frame) < 0) { PyObject *locals = _PyEval_GetFrameLocals();
return NULL;
}
PyObject *locals = current_frame->f_locals;
assert(locals != NULL);
return locals; return locals;
} }
@ -2494,7 +2489,24 @@ _PyEval_GetFrameLocals(void)
return NULL; return NULL;
} }
return _PyFrame_GetLocals(current_frame, 1); PyObject *locals = _PyFrame_GetLocals(current_frame);
if (locals == NULL) {
return NULL;
}
if (PyFrameLocalsProxy_Check(locals)) {
PyObject* ret = PyDict_New();
if (PyDict_Update(ret, locals)) {
Py_DECREF(ret);
return NULL;
}
Py_DECREF(locals);
return ret;
} else if (PyMapping_Check(locals)) {
return locals;
}
return NULL;
} }
PyObject * PyObject *
@ -2508,6 +2520,28 @@ PyEval_GetGlobals(void)
return current_frame->f_globals; return current_frame->f_globals;
} }
PyObject*
PyEval_GetFrameLocals(void)
{
return _PyEval_GetFrameLocals();
}
PyObject* PyEval_GetFrameGlobals(void)
{
PyThreadState *tstate = _PyThreadState_GET();
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
if (current_frame == NULL) {
return NULL;
}
return Py_XNewRef(current_frame->f_globals);
}
PyObject* PyEval_GetFrameBuiltins(void)
{
PyThreadState *tstate = _PyThreadState_GET();
return Py_XNewRef(_PyEval_GetBuiltins(tstate));
}
int int
PyEval_MergeCompilerFlags(PyCompilerFlags *cf) PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
{ {

View File

@ -123,18 +123,15 @@ static PyObject *
import_star(PyThreadState* tstate, PyObject *from) import_star(PyThreadState* tstate, PyObject *from)
{ {
_PyInterpreterFrame *frame = tstate->current_frame; _PyInterpreterFrame *frame = tstate->current_frame;
if (_PyFrame_FastToLocalsWithError(frame) < 0) {
return NULL;
}
PyObject *locals = frame->f_locals; PyObject *locals = _PyFrame_GetLocals(frame);
if (locals == NULL) { if (locals == NULL) {
_PyErr_SetString(tstate, PyExc_SystemError, _PyErr_SetString(tstate, PyExc_SystemError,
"no locals found during 'import *'"); "no locals found during 'import *'");
return NULL; return NULL;
} }
int err = import_all_from(tstate, locals, from); int err = import_all_from(tstate, locals, from);
_PyFrame_LocalsToFast(frame, 0); Py_DECREF(locals);
if (err < 0) { if (err < 0) {
return NULL; return NULL;
} }

View File

@ -1022,13 +1022,6 @@ static PyObject *
call_trampoline(PyThreadState *tstate, PyObject* callback, call_trampoline(PyThreadState *tstate, PyObject* callback,
PyFrameObject *frame, int what, PyObject *arg) PyFrameObject *frame, int what, PyObject *arg)
{ {
/* Discard any previous modifications the frame's fast locals */
if (frame->f_fast_as_locals) {
if (PyFrame_FastToLocalsWithError(frame) < 0) {
return NULL;
}
}
/* call the Python-level function */ /* call the Python-level function */
if (arg == NULL) { if (arg == NULL) {
arg = Py_None; arg = Py_None;
@ -1036,7 +1029,6 @@ call_trampoline(PyThreadState *tstate, PyObject* callback,
PyObject *args[3] = {(PyObject *)frame, whatstrings[what], arg}; PyObject *args[3] = {(PyObject *)frame, whatstrings[what], arg};
PyObject *result = _PyObject_VectorcallTstate(tstate, callback, args, 3, NULL); PyObject *result = _PyObject_VectorcallTstate(tstate, callback, args, 3, NULL);
PyFrame_LocalsToFast(frame, 1);
return result; return result;
} }

View File

@ -43,6 +43,7 @@ Objects/enumobject.c - PyReversed_Type -
Objects/fileobject.c - PyStdPrinter_Type - Objects/fileobject.c - PyStdPrinter_Type -
Objects/floatobject.c - PyFloat_Type - Objects/floatobject.c - PyFloat_Type -
Objects/frameobject.c - PyFrame_Type - Objects/frameobject.c - PyFrame_Type -
Objects/frameobject.c - PyFrameLocalsProxy_Type -
Objects/funcobject.c - PyClassMethod_Type - Objects/funcobject.c - PyClassMethod_Type -
Objects/funcobject.c - PyFunction_Type - Objects/funcobject.c - PyFunction_Type -
Objects/funcobject.c - PyStaticMethod_Type - Objects/funcobject.c - PyStaticMethod_Type -

Can't render this file because it has a wrong number of fields in line 4.