From 333a6428abe6b9eb495687492bbcfef3ba89c153 Mon Sep 17 00:00:00 2001 From: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> Date: Thu, 4 Aug 2022 21:53:31 +0800 Subject: [PATCH] gh-94936: C getters: co_varnames, co_cellvars, co_freevars (GH-95008) (cherry picked from commit 42b102bbf9a9ae6fae8f6710202fb7afeeac277c) Co-authored-by: Ken Jin <28750310+Fidget-Spinner@users.noreply.github.com> --- Doc/c-api/code.rst | 25 ++++++ Doc/whatsnew/3.11.rst | 11 ++- Include/cpython/code.h | 6 ++ ...2-07-19-22-37-40.gh-issue-94936.LGlmKv.rst | 3 + Modules/_testcapimodule.c | 80 ++++++++++++++++--- Objects/codeobject.c | 18 +++++ 6 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2022-07-19-22-37-40.gh-issue-94936.LGlmKv.rst diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 7915b81b463773..d4a3c4ae35fa09 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 1067982c3dc361..c57f8a0f3e9fb9 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 ef8c6422046dc7..7006060cc760e5 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 00000000000000..abef9bb376c895 --- /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 b0a4687a973d9e..961616dae63e19 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 970aa6116bfab8..c0151434489ad1 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) {