]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-94936: C getters: co_varnames, co_cellvars, co_freevars (#95008)
authorKen Jin <28750310+Fidget-Spinner@users.noreply.github.com>
Thu, 4 Aug 2022 13:53:31 +0000 (21:53 +0800)
committerGitHub <noreply@github.com>
Thu, 4 Aug 2022 13:53:31 +0000 (06:53 -0700)
Doc/c-api/code.rst
Doc/whatsnew/3.11.rst
Include/cpython/code.h
Misc/NEWS.d/next/C API/2022-07-19-22-37-40.gh-issue-94936.LGlmKv.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/codeobject.c

index 7915b81b463773c2191436a24d970b99ce680d9e..d4a3c4ae35fa0927126dd230011f3116652367ec 100644 (file)
@@ -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
index 2db647bb4e4d8a60134ff940f3285a8a848337b7..c976eddb08ba0800a907dfda0173c73f5e8e3d32 100644 (file)
@@ -1719,10 +1719,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
index 595cd9e94f31a8a867537554b8aebc169322c188..7ce69022557af07544be6a9c88dcc2989efa9546 100644 (file)
@@ -210,6 +210,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 (file)
index 0000000..abef9bb
--- /dev/null
@@ -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.
index f56b36a15e7940d8a53707a6bb42ff8586699296..517591465b4914a707cd703af1c0a560b7928823 100644 (file)
@@ -5600,21 +5600,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
index 7ebbfdbdec18b38e96a5c775d799e3292dab194b..aeb6a8c0804e54fa5f94d5c4232aa61536da23f8 100644 (file)
@@ -1401,18 +1401,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)
 {