From 9bcf13af5c31380af9b5d55a3df8deca7b80f514 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 5 May 2023 01:25:33 +0100 Subject: [PATCH 1/7] Document new unstable internal frame API functions --- Doc/c-api/frame.rst | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 1ac8f03d6e48f8..f62e3298c20b6f 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -130,3 +130,38 @@ See also :ref:`Reflection `. .. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame) Return the line number that *frame* is currently executing. + + + +Internal Frames +--------------- + +Unless using PEP 523, you will not need this. + +.. c:type:: struct _PyInterpreterFrame + + This C structure for internal frames. + + +.. c:function:: PyCodeObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame); + + Get the code object for the frame + Returns a :term:`strong reference`. + + .. versionadded:: 3.12 + + +.. c:function:: int PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame); + + Gets the byte offset into the last executed instruction. + + .. versionadded:: 3.12 + + +.. c:function:: int PyUnstable_InterpreterFrame_GetLine(struct _PyInterpreterFrame *frame); + + Gets the currently executing line number, or -1 if there is no line number. + + .. versionadded:: 3.12 + + From 1e7a1a00e06962c5955f9b164b26934a5cea2ee6 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Sat, 6 May 2023 11:04:22 +0100 Subject: [PATCH 2/7] Apply suggestions from code review Co-authored-by: Carl Meyer --- Doc/c-api/frame.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index f62e3298c20b6f..d37606642cb344 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -140,27 +140,26 @@ Unless using PEP 523, you will not need this. .. c:type:: struct _PyInterpreterFrame - This C structure for internal frames. + The interpreter's internal frame representation. .. c:function:: PyCodeObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame); - Get the code object for the frame - Returns a :term:`strong reference`. + Return a :term:`strong reference` to the code object for the frame. .. versionadded:: 3.12 .. c:function:: int PyUnstable_InterpreterFrame_GetLasti(struct _PyInterpreterFrame *frame); - Gets the byte offset into the last executed instruction. + Return the byte offset into the last executed instruction. .. versionadded:: 3.12 .. c:function:: int PyUnstable_InterpreterFrame_GetLine(struct _PyInterpreterFrame *frame); - Gets the currently executing line number, or -1 if there is no line number. + Return the currently executing line number, or -1 if there is no line number. .. versionadded:: 3.12 From c144a623edeb62dd953779ca5b7f97768d2fe660 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 5 May 2023 17:02:29 +0100 Subject: [PATCH 3/7] Weaken contract of PyUnstable_InterpreterFrame_GetCode to return PyObject*. --- Doc/c-api/frame.rst | 2 +- Include/cpython/frameobject.h | 2 +- Python/frame.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index d37606642cb344..264f9670fe50d1 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -143,7 +143,7 @@ Unless using PEP 523, you will not need this. The interpreter's internal frame representation. -.. c:function:: PyCodeObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame); +.. c:function:: PyObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame); Return a :term:`strong reference` to the code object for the frame. diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 6f3efe36ede5d8..a3dc6661786451 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -35,7 +35,7 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); /* Returns the code object of the frame (strong reference). * Does not raise an exception. */ -PyAPI_FUNC(PyCodeObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame); +PyAPI_FUNC(PyObject *) PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame); /* Returns a byte ofsset into the last executed instruction. * Does not raise an exception. */ diff --git a/Python/frame.c b/Python/frame.c index d792b92fa57560..b84fd9b6a9380a 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -146,10 +146,10 @@ _PyFrame_ClearExceptCode(_PyInterpreterFrame *frame) /* Unstable API functions */ -PyCodeObject * +PyObject * PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame) { - PyCodeObject *code = frame->f_code; + PyObject *code = (PyObject *)frame->f_code; Py_INCREF(code); return code; } From dea0300433b96ea8a3db4136bbd436b2f435c8fe Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 5 May 2023 17:35:15 +0100 Subject: [PATCH 4/7] Add tests --- Lib/test/test_capi/test_misc.py | 24 ++++++++++++++++++++++ Modules/_testinternalcapi.c | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 3fc2c07f933061..2c4501aa517ed3 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -1744,6 +1744,30 @@ class Subclass(BaseException, self.module.StateAccessType): self.assertIs(Subclass().get_defining_module(), self.module) +class TestInternalFrameApi(unittest.TestCase): + + @staticmethod + def func(): + return sys._getframe() + + def test_code(self): + frame = self.func() + code = _testinternalcapi.iframe_getcode(frame) + self.assertIs(code, self.func.__code__) + + def test_lasti(self): + frame = self.func() + lasti = _testinternalcapi.iframe_getlasti(frame) + self.assertGreater(lasti, 0) + self.assertLess(lasti, len(self.func.__code__.co_code)) + + def test_line(self): + frame = self.func() + line = _testinternalcapi.iframe_getline(frame) + firstline = self.func.__code__.co_firstlineno + self.assertEqual(line, firstline + 2) + + SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100 class Test_Pep523API(unittest.TestCase): diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 40ad6f88868daf..49fe8c80b06b13 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -12,6 +12,7 @@ #define PY_SSIZE_T_CLEAN #include "Python.h" +#include "frameobject.h" #include "pycore_atomic_funcs.h" // _Py_atomic_int_get() #include "pycore_bitutils.h" // _Py_bswap32() #include "pycore_compile.h" // _PyCompile_CodeGen, _PyCompile_OptimizeCfg, _PyCompile_Assemble @@ -755,6 +756,38 @@ clear_extension(PyObject *self, PyObject *args) Py_RETURN_NONE; } +static PyObject * +iframe_getcode(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame; + return PyUnstable_InterpreterFrame_GetCode(f); +} + +static PyObject * +iframe_getline(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame; + return PyLong_FromLong(PyUnstable_InterpreterFrame_GetLine(f)); +} + +static PyObject * +iframe_getlasti(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + struct _PyInterpreterFrame *f = ((PyFrameObject *)frame)->f_frame; + return PyLong_FromLong(PyUnstable_InterpreterFrame_GetLasti(f)); +} static PyMethodDef module_functions[] = { {"get_configs", get_configs, METH_NOARGS}, @@ -779,6 +812,9 @@ static PyMethodDef module_functions[] = { _TESTINTERNALCAPI_ASSEMBLE_CODE_OBJECT_METHODDEF {"get_interp_settings", get_interp_settings, METH_VARARGS, NULL}, {"clear_extension", clear_extension, METH_VARARGS, NULL}, + {"iframe_getcode", iframe_getcode, METH_O, NULL}, + {"iframe_getline", iframe_getline, METH_O, NULL}, + {"iframe_getlasti", iframe_getlasti, METH_O, NULL}, {NULL, NULL} /* sentinel */ }; From 2a82445616a91ff6bdaa4cf21d27144c56c2be97 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 5 May 2023 17:37:20 +0100 Subject: [PATCH 5/7] Use correct sphinx tag --- Doc/c-api/frame.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 264f9670fe50d1..67dd571734916d 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -138,7 +138,7 @@ Internal Frames Unless using PEP 523, you will not need this. -.. c:type:: struct _PyInterpreterFrame +.. c:struct:: _PyInterpreterFrame The interpreter's internal frame representation. From 86865b5b1dd8152bfc876bca23b0ebb4bd4996e0 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 16 May 2023 10:39:05 +0100 Subject: [PATCH 6/7] Apply suggestions from code review Co-authored-by: Carl Meyer --- Doc/c-api/frame.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 67dd571734916d..d0d43fd56fa472 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -136,12 +136,13 @@ See also :ref:`Reflection `. Internal Frames --------------- -Unless using PEP 523, you will not need this. +Unless using :pep:`523`, you will not need this. .. c:struct:: _PyInterpreterFrame The interpreter's internal frame representation. + .. versionadded:: 3.11 .. c:function:: PyObject* PyUnstable_InterpreterFrame_GetCode(struct _PyInterpreterFrame *frame); From 31e386cb27da0fa16493268639201f1252fb8b3e Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 16 May 2023 16:52:53 +0100 Subject: [PATCH 7/7] Update Doc/c-api/frame.rst Co-authored-by: Erlend E. Aasland --- Doc/c-api/frame.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index d0d43fd56fa472..9f7addfbbf3cb4 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -160,7 +160,7 @@ Unless using :pep:`523`, you will not need this. .. c:function:: int PyUnstable_InterpreterFrame_GetLine(struct _PyInterpreterFrame *frame); - Return the currently executing line number, or -1 if there is no line number. + Return the currently executing line number, or -1 if there is no line number. .. versionadded:: 3.12