Skip to content

Commit cd8b975

Browse files
Add and document PyUnstable_Code_GetLocalPlusNames
1 parent 442d8cd commit cd8b975

6 files changed

Lines changed: 48 additions & 3 deletions

File tree

‎Doc/c-api/code.rst‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ bound into a function.
4444
The old name is deprecated, but will remain available until the
4545
signature changes again.
4646
47+
.. c:function:: PyObject* PyUnstable_Code_GetLocalPlusNames(PyCodeObject *co)
48+
49+
Return a new :term:`strong reference` to the tuple of names of the local,
50+
cell and free variables of a code object. The tuple is indexed like the
51+
*localsplus* array of a frame, so it can be used together with
52+
:c:func:`PyUnstable_InterpreterFrame_GetLocal`.
53+
54+
.. versionadded:: next
55+
4756
.. c:function:: PyCodeObject* PyUnstable_Code_New(int argcount, int kwonlyargcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, PyObject *qualname, int firstlineno, PyObject *linetable, PyObject *exceptiontable)
4857
4958
Return a new code object. If you need a dummy code object to create a frame,

‎Doc/whatsnew/3.16.rst‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,9 @@ New features
11781178
(Contributed by Victor Stinner in :gh:`148233`.)
11791179

11801180
* Add :c:func:`PyUnstable_InterpreterFrame_GetLocal` to read a local variable
1181-
of an internal interpreter frame by its localsplus index.
1181+
of an internal interpreter frame by its localsplus index, and
1182+
:c:func:`PyUnstable_Code_GetLocalPlusNames` to get the matching variable
1183+
names.
11821184
(Contributed by Guilherme Leobas in :gh:`156133`.)
11831185

11841186
Porting to Python 3.16

‎Include/cpython/code.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ static inline int PyUnstable_Code_GetFirstFree(PyCodeObject *op) {
177177
return op->co_nlocalsplus - op->co_nfreevars;
178178
}
179179

180+
static inline PyObject *PyUnstable_Code_GetLocalPlusNames(PyCodeObject *op) {
181+
assert(PyCode_Check(op));
182+
return Py_NewRef(op->co_localsplusnames);
183+
}
184+
180185
Py_DEPRECATED(3.13) static inline int PyCode_GetFirstFree(PyCodeObject *op) {
181186
return PyUnstable_Code_GetFirstFree(op);
182187
}

‎Lib/test/test_capi/test_misc.py‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2837,6 +2837,30 @@ def inner():
28372837
self.assertEqual(d['z'], 7)
28382838

28392839

2840+
def test_code_get_localsplus_names(self):
2841+
def outer(a, b):
2842+
c = a
2843+
def inner():
2844+
return a, d
2845+
d = b
2846+
return inner
2847+
2848+
names = _testinternalcapi.code_get_localsplus_names(outer.__code__)
2849+
self.assertIsInstance(names, tuple)
2850+
# Arguments and locals come first, followed by cells that are not
2851+
# arguments; here a and d are cells.
2852+
self.assertEqual(sorted(names), sorted(['a', 'b', 'c', 'd', 'inner']))
2853+
self.assertEqual(names[:2], ('a', 'b'))
2854+
2855+
inner = outer(1, 2)
2856+
names = _testinternalcapi.code_get_localsplus_names(inner.__code__)
2857+
self.assertEqual(names, ('a', 'd'))
2858+
self.assertEqual(names[inner.__code__.co_nlocals:], ('a', 'd'))
2859+
2860+
with self.assertRaises(TypeError):
2861+
_testinternalcapi.code_get_localsplus_names(None)
2862+
2863+
28402864
SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
28412865

28422866
class Test_Pep523API(unittest.TestCase):
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
Add :c:func:`PyUnstable_InterpreterFrame_GetLocal` to read a local variable of
2-
an internal interpreter frame by its localsplus index.
2+
an internal interpreter frame by its localsplus index, and
3+
:c:func:`PyUnstable_Code_GetLocalPlusNames` to get the matching variable names.

‎Modules/_testinternalcapi.c‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1553,24 +1553,28 @@ get_frame_locals(PyObject *self, PyObject *Py_UNUSED(ignored))
15531553
if (dict == NULL) {
15541554
return NULL;
15551555
}
1556+
PyObject *names = PyUnstable_Code_GetLocalPlusNames(co);
15561557
for (Py_ssize_t i = 0; i < n; i++) {
15571558
PyObject *value;
15581559
int rc = PyUnstable_InterpreterFrame_GetLocal(frame, i, &value);
15591560
if (rc < 0) {
1561+
Py_DECREF(names);
15601562
Py_DECREF(dict);
15611563
return NULL;
15621564
}
15631565
if (rc == 0) {
15641566
continue; // unset or hidden slot
15651567
}
1566-
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
1568+
PyObject *name = PyTuple_GET_ITEM(names, i);
15671569
int err = PyDict_SetItem(dict, name, value);
15681570
Py_DECREF(value);
15691571
if (err < 0) {
1572+
Py_DECREF(names);
15701573
Py_DECREF(dict);
15711574
return NULL;
15721575
}
15731576
}
1577+
Py_DECREF(names);
15741578
return dict;
15751579
}
15761580

0 commit comments

Comments
 (0)