Skip to content

Commit 7036477

Browse files
authored
bpo-40421: Add PyFrame_GetBack() function (GH-19765)
New PyFrame_GetBack() function: get the frame next outer frame. Replace frame->f_back with PyFrame_GetBack(frame) in most code but frameobject.c, ceval.c and genobject.c.
1 parent 66abe98 commit 7036477

File tree

9 files changed

+65
-19
lines changed

9 files changed

+65
-19
lines changed

Doc/c-api/reflection.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ Reflection
3131
See also :c:func:`PyThreadState_GetFrame`.
3232
3333
34+
.. c:function:: int PyFrame_GetBack(PyFrameObject *frame)
35+
36+
Get the *frame* next outer frame.
37+
38+
Return a strong reference, or ``NULL`` if *frame* has no outer frame.
39+
40+
*frame* must not be ``NULL``.
41+
42+
.. versionadded:: 3.9
43+
44+
3445
.. c:function:: int PyFrame_GetCode(PyFrameObject *frame)
3546
3647
Get the *frame* code.

Doc/whatsnew/3.9.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ Build and C API Changes
538538
=======================
539539

540540
* New :c:func:`PyFrame_GetCode` function: get a frame code.
541+
New :c:func:`PyFrame_GetBack` function: get the frame next outer frame.
541542
(Contributed by Victor Stinner in :issue:`40421`.)
542543

543544
* Add :c:func:`PyFrame_GetLineNumber` to the limited C API.

Include/cpython/frameobject.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
7777

7878
PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out);
7979

80+
PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
81+
8082
#ifdef __cplusplus
8183
}
8284
#endif
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
New :c:func:`PyFrame_GetBack` function: get the frame next outer frame.

Modules/_tracemalloc.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include "pycore_pymem.h" // _Py_tracemalloc_config
44
#include "pycore_traceback.h"
55
#include "hashtable.h"
6-
#include "frameobject.h"
6+
#include "frameobject.h" // PyFrame_GetBack()
77

88
#include "clinic/_tracemalloc.c.h"
99
/*[clinic input]
@@ -434,15 +434,19 @@ traceback_get_frames(traceback_t *traceback)
434434
}
435435

436436
PyFrameObject *pyframe = PyThreadState_GetFrame(tstate);
437-
Py_XDECREF(pyframe); // use a borrowed reference
438-
for (; pyframe != NULL; pyframe = pyframe->f_back) {
437+
for (; pyframe != NULL;) {
439438
if (traceback->nframe < _Py_tracemalloc_config.max_nframe) {
440439
tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]);
441440
assert(traceback->frames[traceback->nframe].filename != NULL);
442441
traceback->nframe++;
443442
}
444-
if (traceback->total_nframe < UINT16_MAX)
443+
if (traceback->total_nframe < UINT16_MAX) {
445444
traceback->total_nframe++;
445+
}
446+
447+
PyFrameObject *back = PyFrame_GetBack(pyframe);
448+
Py_DECREF(pyframe);
449+
pyframe = back;
446450
}
447451
}
448452

Objects/frameobject.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,3 +1237,13 @@ PyFrame_GetCode(PyFrameObject *frame)
12371237
Py_INCREF(code);
12381238
return code;
12391239
}
1240+
1241+
1242+
PyFrameObject*
1243+
PyFrame_GetBack(PyFrameObject *frame)
1244+
{
1245+
assert(frame != NULL);
1246+
PyFrameObject *back = frame->f_back;
1247+
Py_XINCREF(back);
1248+
return back;
1249+
}

Python/_warnings.c

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include "pycore_interp.h" // PyInterpreterState.warnings
44
#include "pycore_pyerrors.h"
55
#include "pycore_pystate.h" // _PyThreadState_GET()
6-
#include "frameobject.h"
6+
#include "frameobject.h" // PyFrame_GetBack()
77
#include "clinic/_warnings.c.h"
88

99
#define MODULE_NAME "_warnings"
@@ -815,7 +815,9 @@ static PyFrameObject *
815815
next_external_frame(PyFrameObject *frame)
816816
{
817817
do {
818-
frame = frame->f_back;
818+
PyFrameObject *back = PyFrame_GetBack(frame);
819+
Py_DECREF(frame);
820+
frame = back;
819821
} while (frame != NULL && is_internal_frame(frame));
820822

821823
return frame;
@@ -831,12 +833,15 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
831833
PyObject *globals;
832834

833835
/* Setup globals, filename and lineno. */
834-
PyFrameObject *f = _PyThreadState_GET()->frame;
836+
PyThreadState *tstate = _PyThreadState_GET();
837+
PyFrameObject *f = PyThreadState_GetFrame(tstate);
835838
// Stack level comparisons to Python code is off by one as there is no
836839
// warnings-related stack level to avoid.
837840
if (stack_level <= 0 || is_internal_frame(f)) {
838841
while (--stack_level > 0 && f != NULL) {
839-
f = f->f_back;
842+
PyFrameObject *back = PyFrame_GetBack(f);
843+
Py_DECREF(f);
844+
f = back;
840845
}
841846
}
842847
else {
@@ -857,6 +862,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
857862
Py_DECREF(code);
858863
Py_INCREF(*filename);
859864
*lineno = PyFrame_GetLineNumber(f);
865+
Py_DECREF(f);
860866
}
861867

