mirror of https://github.com/python/cpython
gh-91248: Add PyFrame_GetVar() function (#95712)
Add PyFrame_GetVar() and PyFrame_GetVarString() functions to get a frame variable by its name. Move PyFrameObject C API tests from test_capi to test_frame.
This commit is contained in:
parent
acf4d5d5bd
commit
4d5fcca273
|
@ -79,6 +79,25 @@ See also :ref:`Reflection <reflection>`.
|
|||
.. 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`).
|
||||
|
|
|
@ -735,6 +735,10 @@ New Features
|
|||
(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
|
||||
----------------------
|
||||
|
||||
|
|
|
@ -14,4 +14,5 @@ PyAPI_FUNC(PyObject *) PyFrame_GetBuiltins(PyFrameObject *frame);
|
|||
|
||||
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);
|
||||
|
|
|
@ -1677,27 +1677,6 @@ class Test_ModuleStateAccess(unittest.TestCase):
|
|||
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):
|
||||
|
|
|
@ -5,6 +5,10 @@ import textwrap
|
|||
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
|
||||
|
@ -326,5 +330,36 @@ class TestIncompleteFrameAreInvisible(unittest.TestCase):
|
|||
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()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Add :c:func:`PyFrame_GetVar` and :c:func:`PyFrame_GetVarString` functions to
|
||||
get a frame variable by its name. Patch by Victor Stinner.
|
|
@ -5607,6 +5607,38 @@ frame_getlasti(PyObject *self, PyObject *frame)
|
|||
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)
|
||||
{
|
||||
|
@ -6294,6 +6326,8 @@ static PyMethodDef TestMethods[] = {
|
|||
{"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},
|
||||
|
|
|
@ -1430,4 +1430,34 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals)
|
|||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue