Skip to content

Commit f292635

Browse files
gh-94936: C getters: co_varnames, co_cellvars, co_freevars (GH-95008)
(cherry picked from commit 42b102b) Co-authored-by: Ken Jin <[email protected]>
1 parent c521c5c commit f292635

File tree

6 files changed

+128
-15
lines changed

6 files changed

+128
-15
lines changed

Doc/c-api/code.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,28 @@ bound into a function.
9090
9191
.. versionadded:: 3.11
9292
93+
.. c:function:: PyObject* PyCode_GetVarnames(PyCodeObject *co)
94+
95+
Equivalent to the Python code ``getattr(co, 'co_varnames')``.
96+
Returns a new reference to a :c:type:`PyTupleObject` containing the names of
97+
the local variables. On error, ``NULL`` is returned and an exception
98+
is raised.
99+
100+
.. versionadded:: 3.11
101+
102+
.. c:function:: PyObject* PyCode_GetCellvars(PyCodeObject *co)
103+
104+
Equivalent to the Python code ``getattr(co, 'co_cellvars')``.
105+
Returns a new reference to a :c:type:`PyTupleObject` containing the names of
106+
the local variables that are referenced by nested functions. On error, ``NULL``
107+
is returned and an exception is raised.
108+
109+
.. versionadded:: 3.11
110+
111+
.. c:function:: PyObject* PyCode_GetFreevars(PyCodeObject *co)
112+
113+
Equivalent to the Python code ``getattr(co, 'co_freevars')``.
114+
Returns a new reference to a :c:type:`PyTupleObject` containing the names of
115+
the free variables. On error, ``NULL`` is returned and an exception is raised.
116+
117+
.. versionadded:: 3.11

Doc/whatsnew/3.11.rst

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,10 +1724,13 @@ Porting to Python 3.11
17241724
To get a custom code object: create a code object using the compiler,
17251725
then get a modified version with the ``replace`` method.
17261726

1727-
* :c:type:`PyCodeObject` no longer has a ``co_code`` field. Instead,
1728-
use ``PyObject_GetAttrString(code_object, "co_code")`` or
1729-
:c:func:`PyCode_GetCode` to get the underlying bytes object.
1730-
(Contributed by Brandt Bucher in :issue:`46841` and Ken Jin in :gh:`92154`.)
1727+
* :c:type:`PyCodeObject` no longer has the ``co_code``, ``co_varnames``,
1728+
``co_cellvars`` and ``co_freevars`` fields. Instead, use
1729+
:c:func:`PyCode_GetCode`, :c:func:`PyCode_GetVarnames`,
1730+
:c:func:`PyCode_GetCellvars` and :c:func:`PyCode_GetFreevars` respectively
1731+
to access them via the C API.
1732+
(Contributed by Brandt Bucher in :issue:`46841` and Ken Jin in :gh:`92154`
1733+
and :gh:`94936`.)
17311734

17321735
* The old trashcan macros (``Py_TRASHCAN_SAFE_BEGIN``/``Py_TRASHCAN_SAFE_END``)
17331736
are now deprecated. They should be replaced by the new macros

