From a42ca74fa30227e2f89a619332557cf093a937d5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 28 Apr 2020 19:01:31 +0200 Subject: [PATCH] bpo-40421: Add PyFrame_GetCode() function (GH-19757) PyFrame_GetCode(frame): return a borrowed reference to the frame code. Replace frame->f_code with PyFrame_GetCode(frame) in most code, except in frameobject.c, genobject.c and ceval.c. Also add PyFrame_GetLineNumber() to the limited C API. --- Doc/c-api/init.rst | 6 ++++-- Doc/c-api/reflection.rst | 9 +++++++++ Doc/whatsnew/3.9.rst | 4 ++++ Include/pyframe.h | 2 ++ .../2020-04-28-15-47-58.bpo-40421.ZIzOV0.rst | 2 ++ Modules/_lsprof.c | 9 +++++---- Modules/_tracemalloc.c | 2 +- Objects/frameobject.c | 7 +++++++ Objects/typeobject.c | 4 ++-- Python/_warnings.c | 18 +++++++++++++----- Python/import.c | 3 +-- Python/traceback.c | 17 ++++++++--------- 12 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2020-04-28-15-47-58.bpo-40421.ZIzOV0.rst diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 435808f537b..afde3db3038 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -1074,8 +1074,10 @@ All of the following functions must be called after :c:func:`Py_Initialize`. .. c:function:: PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) - Get the current frame of the Python thread state *tstate*. It can be - ``NULL`` if no frame is currently executing. + Get a borrowed reference to the current frame of the Python thread state + *tstate*. + + Return ``NULL`` if no frame is currently executing. See also :c:func:`PyEval_GetFrame`. diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst index 498219fd9aa..b313ea30259 100644 --- a/Doc/c-api/reflection.rst +++ b/Doc/c-api/reflection.rst @@ -31,6 +31,15 @@ Reflection See also :c:func:`PyThreadState_GetFrame`. +.. c:function:: int PyFrame_GetCode(PyFrameObject *frame) + + Return a borrowed reference to the *frame* code. + + *frame* must not be ``NULL``. + + .. versionadded:: 3.9 + + .. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame) Return the line number that *frame* is currently executing. diff --git a/Doc/whatsnew/3.9.rst b/Doc/whatsnew/3.9.rst index 8b8aa9a514c..e3751fa1680 100644 --- a/Doc/whatsnew/3.9.rst +++ b/Doc/whatsnew/3.9.rst @@ -537,6 +537,10 @@ Optimizations Build and C API Changes ======================= +* New :c:func:`PyFrame_GetCode` function: return a borrowed reference to the + frame code. + (Contributed by Victor Stinner in :issue:`40421`.) + * Add :c:func:`PyFrame_GetLineNumber` to the limited C API. (Contributed by Victor Stinner in :issue:`40421`.) diff --git a/Include/pyframe.h b/Include/pyframe.h index d3404cde4a1..3816224201c 100644 --- a/Include/pyframe.h +++ b/Include/pyframe.h @@ -14,6 +14,8 @@ typedef struct _frame PyFrameObject; /* Return the line of code the frame is currently executing. */ PyAPI_FUNC(int) PyFrame_GetLineNumber(PyFrameObject *); +PyAPI_FUNC(PyCodeObject *) PyFrame_GetCode(PyFrameObject *frame); + #ifdef __cplusplus } #endif diff --git a/Misc/NEWS.d/next/C API/2020-04-28-15-47-58.bpo-40421.ZIzOV0.rst b/Misc/NEWS.d/next/C API/2020-04-28-15-47-58.bpo-40421.ZIzOV0.rst new file mode 100644 index 00000000000..11cf87872d5 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-04-28-15-47-58.bpo-40421.ZIzOV0.rst @@ -0,0 +1,2 @@ +New :c:func:`PyFrame_GetCode` function: return a borrowed reference to the +frame code. diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c index 7115fee1f2e..39cf6e126d6 100644 --- a/Modules/_lsprof.c +++ b/Modules/_lsprof.c @@ -1,5 +1,4 @@ #include "Python.h" -#include "frameobject.h" #include "rotatingtree.h" /************************************************************/ @@ -388,14 +387,16 @@ profiler_callback(PyObject *self, PyFrameObject *frame, int what, /* the 'frame' of a called function is about to start its execution */ case PyTrace_CALL: - ptrace_enter_call(self, (void *)frame->f_code, - (PyObject *)frame->f_code); + { + PyCodeObject *code = PyFrame_GetCode(frame); + ptrace_enter_call(self, (void *)code, (PyObject *)code); break; + } /* the 'frame' of a called function is about to finish (either normally or with an exception) */ case PyTrace_RETURN: - ptrace_leave_call(self, (void *)frame->f_code); + ptrace_leave_call(self, (void *)PyFrame_GetCode(frame)); break; /* case PyTrace_EXCEPTION: diff --git a/Modules/_tracemalloc.c b/Modules/_tracemalloc.c index dbae107c2da..3593baee512 100644 --- a/Modules/_tracemalloc.c +++ b/Modules/_tracemalloc.c @@ -346,7 +346,7 @@ tracemalloc_get_frame(PyFrameObject *pyframe, frame_t *frame) lineno = 0; frame->lineno = (unsigned int)lineno; - code = pyframe->f_code; + code = PyFrame_GetCode(pyframe); if (code == NULL) { #ifdef TRACE_DEBUG tracemalloc_error("failed to get the code object of the frame"); diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d0a15e77512..92206c5f521 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1222,3 +1222,10 @@ _PyFrame_DebugMallocStats(FILE *out) numfree, sizeof(PyFrameObject)); } + +PyCodeObject * +PyFrame_GetCode(PyFrameObject *frame) +{ + assert(frame != NULL); + return frame->f_code; +} diff --git a/Objects/typeobject.c b/Objects/typeobject.c index bf95dd604e5..9d97f389401 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -8033,13 +8033,13 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds) PyFrameObject *f; PyCodeObject *co; Py_ssize_t i, n; - f = _PyThreadState_GET()->frame; + f = PyThreadState_GetFrame(_PyThreadState_GET()); if (f == NULL) { PyErr_SetString(PyExc_RuntimeError, "super(): no current frame"); return -1; } - co = f->f_code; + co = PyFrame_GetCode(f); if (co == NULL) { PyErr_SetString(PyExc_RuntimeError, "super(): no code object"); diff --git a/Python/_warnings.c b/Python/_warnings.c index f4ef0bb4b12..91c611c2573 100644 --- a/Python/_warnings.c +++ b/Python/_warnings.c @@ -762,7 +762,6 @@ is_internal_frame(PyFrameObject *frame) { static PyObject *importlib_string = NULL; static PyObject *bootstrap_string = NULL; - PyObject *filename; int contains; if (importlib_string == NULL) { @@ -780,14 +779,23 @@ is_internal_frame(PyFrameObject *frame) Py_INCREF(bootstrap_string); } - if (frame == NULL || frame->f_code == NULL || - frame->f_code->co_filename == NULL) { + if (frame == NULL) { + return 0; + } + + PyCodeObject *code = PyFrame_GetCode(frame); + if (code == NULL) { + return 0; + } + + PyObject *filename = code->co_filename; + if (filename == NULL) { return 0; } - filename = frame->f_code->co_filename; if (!PyUnicode_Check(filename)) { return 0; } + contains = PyUnicode_Contains(filename, importlib_string); if (contains < 0) { return 0; @@ -846,7 +854,7 @@ setup_context(Py_ssize_t stack_level, PyObject **filename, int *lineno, } else { globals = f->f_globals; - *filename = f->f_code->co_filename; + *filename = PyFrame_GetCode(f)->co_filename; Py_INCREF(*filename); *lineno = PyFrame_GetLineNumber(f); } diff --git a/Python/import.c b/Python/import.c index a8743458dd5..9142ebba40d 100644 --- a/Python/import.c +++ b/Python/import.c @@ -15,7 +15,6 @@ #include "errcode.h" #include "marshal.h" #include "code.h" -#include "frameobject.h" #include "importdl.h" #include "pydtrace.h" @@ -1536,7 +1535,7 @@ remove_importlib_frames(PyThreadState *tstate) PyTracebackObject *traceback = (PyTracebackObject *)tb; PyObject *next = (PyObject *) traceback->tb_next; PyFrameObject *frame = traceback->tb_frame; - PyCodeObject *code = frame->f_code; + PyCodeObject *code = PyFrame_GetCode(frame); int now_in_importlib; assert(PyTraceBack_Check(tb)); diff --git a/Python/traceback.c b/Python/traceback.c index 85e9124bb6a..1ea6cbada96 100644 --- a/Python/traceback.c +++ b/Python/traceback.c @@ -560,24 +560,23 @@ tb_printinternal(PyTracebackObject *tb, PyObject *f, long limit) tb = tb->tb_next; } while (tb != NULL && err == 0) { + PyCodeObject *code = PyFrame_GetCode(tb->tb_frame); if (last_file == NULL || - tb->tb_frame->f_code->co_filename != last_file || + code->co_filename != last_file || last_line == -1 || tb->tb_lineno != last_line || - last_name == NULL || tb->tb_frame->f_code->co_name != last_name) { + last_name == NULL || code->co_name != last_name) { if (cnt > TB_RECURSIVE_CUTOFF) { err = tb_print_line_repeated(f, cnt); } - last_file = tb->tb_frame->f_code->co_filename; + last_file = code->co_filename; last_line = tb->tb_lineno; - last_name = tb->tb_frame->f_code->co_name; + last_name = code->co_name; cnt = 0; } cnt++; if (err == 0 && cnt <= TB_RECURSIVE_CUTOFF) { - err = tb_displayline(f, - tb->tb_frame->f_code->co_filename, - tb->tb_lineno, - tb->tb_frame->f_code->co_name); + err = tb_displayline(f, code->co_filename, tb->tb_lineno, + code->co_name); if (err == 0) { err = PyErr_CheckSignals(); } @@ -756,7 +755,7 @@ dump_frame(int fd, PyFrameObject *frame) PyCodeObject *code; int lineno; - code = frame->f_code; + code = PyFrame_GetCode(frame); PUTS(fd, " File "); if (code != NULL && code->co_filename != NULL && PyUnicode_Check(code->co_filename))