From ba149a9a03e97607bcc1fcbd31cc1d8646fa82a3 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 3 May 2022 01:48:12 +0800 Subject: [PATCH 1/5] Expose PyCode_GetCode --- Doc/c-api/code.rst | 13 +++++++++++++ Doc/whatsnew/3.11.rst | 4 ++++ Include/cpython/code.h | 3 +++ Modules/_testcapimodule.c | 19 +++++++++++++++++++ Objects/codeobject.c | 9 +++++++++ 5 files changed, 48 insertions(+) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 407d8b481be4ae..d782eab4531fdd 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -76,3 +76,16 @@ bound into a function. information is not available for any particular element. Returns ``1`` if the function succeeds and 0 otherwise. + +.. c:function:: PyObject *PyCode_GetCode(PyObject *co) + + Equivalent to the Python code ``getattr(co, 'co_code')``. + Returns a strong reference to a :c:type:`PyBytesObject` representing the + bytecode in a code object. On error, a ``NULL`` is returned. + + This `PyBytesObject` may be created on-demand by the interpreter and does not + necessarily represent the bytecode actually executed by CPython. The primary + use case for this function is debuggers and profilers. + + .. versionadded:: 3.11 + diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 0a8ba1e8843e06..5c389941fb0315 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1408,6 +1408,10 @@ C API Changes To get a custom code object: create a code object using the compiler, then get a modified version with the ``replace`` method. +* :c:type:`PyCodeObject` no longer has a ``co_code`` field. Instead, + use ``PyObject_GetAttrString(code_object, 'co_code')`` or + :c:func:`PyCode_GetCode` to get the underlying bytes object. + New Features ------------ diff --git a/Include/cpython/code.h b/Include/cpython/code.h index be3b10bba724b8..442d368619a7da 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -202,6 +202,9 @@ PyAPI_FUNC(int) _PyCode_GetExtra(PyObject *code, Py_ssize_t index, PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, void *extra); +/* Equivalent to getattr(code, 'co_code') in Python. + Returns a strong reference to a bytes object. */ +PyAPI_FUNC(PyObject *) PyCode_GetCode(PyObject *code); typedef enum _PyCodeLocationInfoKind { /* short forms are 0 to 9 */ diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 9073f33e226bd3..a1694e1d9c32ad 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5931,6 +5931,24 @@ get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args)) return result; } +static PyObject * +test_code_api(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyObject *co = PyCode_NewEmpty("_testcapi", "dummy", 1); + if (co == NULL) { + return NULL; + } + PyObject *co_code = PyCode_GetCode(co); + if (co_code == NULL) { + return NULL; + } + assert(PyBytes_CheckExact(co_code)); + if (PyObject_Length(co_code) == 0) { + PyErr_SetString(PyExc_ValueError, "empty co_code"); + return NULL; + } + Py_RETURN_NONE; +} static PyObject *negative_dictoffset(PyObject *, PyObject *); static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); @@ -6227,6 +6245,7 @@ static PyMethodDef TestMethods[] = { {"frame_getbuiltins", frame_getbuiltins, METH_O, NULL}, {"frame_getlasti", frame_getlasti, METH_O, NULL}, {"get_feature_macros", get_feature_macros, METH_NOARGS, NULL}, + {"test_code_api", test_code_api, METH_NOARGS, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/codeobject.c b/Objects/codeobject.c index e3e4ca159a6329..f785511e1a299e 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1370,6 +1370,15 @@ _PyCode_GetCode(PyCodeObject *co) return code; } +PyObject * +PyCode_GetCode(PyObject *co) +{ + if (!PyCode_Check(co)) { + PyErr_BadInternalCall(); + return NULL; + } + return _PyCode_GetCode((PyCodeObject *)co); +} /****************** * PyCode_Type From d24ae35c4554105299ea4bde2a4714bd634c66e1 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 3 May 2022 01:50:18 +0800 Subject: [PATCH 2/5] Add news --- Misc/NEWS.d/next/C API/2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst diff --git a/Misc/NEWS.d/next/C API/2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst b/Misc/NEWS.d/next/C API/2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst new file mode 100644 index 00000000000000..7713954fe72e9b --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst @@ -0,0 +1,2 @@ +Added the :c:func:`PyCode_GetCode` function. This function does the +equivalent of the Python code ``getattr(code_object, 'co_code')``. From 6bc59400205709800f3cf60c16843982cd4566eb Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 3 May 2022 14:29:35 +0800 Subject: [PATCH 3/5] Add DECREFs and fix compiler warnings --- Modules/_testcapimodule.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index a1694e1d9c32ad..eba913d35f3cad 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5934,19 +5934,24 @@ get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * test_code_api(PyObject *self, PyObject *Py_UNUSED(args)) { - PyObject *co = PyCode_NewEmpty("_testcapi", "dummy", 1); + PyObject *co = (PyObject *)PyCode_NewEmpty("_testcapi", "dummy", 1); if (co == NULL) { return NULL; } PyObject *co_code = PyCode_GetCode(co); if (co_code == NULL) { + Py_DECREF(co); return NULL; } assert(PyBytes_CheckExact(co_code)); if (PyObject_Length(co_code) == 0) { PyErr_SetString(PyExc_ValueError, "empty co_code"); + Py_DECREF(co); + Py_DECREF(co_code); return NULL; } + Py_DECREF(co); + Py_DECREF(co_code); Py_RETURN_NONE; } From 8fdf0e81f9547a7b5b418fc73681bdf072a143a6 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 3 May 2022 15:09:08 +0800 Subject: [PATCH 4/5] fix docs formatting --- Doc/c-api/code.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index d782eab4531fdd..2b7af3acc57999 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -77,11 +77,11 @@ bound into a function. Returns ``1`` if the function succeeds and 0 otherwise. -.. c:function:: PyObject *PyCode_GetCode(PyObject *co) +.. c:function:: PyObject* PyCode_GetCode(PyObject *co) Equivalent to the Python code ``getattr(co, 'co_code')``. Returns a strong reference to a :c:type:`PyBytesObject` representing the - bytecode in a code object. On error, a ``NULL`` is returned. + bytecode in a code object. On error, ``NULL`` is returned. This `PyBytesObject` may be created on-demand by the interpreter and does not necessarily represent the bytecode actually executed by CPython. The primary From 87c2bbea7f8288cacc84c993334ba59da4ffcbd2 Mon Sep 17 00:00:00 2001 From: Fidget-Spinner <28750310+Fidget-Spinner@users.noreply.github.com> Date: Tue, 3 May 2022 20:15:37 +0800 Subject: [PATCH 5/5] Address code review by Victor --- Doc/c-api/code.rst | 11 ++++++----- Doc/whatsnew/3.11.rst | 3 ++- Include/cpython/code.h | 2 +- ... => 2022-05-03-20-08-35.gh-issue-92154.IqMcAJ.rst} | 0 Modules/_testcapimodule.c | 2 +- Objects/codeobject.c | 8 ++------ 6 files changed, 12 insertions(+), 14 deletions(-) rename Misc/NEWS.d/next/C API/{2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst => 2022-05-03-20-08-35.gh-issue-92154.IqMcAJ.rst} (100%) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 2b7af3acc57999..7915b81b463773 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -77,15 +77,16 @@ bound into a function. Returns ``1`` if the function succeeds and 0 otherwise. -.. c:function:: PyObject* PyCode_GetCode(PyObject *co) +.. c:function:: PyObject* PyCode_GetCode(PyCodeObject *co) Equivalent to the Python code ``getattr(co, 'co_code')``. Returns a strong reference to a :c:type:`PyBytesObject` representing the - bytecode in a code object. On error, ``NULL`` is returned. + bytecode in a code object. On error, ``NULL`` is returned and an exception + is raised. - This `PyBytesObject` may be created on-demand by the interpreter and does not - necessarily represent the bytecode actually executed by CPython. The primary - use case for this function is debuggers and profilers. + This ``PyBytesObject`` may be created on-demand by the interpreter and does + not necessarily represent the bytecode actually executed by CPython. The + primary use case for this function is debuggers and profilers. .. versionadded:: 3.11 diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 5c389941fb0315..a22473ef9b4ea6 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1409,8 +1409,9 @@ C API Changes then get a modified version with the ``replace`` method. * :c:type:`PyCodeObject` no longer has a ``co_code`` field. Instead, - use ``PyObject_GetAttrString(code_object, 'co_code')`` or + use ``PyObject_GetAttrString(code_object, "co_code")`` or :c:func:`PyCode_GetCode` to get the underlying bytes object. + (Contributed by Brandt Bucher in :issue:`46841` and Ken Jin in :gh:`92154`.) New Features ------------ diff --git a/Include/cpython/code.h b/Include/cpython/code.h index 442d368619a7da..ba7324b48d8675 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -204,7 +204,7 @@ PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index, /* Equivalent to getattr(code, 'co_code') in Python. Returns a strong reference to a bytes object. */ -PyAPI_FUNC(PyObject *) PyCode_GetCode(PyObject *code); +PyAPI_FUNC(PyObject *) PyCode_GetCode(PyCodeObject *code); typedef enum _PyCodeLocationInfoKind { /* short forms are 0 to 9 */ diff --git a/Misc/NEWS.d/next/C API/2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst b/Misc/NEWS.d/next/C API/2022-05-03-20-08-35.gh-issue-92154.IqMcAJ.rst similarity index 100% rename from Misc/NEWS.d/next/C API/2022-05-03-01-50-10.bpo-92154.IqMcAJ.rst rename to Misc/NEWS.d/next/C API/2022-05-03-20-08-35.gh-issue-92154.IqMcAJ.rst diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index eba913d35f3cad..26d8d38f2f7954 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5934,7 +5934,7 @@ get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * test_code_api(PyObject *self, PyObject *Py_UNUSED(args)) { - PyObject *co = (PyObject *)PyCode_NewEmpty("_testcapi", "dummy", 1); + PyCodeObject *co = PyCode_NewEmpty("_testcapi", "dummy", 1); if (co == NULL) { return NULL; } diff --git a/Objects/codeobject.c b/Objects/codeobject.c index f785511e1a299e..9f922dabeac63e 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1371,13 +1371,9 @@ _PyCode_GetCode(PyCodeObject *co) } PyObject * -PyCode_GetCode(PyObject *co) +PyCode_GetCode(PyCodeObject *co) { - if (!PyCode_Check(co)) { - PyErr_BadInternalCall(); - return NULL; - } - return _PyCode_GetCode((PyCodeObject *)co); + return _PyCode_GetCode(co); } /******************