summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/internal/pycore_frame.h4
-rw-r--r--Lib/test/test_sys.py2
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2024-05-30-04-11-36.gh-issue-118934.fbDqve.rst1
-rw-r--r--Objects/frameobject.c4
-rw-r--r--Python/ceval.c33
5 files changed, 42 insertions, 2 deletions
diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h
index 506c20c..d5115ad 100644
--- a/Include/internal/pycore_frame.h
+++ b/Include/internal/pycore_frame.h
@@ -27,6 +27,10 @@ struct _frame {
char f_trace_lines; /* Emit per-line trace events? */
char f_trace_opcodes; /* Emit per-opcode trace events? */
PyObject *f_extra_locals; /* Dict for locals set by users using f_locals, could be NULL */
+ /* This is purely for backwards compatibility for PyEval_GetLocals.
+ PyEval_GetLocals requires a borrowed reference so the actual reference
+ is stored here */
+ PyObject *f_locals_cache;
/* The frame data, if this frame object owns the frame */
PyObject *_f_frame_data[1];
};
diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 69dccf9..81789b2 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1603,7 +1603,7 @@ class SizeofTest(unittest.TestCase):
def func():
return sys._getframe()
x = func()
- check(x, size('3Pi2cP7P2ic??2P'))
+ check(x, size('3Pi2c2P7P2ic??2P'))
# function
def func(): pass
check(func, size('16Pi'))
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-05-30-04-11-36.gh-issue-118934.fbDqve.rst b/Misc/NEWS.d/next/Core and Builtins/2024-05-30-04-11-36.gh-issue-118934.fbDqve.rst
new file mode 100644
index 0000000..3087034
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-05-30-04-11-36.gh-issue-118934.fbDqve.rst
@@ -0,0 +1 @@
+Make ``PyEval_GetLocals`` return borrowed reference
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 3dc9ff0..2ef3bea 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -1627,6 +1627,7 @@ frame_dealloc(PyFrameObject *f)
Py_CLEAR(f->f_back);
Py_CLEAR(f->f_trace);
Py_CLEAR(f->f_extra_locals);
+ Py_CLEAR(f->f_locals_cache);
PyObject_GC_Del(f);
Py_XDECREF(co);
Py_TRASHCAN_END;
@@ -1638,6 +1639,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);
+ Py_VISIT(f->f_locals_cache);
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
return 0;
}
@@ -1650,6 +1652,7 @@ frame_tp_clear(PyFrameObject *f)
{
Py_CLEAR(f->f_trace);
Py_CLEAR(f->f_extra_locals);
+ Py_CLEAR(f->f_locals_cache);
/* locals and stack */
_PyStackRef *locals = _PyFrame_GetLocalsArray(f->f_frame);
@@ -1787,6 +1790,7 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
f->f_trace_opcodes = 0;
f->f_lineno = 0;
f->f_extra_locals = NULL;
+ f->f_locals_cache = NULL;
return f;
}
diff --git a/Python/ceval.c b/Python/ceval.c
index d8bc830..026e018 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -2525,6 +2525,7 @@ _PyEval_GetBuiltinId(_Py_Identifier *name)
PyObject *
PyEval_GetLocals(void)
{
+ // We need to return a borrowed reference here, so some tricks are needed
PyThreadState *tstate = _PyThreadState_GET();
_PyInterpreterFrame *current_frame = _PyThreadState_GetFrame(tstate);
if (current_frame == NULL) {
@@ -2532,7 +2533,37 @@ PyEval_GetLocals(void)
return NULL;
}
- PyObject *locals = _PyEval_GetFrameLocals();
+ // Be aware that this returns a new reference
+ PyObject *locals = _PyFrame_GetLocals(current_frame);
+
+ if (locals == NULL) {
+ return NULL;
+ }
+
+ if (PyFrameLocalsProxy_Check(locals)) {
+ PyFrameObject *f = _PyFrame_GetFrameObject(current_frame);
+ PyObject *ret = f->f_locals_cache;
+ if (ret == NULL) {
+ PyObject *ret = PyDict_New();
+ if (ret == NULL) {
+ Py_DECREF(locals);
+ return NULL;
+ }
+ f->f_locals_cache = ret;
+ }
+ if (PyDict_Update(ret, locals) < 0) {
+ // At this point, if the cache dict is broken, it will stay broken, as
+ // trying to clean it up or replace it will just cause other problems
+ ret = NULL;
+ }
+ Py_DECREF(locals);
+ return ret;
+ }
+
+ assert(PyMapping_Check(locals));
+ assert(Py_REFCNT(locals) > 1);
+ Py_DECREF(locals);
+
return locals;
}