diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 407d8b481be4ae..7915b81b463773 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -76,3 +76,17 @@ 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(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 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. + + .. versionadded:: 3.11 + diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 607f2e749bb5a4..c19f158f57a31a 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1412,6 +1412,11 @@ 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. + (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 be3b10bba724b8..ba7324b48d8675 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(PyCodeObject *code); typedef enum _PyCodeLocationInfoKind { /* short forms are 0 to 9 */ diff --git a/Misc/NEWS.d/next/C API/2022-05-03-20-08-35.gh-issue-92154.IqMcAJ.rst b/Misc/NEWS.d/next/C API/2022-05-03-20-08-35.gh-issue-92154.IqMcAJ.rst new file mode 100644 index 00000000000000..7713954fe72e9b --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-05-03-20-08-35.gh-issue-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')``. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 9073f33e226bd3..26d8d38f2f7954 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5931,6 +5931,29 @@ get_feature_macros(PyObject *self, PyObject *Py_UNUSED(args)) return result; } +static PyObject * +test_code_api(PyObject *self, PyObject *Py_UNUSED(args)) +{ + PyCodeObject *co = 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; +} static PyObject *negative_dictoffset(PyObject *, PyObject *); static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); @@ -6227,6 +6250,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..9f922dabeac63e 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1370,6 +1370,11 @@ _PyCode_GetCode(PyCodeObject *co) return code; } +PyObject * +PyCode_GetCode(PyCodeObject *co) +{ + return _PyCode_GetCode(co); +} /****************** * PyCode_Type