diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 7915b81b463773c2191436a24d970b99ce680d9e..d4a3c4ae35fa0927126dd230011f3116652367ec 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -90,3 +90,28 @@ bound into a function. .. versionadded:: 3.11 +.. c:function:: PyObject* PyCode_GetVarnames(PyCodeObject *co) + + Equivalent to the Python code ``getattr(co, 'co_varnames')``. + Returns a new reference to a :c:type:`PyTupleObject` containing the names of + the local variables. On error, ``NULL`` is returned and an exception + is raised. + + .. versionadded:: 3.11 + +.. c:function:: PyObject* PyCode_GetCellvars(PyCodeObject *co) + + Equivalent to the Python code ``getattr(co, 'co_cellvars')``. + Returns a new reference to a :c:type:`PyTupleObject` containing the names of + the local variables that are referenced by nested functions. On error, ``NULL`` + is returned and an exception is raised. + + .. versionadded:: 3.11 + +.. c:function:: PyObject* PyCode_GetFreevars(PyCodeObject *co) + + Equivalent to the Python code ``getattr(co, 'co_freevars')``. + Returns a new reference to a :c:type:`PyTupleObject` containing the names of + the free variables. On error, ``NULL`` is returned and an exception is raised. + + .. versionadded:: 3.11 diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 1067982c3dc361c2c32f8eda1741dcf04d33c8c6..c57f8a0f3e9fb9ea2caea44c8c927c7068bd0d70 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -1724,10 +1724,13 @@ Porting to Python 3.11 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`.) +* :c:type:`PyCodeObject` no longer has the ``co_code``, ``co_varnames``, + ``co_cellvars`` and ``co_freevars`` fields. Instead, use + :c:func:`PyCode_GetCode`, :c:func:`PyCode_GetVarnames`, + :c:func:`PyCode_GetCellvars` and :c:func:`PyCode_GetFreevars` respectively + to access them via the C API. + (Contributed by Brandt Bucher in :issue:`46841` and Ken Jin in :gh:`92154` + and :gh:`94936`.) * The old trashcan macros (``Py_TRASHCAN_SAFE_BEGIN``/``Py_TRASHCAN_SAFE_END``) are now deprecated. They should be replaced by the new macros diff --git a/Include/cpython/code.h b/Include/cpython/code.h index ef8c6422046dc784a9fb3ffd45a6ae94b11c3a2d..7006060cc760e5ca1468965cb1fc1252d5be9ae0 100644 --- a/Include/cpython/code.h +++ b/Include/cpython/code.h @@ -209,6 +209,12 @@ 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(PyCodeObject *code); +/* Equivalent to getattr(code, 'co_varnames') in Python. */ +PyAPI_FUNC(PyObject *) PyCode_GetVarnames(PyCodeObject *code); +/* Equivalent to getattr(code, 'co_cellvars') in Python. */ +PyAPI_FUNC(PyObject *) PyCode_GetCellvars(PyCodeObject *code); +/* Equivalent to getattr(code, 'co_freevars') in Python. */ +PyAPI_FUNC(PyObject *) PyCode_GetFreevars(PyCodeObject *code); typedef enum _PyCodeLocationInfoKind { /* short forms are 0 to 9 */ diff --git a/Misc/NEWS.d/next/C API/2022-07-19-22-37-40.gh-issue-94936.LGlmKv.rst b/Misc/NEWS.d/next/C API/2022-07-19-22-37-40.gh-issue-94936.LGlmKv.rst new file mode 100644 index 0000000000000000000000000000000000000000..abef9bb376c8959ca3a595f468e43134ec91fe45 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-07-19-22-37-40.gh-issue-94936.LGlmKv.rst @@ -0,0 +1,3 @@ +Added :c:func:`PyCode_GetVarnames`, :c:func:`PyCode_GetCellvars` and +:c:func:`PyCode_GetFreevars` for accessing ``co_varnames``, ``co_cellvars`` +and ``co_freevars`` respectively via the C API. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index b0a4687a973d9e914c2bc034f69a7e8fc48520f2..961616dae63e19758c4832b7af4be1eb6d6d8de3 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5952,21 +5952,79 @@ test_code_api(PyObject *self, PyObject *Py_UNUSED(args)) 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); + /* co_code */ + { + PyObject *co_code = PyCode_GetCode(co); + if (co_code == NULL) { + goto fail; + } + assert(PyBytes_CheckExact(co_code)); + if (PyObject_Length(co_code) == 0) { + PyErr_SetString(PyExc_ValueError, "empty co_code"); + Py_DECREF(co_code); + goto fail; + } Py_DECREF(co_code); - return NULL; + } + /* co_varnames */ + { + PyObject *co_varnames = PyCode_GetVarnames(co); + if (co_varnames == NULL) { + goto fail; + } + if (!PyTuple_CheckExact(co_varnames)) { + PyErr_SetString(PyExc_TypeError, "co_varnames not tuple"); + Py_DECREF(co_varnames); + goto fail; + } + if (PyTuple_GET_SIZE(co_varnames) != 0) { + PyErr_SetString(PyExc_ValueError, "non-empty co_varnames"); + Py_DECREF(co_varnames); + goto fail; + } + Py_DECREF(co_varnames); + } + /* co_cellvars */ + { + PyObject *co_cellvars = PyCode_GetCellvars(co); + if (co_cellvars == NULL) { + goto fail; + } + if (!PyTuple_CheckExact(co_cellvars)) { + PyErr_SetString(PyExc_TypeError, "co_cellvars not tuple"); + Py_DECREF(co_cellvars); + goto fail; + } + if (PyTuple_GET_SIZE(co_cellvars) != 0) { + PyErr_SetString(PyExc_ValueError, "non-empty co_cellvars"); + Py_DECREF(co_cellvars); + goto fail; + } + Py_DECREF(co_cellvars); + } + /* co_freevars */ + { + PyObject *co_freevars = PyCode_GetFreevars(co); + if (co_freevars == NULL) { + goto fail; + } + if (!PyTuple_CheckExact(co_freevars)) { + PyErr_SetString(PyExc_TypeError, "co_freevars not tuple"); + Py_DECREF(co_freevars); + goto fail; + } + if (PyTuple_GET_SIZE(co_freevars) != 0) { + PyErr_SetString(PyExc_ValueError, "non-empty co_freevars"); + Py_DECREF(co_freevars); + goto fail; + } + Py_DECREF(co_freevars); } Py_DECREF(co); - Py_DECREF(co_code); Py_RETURN_NONE; +fail: + Py_DECREF(co); + return NULL; } static int diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 970aa6116bfab8a43b6fdbf28509083e2dee3a58..c0151434489ad1750ac2f1e4f2666d02fe5596c7 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -1399,18 +1399,36 @@ _PyCode_GetVarnames(PyCodeObject *co) return get_localsplus_names(co, CO_FAST_LOCAL, co->co_nlocals); } +PyObject * +PyCode_GetVarnames(PyCodeObject *code) +{ + return _PyCode_GetVarnames(code); +} + PyObject * _PyCode_GetCellvars(PyCodeObject *co) { return get_localsplus_names(co, CO_FAST_CELL, co->co_ncellvars); } +PyObject * +PyCode_GetCellvars(PyCodeObject *code) +{ + return _PyCode_GetCellvars(code); +} + PyObject * _PyCode_GetFreevars(PyCodeObject *co) { return get_localsplus_names(co, CO_FAST_FREE, co->co_nfreevars); } +PyObject * +PyCode_GetFreevars(PyCodeObject *code) +{ + return _PyCode_GetFreevars(code); +} + static void deopt_code(_Py_CODEUNIT *instructions, Py_ssize_t len) {