[Python-checkins] bpo-40421: Add PyFrame_GetBack() function (GH-19765)

Victor Stinner webhook-mailer at python.org
Tue Apr 28 21:28:51 EDT 2020


https://github.com/python/cpython/commit/703647732359200c54f1d2e695cc3a06b9a96c9a
commit: 703647732359200c54f1d2e695cc3a06b9a96c9a
branch: master
author: Victor Stinner <vstinner at python.org>
committer: GitHub <noreply at github.com>
date: 2020-04-29T03:28:46+02:00
summary:

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.

files:
A Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst
M Doc/c-api/reflection.rst
M Doc/whatsnew/3.9.rst
M Include/cpython/frameobject.h
M Modules/_tracemalloc.c
M Objects/frameobject.c
M Python/_warnings.c
M Python/sysmodule.c
M Python/traceback.c

diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst
index 21d9878609127..9207d86012c8b 100644
--- a/Doc/c-api/reflection.rst
+++ b/Doc/c-api/reflection.rst
@@ -31,6 +31,17 @@ Reflection
    See also :c:func:`PyThreadState_GetFrame`.
 
 
+.. c:function:: int PyFrame_GetBack(PyFrameObject *frame)
+
+   Get the *frame* next outer frame.
+
+   Return a strong reference, or ``NULL`` if *frame* has no outer frame.
+
+   *frame* must not be ``NULL``.
+
+   .. versionadded:: 3.9
+
+
 .. c:function:: int PyFrame_GetCode(PyFrameObject *frame)
 
    Get the *frame* code.
diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst
index e26bd473e6189..0edb11419c43c 100644
--- a/Doc/whatsnew/3.9.rst
+++ b/Doc/whatsnew/3.9.rst
@@ -538,6 +538,7 @@ Build and C API Changes
 =======================
 
 * New :c:func:`PyFrame_GetCode` function: get a frame code.
+  New :c:func:`PyFrame_GetBack` function: get the frame next outer frame.
   (Contributed by Victor Stinner in :issue:`40421`.)
 
 * Add :c:func:`PyFrame_GetLineNumber` to the limited C API.
diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h
index e32efac594718..36a51baae8784 100644
--- a/Include/cpython/frameobject.h
+++ b/Include/cpython/frameobject.h
@@ -77,6 +77,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *);
 
 PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out);
 
+PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst b/Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst
new file mode 100644
index 0000000000000..aadfb339b1711
--- /dev/null
+++ b/Misc/NEWS.d/next/C API/2020-04-28-19-29-36.bpo-40421.3uIIaB.rst	
@@ -0,0 +1 @@
+New :c:func:`PyFrame_GetBack` function: get the frame next outer frame.
diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c
index 6f28f7f5757fa..ea7e0127366ab 100644
--- a/Modules/_tracemalloc.c
+++ b/Modules/_tracemalloc.c
@@ -3,7 +3,7 @@
 #include "pycore_pymem.h"         // _Py_tracemalloc_config
 #include "pycore_traceback.h"
 #include "hashtable.h"
-#include "frameobject.h"
+#include "frameobject.h"          // PyFrame_GetBack()
 
 #include "clinic/_tracemalloc.c.h"
 /*[clinic input]
@@ -434,15 +434,19 @@ traceback_get_frames(traceback_t *traceback)
     }
 
     PyFrameObject *pyframe = PyThreadState_GetFrame(tstate);
-    Py_XDECREF(pyframe); // use a borrowed reference
-    for (; pyframe != NULL; pyframe = pyframe->f_back) {
+    for (; pyframe != NULL;) {
         if (traceback->nframe < _Py_tracemalloc_config.max_nframe) {
             tracemalloc_get_frame(pyframe, &traceback->frames[traceback->nframe]);
             assert(traceback->frames[traceback->nframe].filename != NULL);
             traceback->nframe++;
         }
-        if (traceback->total_nframe < UINT16_MAX)
+        if (traceback->total_nframe < UINT16_MAX) {
             traceback->total_nframe++;
+        }
+
+        PyFrameObject *back = PyFrame_GetBack(pyframe);
+        Py_DECREF(pyframe);
+        pyframe = back;
     }
 }
 
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 6d288b5b059d7..451c895a77c6b 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -1237,3 +1237,13 @@ PyFrame_GetCode(PyFrameObject *frame)
     Py_INCREF(code);
     return code;
 }
+
+
+PyFrameObject*
+PyFrame_GetBack(PyFrameObject *frame)
+{
+    assert(frame != NULL);
+    PyFrameObject *back = frame->f_back;
+    Py_XINCREF(back);
+    return back;
+}
diff --git a/Python/_warnings.c b/Python/_warnings.c
index 7c15ce0ef89c3..4d65bb30c8e5c 100644
--- a/Python/_warnings.c
+++ b/Python/_warnings.c
@@ -3,7 +3,7 @@
 #include "pycore_interp.h"        // PyInterpreterState.warnings
 #include "pycore_pyerrors.h"
 #include "pycore_pystate.h"       // _PyThreadState_GET()
-#include "frameobject.h"
+#include "frameobject.h"          // PyFrame_GetBack()
 #include "clinic/_warnings.c.h"
 
 #define MODULE_NAME "_warnings"
@@ -815,7 +815,9 @@ static PyFrameObject *
 next_external_frame(PyFrameObject *frame)
 {
     do {
-        frame = frame->f_back;
+        PyFrameObject *back = PyFrame_GetBack(frame);
+        Py_DECREF(frame);
+        frame = back;
     } while (frame != NULL && is_internal_frame(frame));
 
     return frame;
@@ -831,12 +833,15 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
     PyObject *globals;
 
     /* Setup globals, filename and lineno. */
