mirror of https://github.com/python/cpython
gh-74929: Implement PEP 667 (GH-115153)
This commit is contained in:
parent
1ab6356ebe
commit
b034f14a4b
|
@ -188,6 +188,9 @@ function,PyEval_EvalFrame,3.2,,
|
|||
function,PyEval_EvalFrameEx,3.2,,
|
||||
function,PyEval_GetBuiltins,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_GetFuncName,3.2,,
|
||||
function,PyEval_GetGlobals,3.2,,
|
||||
|
|
|
@ -22,6 +22,10 @@ PyAPI_FUNC(PyObject *) PyEval_GetGlobals(void);
|
|||
PyAPI_FUNC(PyObject *) PyEval_GetLocals(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_MakePendingCalls(void);
|
||||
|
||||
|
|
|
@ -27,3 +27,9 @@ PyAPI_FUNC(int) _PyFrame_IsEntryFrame(PyFrameObject *frame);
|
|||
|
||||
PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f);
|
||||
PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
|
||||
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
PyFrameObject* frame;
|
||||
} PyFrameLocalsProxyObject;
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
#endif
|
||||
|
||||
PyAPI_DATA(PyTypeObject) PyFrame_Type;
|
||||
PyAPI_DATA(PyTypeObject) PyFrameLocalsProxy_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(PyObject *) PyFrame_GetLocals(PyFrameObject *frame);
|
||||
|
|
|
@ -25,7 +25,7 @@ struct _frame {
|
|||
int f_lineno; /* Current line number. Only valid if non-zero */
|
||||
char f_trace_lines; /* Emit per-line 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 */
|
||||
PyObject *_f_frame_data[1];
|
||||
};
|
||||
|
@ -245,14 +245,11 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame * frame);
|
|||
int
|
||||
_PyFrame_Traverse(_PyInterpreterFrame *frame, visitproc visit, void *arg);
|
||||
|
||||
bool
|
||||
_PyFrame_HasHiddenLocals(_PyInterpreterFrame *frame);
|
||||
|
||||
PyObject *
|
||||
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden);
|
||||
|
||||
int
|
||||
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame);
|
||||
|
||||
void
|
||||
_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear);
|
||||
_PyFrame_GetLocals(_PyInterpreterFrame *frame);
|
||||
|
||||
static inline bool
|
||||
_PyThreadState_HasStackSpace(PyThreadState *tstate, int size)
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import copy
|
||||
import gc
|
||||
import operator
|
||||
import re
|
||||
|
@ -13,7 +14,7 @@ except ImportError:
|
|||
_testcapi = None
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
@ -198,14 +199,6 @@ class FrameAttrsTest(unittest.TestCase):
|
|||
tb = tb.tb_next
|
||||
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):
|
||||
# Test f_locals after clear() (issue #21897)
|
||||
f, outer, inner = self.make_frames()
|
||||
|
@ -217,8 +210,8 @@ class FrameAttrsTest(unittest.TestCase):
|
|||
def test_locals_clear_locals(self):
|
||||
# Test f_locals before and after clear() (to exercise caching)
|
||||
f, outer, inner = self.make_frames()
|
||||
outer.f_locals
|
||||
inner.f_locals
|
||||
self.assertNotEqual(outer.f_locals, {})
|
||||
self.assertNotEqual(inner.f_locals, {})
|
||||
outer.clear()
|
||||
inner.clear()
|
||||
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>$"
|
||||
% (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):
|
||||
|
||||
def test_issue95818(self):
|
||||
|
|
|
@ -622,9 +622,14 @@ class ListComprehensionTest(unittest.TestCase):
|
|||
|
||||
def test_frame_locals(self):
|
||||
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
|
||||
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})
|
||||
|
||||
def _recursive_replace(self, maybe_code):
|
||||
|
|
|
@ -933,23 +933,6 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
|||
self.assertNotInBytecode(f, "LOAD_FAST_CHECK")
|
||||
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):
|
||||
f = self.make_function_with_no_checks()
|
||||
def trace(frame, event, arg):
|
||||
|
|
|
@ -227,6 +227,9 @@ SYMBOL_NAMES = (
|
|||
"PyEval_EvalFrameEx",
|
||||
"PyEval_GetBuiltins",
|
||||
"PyEval_GetFrame",
|
||||
"PyEval_GetFrameBuiltins",
|
||||
"PyEval_GetFrameGlobals",
|
||||
"PyEval_GetFrameLocals",
|
||||
"PyEval_GetFuncDesc",
|
||||
"PyEval_GetFuncName",
|
||||
"PyEval_GetGlobals",
|
||||
|
|
|
@ -1561,7 +1561,7 @@ class SizeofTest(unittest.TestCase):
|
|||
def func():
|
||||
return sys._getframe()
|
||||
x = func()
|
||||
check(x, size('3Pi3c7P2ic??2P'))
|
||||
check(x, size('3Pi2cP7P2ic??2P'))
|
||||
# function
|
||||
def func(): pass
|
||||
check(func, size('15Pi'))
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Implement PEP 667 - converted ``frame.f_locals`` to a write through proxy
|
|
@ -2501,3 +2501,9 @@
|
|||
added = '3.13'
|
||||
[function.PyType_GetModuleByDef]
|
||||
added = '3.13'
|
||||
[function.PyEval_GetFrameBuiltins]
|
||||
added = '3.13'
|
||||
[function.PyEval_GetFrameGlobals]
|
||||
added = '3.13'
|
||||
[function.PyEval_GetFrameLocals]
|
||||
added = '3.13'
|
||||
|
|
|
@ -16,12 +16,633 @@
|
|||
|
||||
#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[] = {
|
||||
{"f_trace_lines", Py_T_BOOL, OFF(f_trace_lines), 0},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
||||
static PyObject *
|
||||
frame_getlocals(PyFrameObject *f, void *closure)
|
||||
{
|
||||
|
@ -30,11 +651,14 @@ frame_getlocals(PyFrameObject *f, void *closure)
|
|||
return NULL;
|
||||
}
|
||||
assert(!_PyFrame_IsIncomplete(f->f_frame));
|
||||
PyObject *locals = _PyFrame_GetLocals(f->f_frame, 1);
|
||||
if (locals) {
|
||||
f->f_fast_as_locals = 1;
|
||||
|
||||
PyCodeObject *co = _PyFrame_GetCode(f->f_frame);
|
||||
|
||||
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
|
||||
|
@ -595,20 +1219,6 @@ first_line_not_before(int *lines, int len, int line)
|
|||
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)
|
||||
{
|
||||
assert(!_PyFrame_IsIncomplete(frame->f_frame));
|
||||
|
@ -900,6 +1510,7 @@ frame_dealloc(PyFrameObject *f)
|
|||
}
|
||||
Py_CLEAR(f->f_back);
|
||||
Py_CLEAR(f->f_trace);
|
||||
Py_CLEAR(f->f_extra_locals);
|
||||
PyObject_GC_Del(f);
|
||||
Py_XDECREF(co);
|
||||
Py_TRASHCAN_END;
|
||||
|
@ -910,6 +1521,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
|
|||
{
|
||||
Py_VISIT(f->f_back);
|
||||
Py_VISIT(f->f_trace);
|
||||
Py_VISIT(f->f_extra_locals);
|
||||
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -921,6 +1533,7 @@ static int
|
|||
frame_tp_clear(PyFrameObject *f)
|
||||
{
|
||||
Py_CLEAR(f->f_trace);
|
||||
Py_CLEAR(f->f_extra_locals);
|
||||
|
||||
/* locals and stack */
|
||||
PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame);
|
||||
|
@ -1056,8 +1669,8 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
|
|||
f->f_trace = NULL;
|
||||
f->f_trace_lines = 1;
|
||||
f->f_trace_opcodes = 0;
|
||||
f->f_fast_as_locals = 0;
|
||||
f->f_lineno = 0;
|
||||
f->f_extra_locals = NULL;
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -1204,103 +1817,45 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
|
|||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden)
|
||||
bool
|
||||
_PyFrame_HasHiddenLocals(_PyInterpreterFrame *frame)
|
||||
{
|
||||
/* Merge fast locals into f->f_locals */
|
||||
PyObject *locals = frame->f_locals;
|
||||
if (locals == NULL) {
|
||||
locals = frame->f_locals = PyDict_New();
|
||||
if (locals == NULL) {
|
||||
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);
|
||||
|
||||
/*
|
||||
* This function returns if there are hidden locals introduced by PEP 709,
|
||||
* which are the isolated fast locals for inline comprehensions
|
||||
*/
|
||||
PyCodeObject* co = _PyFrame_GetCode(frame);
|
||||
|
||||
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);
|
||||
|
||||
if (kind & CO_FAST_HIDDEN) {
|
||||
if (include_hidden && value != NULL) {
|
||||
if (PyObject_SetItem(hidden, name, value) != 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
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;
|
||||
PyObject* value = framelocalsproxy_getval(frame, co, i);
|
||||
|
||||
if (value != NULL) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
|
||||
PyObject *
|
||||
_PyFrame_GetLocals(_PyInterpreterFrame *frame)
|
||||
{
|
||||
PyObject *locals = _PyFrame_GetLocals(frame, 0);
|
||||
if (locals == NULL) {
|
||||
return -1;
|
||||
// We should try to avoid creating the FrameObject if possible.
|
||||
// So we check if the frame is a module or class level scope
|
||||
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
|
||||
PyFrame_FastToLocalsWithError(PyFrameObject *f)
|
||||
{
|
||||
if (f == NULL) {
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
PyFrame_FastToLocals(PyFrameObject *f)
|
||||
{
|
||||
int res;
|
||||
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
|
||||
PyFrame_LocalsToFast(PyFrameObject *f, int clear)
|
||||
{
|
||||
assert(!_PyFrame_IsIncomplete(f->f_frame));
|
||||
if (f && f->f_fast_as_locals && !frame_is_cleared(f)) {
|
||||
_PyFrame_LocalsToFast(f->f_frame, clear);
|
||||
f->f_fast_as_locals = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
|
@ -2235,6 +2235,7 @@ static PyTypeObject* static_types[] = {
|
|||
&PyFilter_Type,
|
||||
&PyFloat_Type,
|
||||
&PyFrame_Type,
|
||||
&PyFrameLocalsProxy_Type,
|
||||
&PyFrozenSet_Type,
|
||||
&PyFunction_Type,
|
||||
&PyGen_Type,
|
||||
|
|
|
@ -253,6 +253,9 @@ EXPORT_FUNC(PyEval_EvalFrame)
|
|||
EXPORT_FUNC(PyEval_EvalFrameEx)
|
||||
EXPORT_FUNC(PyEval_GetBuiltins)
|
||||
EXPORT_FUNC(PyEval_GetFrame)
|
||||
EXPORT_FUNC(PyEval_GetFrameBuiltins)
|
||||
EXPORT_FUNC(PyEval_GetFrameGlobals)
|
||||
EXPORT_FUNC(PyEval_GetFrameLocals)
|
||||
EXPORT_FUNC(PyEval_GetFuncDesc)
|
||||
EXPORT_FUNC(PyEval_GetFuncName)
|
||||
EXPORT_FUNC(PyEval_GetGlobals)
|
||||
|
|
|
@ -2475,12 +2475,7 @@ PyEval_GetLocals(void)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (_PyFrame_FastToLocalsWithError(current_frame) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyObject *locals = current_frame->f_locals;
|
||||
assert(locals != NULL);
|
||||
PyObject *locals = _PyEval_GetFrameLocals();
|
||||
return locals;
|
||||
}
|
||||
|
||||
|
@ -2494,7 +2489,24 @@ _PyEval_GetFrameLocals(void)
|
|||
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 *
|
||||
|
@ -2508,6 +2520,28 @@ PyEval_GetGlobals(void)
|
|||
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
|
||||
PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
|
||||
{
|
||||
|
|
|
@ -123,18 +123,15 @@ static PyObject *
|
|||
import_star(PyThreadState* tstate, PyObject *from)
|
||||
{
|
||||
_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) {
|
||||
_PyErr_SetString(tstate, PyExc_SystemError,
|
||||
"no locals found during 'import *'");
|
||||
return NULL;
|
||||
}
|
||||
int err = import_all_from(tstate, locals, from);
|
||||
_PyFrame_LocalsToFast(frame, 0);
|
||||
Py_DECREF(locals);
|
||||
if (err < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1022,13 +1022,6 @@ static PyObject *
|
|||
call_trampoline(PyThreadState *tstate, PyObject* callback,
|
||||
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 */
|
||||
if (arg == NULL) {
|
||||
arg = Py_None;
|
||||
|
@ -1036,7 +1029,6 @@ call_trampoline(PyThreadState *tstate, PyObject* callback,
|
|||
PyObject *args[3] = {(PyObject *)frame, whatstrings[what], arg};
|
||||
PyObject *result = _PyObject_VectorcallTstate(tstate, callback, args, 3, NULL);
|
||||
|
||||
PyFrame_LocalsToFast(frame, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ Objects/enumobject.c - PyReversed_Type -
|
|||
Objects/fileobject.c - PyStdPrinter_Type -
|
||||
Objects/floatobject.c - PyFloat_Type -
|
||||
Objects/frameobject.c - PyFrame_Type -
|
||||
Objects/frameobject.c - PyFrameLocalsProxy_Type -
|
||||
Objects/funcobject.c - PyClassMethod_Type -
|
||||
Objects/funcobject.c - PyFunction_Type -
|
||||
Objects/funcobject.c - PyStaticMethod_Type -
|
||||
|
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Reference in New Issue