.. versionadded:: 3.11
+.. c:function:: PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name)
+
+ Get the variable *name* of *frame*.
+
+ * Return a :term:`strong reference` to the variable value on success.
+ * Raise :exc:`NameError` and return ``NULL`` if the variable does not exist.
+ * Raise an exception and return ``NULL`` on error.
+
+ .. versionadded:: 3.12
+
+
+.. c:function:: PyObject* PyFrame_GetVarString(PyFrameObject *frame, const char *name)
+
+ Similar to :c:func:`PyFrame_GetVar`, but the variable name is a C string
+ encoded in UTF-8.
+
+ .. versionadded:: 3.12
+
+
.. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame)
Get the *frame*'s ``f_locals`` attribute (:class:`dict`).
(Contributed by Carl Meyer in :gh:`91051`.)
+* Add :c:func:`PyFrame_GetVar` and :c:func:`PyFrame_GetVarString` functions to
+ get a frame variable by its name.
+ (Contributed by Victor Stinner in :gh:`91248`.)
+
Porting to Python 3.12
----------------------
PyAPI_FUNC(PyObject *) PyFrame_GetGenerator(PyFrameObject *frame);
PyAPI_FUNC(int) PyFrame_GetLasti(PyFrameObject *frame);
-
+PyAPI_FUNC(PyObject*) PyFrame_GetVar(PyFrameObject *frame, PyObject *name);
+PyAPI_FUNC(PyObject*) PyFrame_GetVarString(PyFrameObject *frame, const char *name);
self.assertIs(Subclass().get_defining_module(), self.module)
-class Test_FrameAPI(unittest.TestCase):
-
- def getframe(self):
- return sys._getframe()
-
- def getgenframe(self):
- yield sys._getframe()
-
- def test_frame_getters(self):
- frame = self.getframe()
- self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
- self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
- self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
- self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))
-
- def test_frame_get_generator(self):
- gen = self.getgenframe()
- frame = next(gen)
- self.assertIs(gen, _testcapi.frame_getgenerator(frame))
-
-
SUFFICIENT_TO_DEOPT_AND_SPECIALIZE = 100
class Test_Pep523API(unittest.TestCase):
import types
import unittest
import weakref
+try:
+ import _testcapi
+except ImportError:
+ _testcapi = None
from test import support
from test.support.script_helper import assert_python_ok
gc.enable()
+@unittest.skipIf(_testcapi is None, 'need _testcapi')
+class TestCAPI(unittest.TestCase):
+ def getframe(self):
+ return sys._getframe()
+
+ def test_frame_getters(self):
+ frame = self.getframe()
+ self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
+ self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
+ self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
+ self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))
+
+ def test_getvar(self):
+ current_frame = sys._getframe()
+ x = 1
+ self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1)
+ self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1)
+ with self.assertRaises(NameError):
+ _testcapi.frame_getvar(current_frame, "y")
+ with self.assertRaises(NameError):
+ _testcapi.frame_getvarstring(current_frame, b"y")
+
+ def getgenframe(self):
+ yield sys._getframe()
+
+ def test_frame_get_generator(self):
+ gen = self.getgenframe()
+ frame = next(gen)
+ self.assertIs(gen, _testcapi.frame_getgenerator(frame))
+
+
if __name__ == "__main__":
unittest.main()
--- /dev/null
+Add :c:func:`PyFrame_GetVar` and :c:func:`PyFrame_GetVarString` functions to
+get a frame variable by its name. Patch by Victor Stinner.
return PyLong_FromLong(lasti);
}
+static PyObject *
+test_frame_getvar(PyObject *self, PyObject *args)
+{
+ PyObject *frame, *name;
+ if (!PyArg_ParseTuple(args, "OO", &frame, &name)) {
+ return NULL;
+ }
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+
+ return PyFrame_GetVar((PyFrameObject *)frame, name);
+}
+
+static PyObject *
+test_frame_getvarstring(PyObject *self, PyObject *args)
+{
+ PyObject *frame;
+ const char *name;
+ if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) {
+ return NULL;
+ }
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+
+ return PyFrame_GetVarString((PyFrameObject *)frame, name);
+}
+
+
static PyObject *
eval_get_func_name(PyObject *self, PyObject *func)
{
{"frame_getgenerator", frame_getgenerator, METH_O, NULL},
{"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
{"frame_getlasti", frame_getlasti, METH_O, NULL},
+ {"frame_getvar", test_frame_getvar, METH_VARARGS, NULL},
+ {"frame_getvarstring", test_frame_getvarstring, METH_VARARGS, NULL},
{"eval_get_func_name", eval_get_func_name, METH_O, NULL},
{"eval_get_func_desc", eval_get_func_desc, METH_O, NULL},
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
return _PyEval_GetBuiltins(tstate);
}
+PyObject *
+PyFrame_GetVar(PyFrameObject *frame, PyObject *name)
+{
+ PyObject *locals = PyFrame_GetLocals(frame);
+ if (locals == NULL) {
+ return NULL;
+ }
+ PyObject *value = PyDict_GetItemWithError(locals, name);
+ Py_DECREF(locals);
+ if (value == NULL) {
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ PyErr_Format(PyExc_NameError, "variable %R does not exist", name);
+ return NULL;
+ }
+ return Py_NewRef(value);
+}
+
+PyObject *
+PyFrame_GetVarString(PyFrameObject *frame, const char *name)
+{
+ PyObject *name_obj = PyUnicode_FromString(name);
+ if (name_obj == NULL) {
+ return NULL;
+ }
+ PyObject *value = PyFrame_GetVar(frame, name_obj);
+ Py_DECREF(name_obj);
+ return value;
+}