Include/cpython/code.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,12 @@ PyAPI_FUNC(int) _PyCode_SetExtra(PyObject *code, Py_ssize_t index,
209209
/* Equivalent to getattr(code, 'co_code') in Python.
210210
Returns a strong reference to a bytes object. */
211211
PyAPI_FUNC(PyObject *) PyCode_GetCode(PyCodeObject *code);
212+
/* Equivalent to getattr(code, 'co_varnames') in Python. */
213+
PyAPI_FUNC(PyObject *) PyCode_GetVarnames(PyCodeObject *code);
214+
/* Equivalent to getattr(code, 'co_cellvars') in Python. */
215+
PyAPI_FUNC(PyObject *) PyCode_GetCellvars(PyCodeObject *code);
216+
/* Equivalent to getattr(code, 'co_freevars') in Python. */
217+
PyAPI_FUNC(PyObject *) PyCode_GetFreevars(PyCodeObject *code);
212218

213219
typedef enum _PyCodeLocationInfoKind {
214220
/* short forms are 0 to 9 */
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Added :c:func:`PyCode_GetVarnames`, :c:func:`PyCode_GetCellvars` and
2+
:c:func:`PyCode_GetFreevars` for accessing ``co_varnames``, ``co_cellvars``
3+
and ``co_freevars`` respectively via the C API.

Modules/_testcapimodule.c

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5952,21 +5952,79 @@ test_code_api(PyObject *self, PyObject *Py_UNUSED(args))
59525952
if (co == NULL) {
59535953
return NULL;
59545954
}
5955-
PyObject *co_code = PyCode_GetCode(co);
5956-
if (co_code == NULL) {
5957-
Py_DECREF(co);
5958-
return NULL;
5959-
}
5960-
assert(PyBytes_CheckExact(co_code));
5961-
if (PyObject_Length(co_code) == 0) {
5962-
PyErr_SetString(PyExc_ValueError, "empty co_code");
5963-
Py_DECREF(co);
5955+
/* co_code */
5956+
{
5957+
PyObject *co_code = PyCode_GetCode(co);
5958+
if (co_code == NULL) {
5959+
goto fail;
5960+
}
5961+
assert(PyBytes_CheckExact(co_code));
5962+
if (PyObject_Length(co_code) == 0) {
5963+
PyErr_SetString(PyExc_ValueError, "empty co_code");
5964+
Py_DECREF(co_code);
5965+
goto fail;
5966+
}
59645967
Py_DECREF(co_code);
5965-
return NULL;
5968+
}
5969+
/* co_varnames */
5970+
{
5971+
PyObject *co_varnames = PyCode_GetVarnames(co);
5972+
if (co_varnames == NULL) {
5973+
goto fail;
5974+
}
5975+
if (!PyTuple_CheckExact(co_varnames)) {
5976+
PyErr_SetString(PyExc_TypeError, "co_varnames not tuple");
5977+
Py_DECREF(co_varnames);
5978+
goto fail;
5979+
}
5980+
if (PyTuple_GET_SIZE(co_varnames) != 0) {
5981+
PyErr_SetString(PyExc_ValueError, "non-empty co_varnames");
5982+
Py_DECREF(co_varnames);
5983+
goto fail;
5984+
}
5985+
Py_DECREF(co_varnames);
5986+
}
5987+
/* co_cellvars */
5988+
{
5989+
PyObject *co_cellvars = PyCode_GetCellvars(co);
5990+
if (co_cellvars == NULL) {
5991+
goto fail;
5992+
}
5993+
if (!PyTuple_CheckExact(co_cellvars)) {
5994+
PyErr_SetString(PyExc_TypeError, "co_cellvars not tuple");
5995+
Py_DECREF(co_cellvars);
5996+
goto fail;
5997+
}
5998+
if (PyTuple_GET_SIZE(co_cellvars) != 0) {
5999+
PyErr_SetString(PyExc_ValueError, "non-empty co_cellvars");
6000+
Py_DECREF(co_cellvars);
6001+
goto fail;
6002+
}
6003+
Py_DECREF(co_cellvars);
6004+
}
6005+
/* co_freevars */
6006+
{
6007+
PyObject *co_freevars = PyCode_GetFreevars(co);
6008+
if (co_freevars == NULL) {
6009+
goto fail;
6010+
}
6011+
if (!PyTuple_CheckExact(co_freevars)) {
6012+
PyErr_SetString(PyExc_TypeError, "co_freevars not tuple");
6013+
Py_DECREF(co_freevars);
6014+
goto fail;
6015+
}
6016+
if (PyTuple_GET_SIZE(co_freevars) != 0) {
6017+
PyErr_SetString(PyExc_ValueError, "non-empty co_freevars");
6018+
Py_DECREF(co_freevars);
6019+
goto fail;
6020+
}
6021+
Py_DECREF(co_freevars);
59666022
}
59676023
Py_DECREF(co);
5968-
Py_DECREF(co_code);
59696024
Py_RETURN_NONE;
6025+
fail:
6026+
Py_DECREF(co);
6027+
return NULL;
59706028
}
59716029

59726030
static int

Objects/codeobject.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,18 +1399,36 @@ _PyCode_GetVarnames(PyCodeObject *co)
13991399
return get_localsplus_names(co, CO_FAST_LOCAL, co->co_nlocals);
14001400
}
14011401

1402+
PyObject *
1403+
PyCode_GetVarnames(PyCodeObject *code)
1404+
{
1405+
return _PyCode_GetVarnames(code);
1406+
}
1407+
14021408
PyObject *
14031409
_PyCode_GetCellvars(PyCodeObject *co)
14041410
{
14051411
return get_localsplus_names(co, CO_FAST_CELL, co->co_ncellvars);
14061412
}
14071413

1414+
PyObject *
1415+
PyCode_GetCellvars(PyCodeObject *code)
1416+
{
1417+
return _PyCode_GetCellvars(code);
1418+
}
1419+
14081420
PyObject *
14091421
_PyCode_GetFreevars(PyCodeObject *co)
14101422
{
14111423
return get_localsplus_names(co, CO_FAST_FREE, co->co_nfreevars);
14121424
}
14131425

1426+
PyObject *
1427+
PyCode_GetFreevars(PyCodeObject *code)
1428+
{
1429+
return _PyCode_GetFreevars(code);
1430+
}
1431+
14141432
static void
14151433
deopt_code(_Py_CODEUNIT *instructions, Py_ssize_t len)
14161434
{

0 commit comments

Comments
 (0)