-    PyFrameObject *f = _PyThreadState_GET()->frame;
+    PyThreadState *tstate = _PyThreadState_GET();
+    PyFrameObject *f = PyThreadState_GetFrame(tstate);
     // Stack level comparisons to Python code is off by one as there is no
     // warnings-related stack level to avoid.
     if (stack_level <= 0 || is_internal_frame(f)) {
         while (--stack_level > 0 && f != NULL) {
-            f = f->f_back;
+            PyFrameObject *back = PyFrame_GetBack(f);
+            Py_DECREF(f);
+            f = back;
         }
     }
     else {
@@ -857,6 +862,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
         Py_DECREF(code);
         Py_INCREF(*filename);
         *lineno = PyFrame_GetLineNumber(f);
+        Py_DECREF(f);
     }
 
     *module = NULL;
@@ -868,7 +874,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
     if (*registry == NULL) {
         int rc;
 
-        if (PyErr_Occurred()) {
+        if (_PyErr_Occurred(tstate)) {
             goto handle_error;
         }
         *registry = PyDict_New();
@@ -887,7 +893,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno,
     if (*module == Py_None || (*module != NULL && PyUnicode_Check(*module))) {
         Py_INCREF(*module);
     }
-    else if (PyErr_Occurred()) {
+    else if (_PyErr_Occurred(tstate)) {
         goto handle_error;
     }
     else {
diff --git a/Python/sysmodule.c b/Python/sysmodule.c
index 92ea5e7d637b9..914beb7e127fe 100644
--- a/Python/sysmodule.c
+++ b/Python/sysmodule.c
@@ -16,7 +16,7 @@ Data members:
 
 #include "Python.h"
 #include "code.h"
-#include "frameobject.h"
+#include "frameobject.h"          // PyFrame_GetBack()
 #include "pycore_ceval.h"         // _Py_RecursionLimitLowerWaterMark()
 #include "pycore_initconfig.h"
 #include "pycore_object.h"
@@ -1787,14 +1787,17 @@ sys__getframe_impl(PyObject *module, int depth)
 /*[clinic end generated code: output=d438776c04d59804 input=c1be8a6464b11ee5]*/
 {
     PyThreadState *tstate = _PyThreadState_GET();
-    PyFrameObject *f = tstate->frame;
+    PyFrameObject *f = PyThreadState_GetFrame(tstate);
 
     if (_PySys_Audit(tstate, "sys._getframe", "O", f) < 0) {
+        Py_DECREF(f);
         return NULL;
     }
 
     while (depth > 0 && f != NULL) {
-        f = f->f_back;
+        PyFrameObject *back = PyFrame_GetBack(f);
+        Py_DECREF(f);
+        f = back;
         --depth;
     }
     if (f == NULL) {
@@ -1802,7 +1805,6 @@ sys__getframe_impl(PyObject *module, int depth)
                          "call stack is not deep enough");
         return NULL;
     }
-    Py_INCREF(f);
     return (PyObject*)f;
 }
 
diff --git a/Python/traceback.c b/Python/traceback.c
index 438a2c4fce7ca..99b63af11f8be 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -4,7 +4,7 @@
 #include "Python.h"
 
 #include "code.h"
-#include "frameobject.h"
+#include "frameobject.h"          // PyFrame_GetBack()
 #include "structmember.h"         // PyMemberDef
 #include "osdefs.h"               // SEP
 #ifdef HAVE_FCNTL_H
@@ -798,22 +798,31 @@ dump_traceback(int fd, PyThreadState *tstate, int write_header)
         PUTS(fd, "Stack (most recent call first):\n");
     }
 
-    frame = tstate->frame;
+    frame = PyThreadState_GetFrame(tstate);
     if (frame == NULL) {
         PUTS(fd, "<no Python frame>\n");
         return;
     }
 
     depth = 0;
-    while (frame != NULL) {
+    while (1) {
         if (MAX_FRAME_DEPTH <= depth) {
+            Py_DECREF(frame);
             PUTS(fd, "  ...\n");
             break;
         }
-        if (!PyFrame_Check(frame))
+        if (!PyFrame_Check(frame)) {
+            Py_DECREF(frame);
             break;
+        }
         dump_frame(fd, frame);
-        frame = frame->f_back;
+        PyFrameObject *back = PyFrame_GetBack(frame);
+        Py_DECREF(frame);
+
+        if (back == NULL) {
+            break;
+        }
+        frame = back;
         depth++;
     }
 }



More information about the Python-checkins mailing list