862868
*module = NULL;
@@ -868,7 +874,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
868874
if (*registry == NULL) {
869875
int rc;
870876

871-
if (PyErr_Occurred()) {
877+
if (_PyErr_Occurred(tstate)) {
872878
goto handle_error;
873879
}
874880
*registry = PyDict_New();
@@ -887,7 +893,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
887893
if (*module == Py_None || (*module != NULL && PyUnicode_Check(*module))) {
888894
Py_INCREF(*module);
889895
}
890-
else if (PyErr_Occurred()) {
896+
else if (_PyErr_Occurred(tstate)) {
891897
goto handle_error;
892898
}
893899
else {

Python/sysmodule.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Data members:
1616

1717
#include "Python.h"
1818
#include "code.h"
19-
#include "frameobject.h"
19+
#include "frameobject.h" // PyFrame_GetBack()
2020
#include "pycore_ceval.h" // _Py_RecursionLimitLowerWaterMark()
2121
#include "pycore_initconfig.h"
2222
#include "pycore_object.h"
@@ -1787,22 +1787,24 @@ sys__getframe_impl(PyObject *module, int depth)
17871787
/*[clinic end generated code: output=d438776c04d59804 input=c1be8a6464b11ee5]*/
17881788
{
17891789
PyThreadState *tstate = _PyThreadState_GET();
1790-
PyFrameObject *f = tstate->frame;
1790+
PyFrameObject *f = PyThreadState_GetFrame(tstate);
17911791

17921792
if (_PySys_Audit(tstate, "sys._getframe", "O", f) < 0) {
1793+
Py_DECREF(f);
17931794
return NULL;
17941795
}
17951796

17961797
while (depth > 0 && f != NULL) {
1797-
f = f->f_back;
1798+
PyFrameObject *back = PyFrame_GetBack(f);
1799+
Py_DECREF(f);
1800+
f = back;
17981801
--depth;
17991802
}
18001803
if (f == NULL) {
18011804
_PyErr_SetString(tstate, PyExc_ValueError,
18021805
"call stack is not deep enough");
18031806
return NULL;
18041807
}
1805-
Py_INCREF(f);
18061808
return (PyObject*)f;
18071809
}
18081810

Python/traceback.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include "Python.h"
55

66
#include "code.h"
7-
#include "frameobject.h"
7+
#include "frameobject.h" // PyFrame_GetBack()
88
#include "structmember.h" // PyMemberDef
99
#include "osdefs.h" // SEP
1010
#ifdef HAVE_FCNTL_H
@@ -798,22 +798,31 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
798798
PUTS(fd, "Stack (most recent call first):\n");
799799
}
800800

801-
frame = tstate->frame;
801+
frame = PyThreadState_GetFrame(tstate);
802802
if (frame == NULL) {
803803
PUTS(fd, "<no Python frame>\n");
804804
return;
805805
}
806806

807807
depth = 0;
808-
while (frame != NULL) {
808+
while (1) {
809809
if (MAX_FRAME_DEPTH <= depth) {
810+
Py_DECREF(frame);
810811
PUTS(fd, " ...\n");
811812
break;
812813
}
813-
if (!PyFrame_Check(frame))
814+
if (!PyFrame_Check(frame)) {
815+
Py_DECREF(frame);
814816
break;
817+
}
815818
dump_frame(fd, frame);
816-
frame = frame->f_back;
819+
PyFrameObject *back = PyFrame_GetBack(frame);
820+
Py_DECREF(frame);
821+
822+
if (back == NULL) {
823+
break;
824+
}
825+
frame = back;
817826
depth++;
818827
}
819828
}

0 commit comments

Comments
 (0)