diff options
author | Mark Shannon <mark@hotpy.org> | 2022-03-31 16:13:25 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-31 16:13:25 (GMT) |
commit | 74b95d86e0f14603f878c4df3133bc8a93f8f80a (patch) | |
tree | bdd3f8b2a671bfa2458c1a4e34098ff70fa6e36b | |
parent | 44e915028d75f7cef141aa1aada962465a5907d6 (diff) | |
download | cpython-74b95d86e0f14603f878c4df3133bc8a93f8f80a.zip cpython-74b95d86e0f14603f878c4df3133bc8a93f8f80a.tar.gz cpython-74b95d86e0f14603f878c4df3133bc8a93f8f80a.tar.bz2 |
bpo-40421: Add missing getters for frame object attributes to C-API. (GH-32114)
-rw-r--r-- | Doc/c-api/frame.rst | 35 | ||||
-rw-r--r-- | Doc/whatsnew/3.11.rst | 11 | ||||
-rw-r--r-- | Include/cpython/frameobject.h | 5 | ||||
-rw-r--r-- | Lib/test/test_capi.py | 20 | ||||
-rw-r--r-- | Misc/NEWS.d/next/C API/2022-03-25-13-40-46.bpo-40421.wJREl2.rst | 3 | ||||
-rw-r--r-- | Modules/_testcapimodule.c | 44 | ||||
-rw-r--r-- | Objects/frameobject.c | 22 |
7 files changed, 136 insertions, 4 deletions
diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 0c11bc1..6d265e4 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -30,6 +30,17 @@ See also :ref:`Reflection <reflection>`. .. versionadded:: 3.9 +.. c:function:: PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) + + Get the *frame*'s ``f_builtins`` attribute. + + Return a :term:`strong reference`. The result cannot be ``NULL``. + + *frame* must not be ``NULL``. + + .. versionadded:: 3.11 + + .. c:function:: PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) Get the *frame* code. @@ -41,6 +52,30 @@ See also :ref:`Reflection <reflection>`. .. versionadded:: 3.9 +.. c:function:: PyObject* PyFrame_GetGenerator(PyFrameObject *frame) + + Get the generator, coroutine, or async generator that owns this frame, + or ``NULL`` if this frame is not owned by a generator. + Does not raise an exception, even if the return value is ``NULL``. + + Return a :term:`strong reference`, or ``NULL``. + + *frame* must not be ``NULL``. + + .. versionadded:: 3.11 + + +.. c:function:: PyObject* PyFrame_GetGlobals(PyFrameObject *frame) + + Get the *frame*'s ``f_globals`` attribute. + + Return a :term:`strong reference`. The result cannot be ``NULL``. + + *frame* must not be ``NULL``. + + .. versionadded:: 3.11 + + .. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame) Get the *frame*'s ``f_locals`` attribute (:class:`dict`). diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 1bd9587..16715c3 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -868,6 +868,9 @@ New Features :c:func:`PyFloat_Unpack8`. (Contributed by Victor Stinner in :issue:`46906`.) +* Add new functions to get frame object attributes: + :c:func:`PyFrame_GetBuiltins`, :c:func:`PyFrame_GetGenerator`, + :c:func:`PyFrame_GetGlobals`. Porting to Python 3.11 ---------------------- @@ -985,13 +988,13 @@ Porting to Python 3.11 * ``f_back``: use :c:func:`PyFrame_GetBack`. * ``f_blockstack``: removed. - * ``f_builtins``: use ``PyObject_GetAttrString((PyObject*)frame, "f_builtins")``. + * ``f_builtins``: use :c:func:`PyFrame_GetBuiltins`. * ``f_code``: use :c:func:`PyFrame_GetCode`. - * ``f_gen``: removed. - * ``f_globals``: use ``PyObject_GetAttrString((PyObject*)frame, "f_globals")``. + * ``f_gen``: use :c:func:`PyFrame_GetGenerator`. + * ``f_globals``: use :c:func:`PyFrame_GetGlobals`. * ``f_iblock``: removed. * ``f_lasti``: use ``PyObject_GetAttrString((PyObject*)frame, "f_lasti")``. - Code using ``f_lasti`` with ``PyCode_Addr2Line()`` must use + Code using ``f_lasti`` with ``PyCode_Addr2Line()`` should use :c:func:`PyFrame_GetLineNumber` instead. * ``f_lineno``: use :c:func:`PyFrame_GetLineNumber` * ``f_locals``: use :c:func:`PyFrame_GetLocals`. diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index d54d365..ffeb8bd 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -24,3 +24,8 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame); + +PyAPI_FUNC(PyObject *) PyFrame_GetGlobals(PyFrameObject *frame); +PyAPI_FUNC(PyObject *) PyFrame_GetBuiltins(PyFrameObject *frame); + +PyAPI_FUNC(PyObject *) PyFrame_GetGenerator(PyFrameObject *frame); diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index d961543..238acf9 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -1087,5 +1087,25 @@ 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.assertEquals(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)) + + def test_frame_get_generator(self): + gen = self.getgenframe() + frame = next(gen) + self.assertIs(gen, _testcapi.frame_getgenerator(frame)) + + if __name__ == "__main__": unittest.main() diff --git a/Misc/NEWS.d/next/C API/2022-03-25-13-40-46.bpo-40421.wJREl2.rst b/Misc/NEWS.d/next/C API/2022-03-25-13-40-46.bpo-40421.wJREl2.rst new file mode 100644 index 0000000..95b7b69 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-03-25-13-40-46.bpo-40421.wJREl2.rst @@ -0,0 +1,3 @@ +Add ``PyFrame_GetBuiltins``, ``PyFrame_GetGenerator`` and +``PyFrame_GetGlobals`` C-API functions to access frame object attributes +safely from C code. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 019c2b8..759656a 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5853,6 +5853,46 @@ test_float_unpack(PyObject *self, PyObject *args) return PyFloat_FromDouble(d); } +static PyObject * +frame_getlocals(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetLocals((PyFrameObject *)frame); +} + +static PyObject * +frame_getglobals(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetGlobals((PyFrameObject *)frame); +} + +static PyObject * +frame_getgenerator(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetGenerator((PyFrameObject *)frame); +} + +static PyObject * +frame_getbuiltins(PyObject *self, PyObject *frame) +{ + if (!PyFrame_Check(frame)) { + PyErr_SetString(PyExc_TypeError, "argument must be a frame"); + return NULL; + } + return PyFrame_GetBuiltins((PyFrameObject *)frame); +} + static PyObject *negative_dictoffset(PyObject *, PyObject *); static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *); @@ -6142,6 +6182,10 @@ static PyMethodDef TestMethods[] = { {"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL}, {"float_pack", test_float_pack, METH_VARARGS, NULL}, {"float_unpack", test_float_unpack, METH_VARARGS, NULL}, + {"frame_getlocals", frame_getlocals, METH_O, NULL}, + {"frame_getglobals", frame_getglobals, METH_O, NULL}, + {"frame_getgenerator", frame_getgenerator, METH_O, NULL}, + {"frame_getbuiltins", frame_getbuiltins, METH_O, NULL}, {NULL, NULL} /* sentinel */ }; diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d4993104..581de22 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1135,6 +1135,28 @@ PyFrame_GetLocals(PyFrameObject *frame) } PyObject* +PyFrame_GetGlobals(PyFrameObject *frame) +{ + return frame_getglobals(frame, NULL); +} + +PyObject* +PyFrame_GetBuiltins(PyFrameObject *frame) +{ + return frame_getbuiltins(frame, NULL); +} + +PyObject * +PyFrame_GetGenerator(PyFrameObject *frame) +{ + if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) { + return NULL; + } + PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame); + return Py_NewRef(gen); +} + +PyObject* _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { PyObject *builtins = PyDict_GetItemWithError(globals, &_Py_ID(__builtins__)); |