From 62be414a95d0f7981d1ec66aa9be25a1762f53e5 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 22 Mar 2022 15:35:31 +0000 Subject: [PATCH 01/13] `PyFrame_FastToLocalsWithError` and `PyFrame_LocalsToFast` are no longer called during profile and tracing. (Contributed by Fabio Zadrozny) --- .../2022-03-22-15-12-28.bpo-42197.SwrrFO.rst | 5 +++++ Python/sysmodule.c | 7 ------- 2 files changed, 5 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst new file mode 100644 index 000000000000000..cafd02c9e935900 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst @@ -0,0 +1,5 @@ +`PyFrame_FastToLocalsWithError` and `PyFrame_LocalsToFast` are no longer +called during profile nor tracing. C code that accesses the `f_locals` directly +(which is not recommended) must now call +`PyFrame_FastToLocalsWithError` to update locals and `PyFrame_LocalsToFast` +after `PyFrameObject.f_locals` are updated. diff --git a/Python/sysmodule.c b/Python/sysmodule.c index c89f81f689f7e38..fe82ec2074810aa 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -924,9 +924,6 @@ static PyObject * call_trampoline(PyThreadState *tstate, PyObject* callback, PyFrameObject *frame, int what, PyObject *arg) { - if (PyFrame_FastToLocalsWithError(frame) < 0) { - return NULL; - } PyObject *stack[3]; stack[0] = (PyObject *)frame; @@ -936,10 +933,6 @@ call_trampoline(PyThreadState *tstate, PyObject* callback, /* call the Python-level function */ PyObject *result = _PyObject_FastCallTstate(tstate, callback, stack, 3); - PyFrame_LocalsToFast(frame, 1); - if (result == NULL) { - PyTraceBack_Here(frame); - } return result; } From d775a87e9d12abc46403e20659f42052fd1837bc Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Tue, 22 Mar 2022 17:34:22 +0000 Subject: [PATCH 02/13] Fix formatting of news item. --- .../2022-03-22-15-12-28.bpo-42197.SwrrFO.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst index cafd02c9e935900..20ac78198ede632 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst @@ -1,5 +1,5 @@ -`PyFrame_FastToLocalsWithError` and `PyFrame_LocalsToFast` are no longer -called during profile nor tracing. C code that accesses the `f_locals` directly -(which is not recommended) must now call -`PyFrame_FastToLocalsWithError` to update locals and `PyFrame_LocalsToFast` -after `PyFrameObject.f_locals` are updated. +``PyFrame_FastToLocalsWithError`` and ``PyFrame_LocalsToFast`` are no longer +called during profile nor tracing. C code that accesses the ``f_locals`` attribute + directly (which is not recommended) must now call +``PyFrame_FastToLocalsWithError`` to update locals and ``PyFrame_LocalsToFast`` +after ``f_locals`` are updated. From 4bc0707bfb72362fd154e07093293d2071f066c3 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 10:05:49 +0000 Subject: [PATCH 03/13] Make accesses to a frame's `f_locals` safe from C code, not relying on calls to `PyFrame_FastToLocals` or `PyFrame_LocalsToFast`. --- Include/cpython/frameobject.h | 1 + Include/internal/pycore_frame.h | 1 + .../2022-03-22-15-12-28.bpo-42197.SwrrFO.rst | 4 +-- Objects/frameobject.c | 29 +++++++++++++++---- Python/sysmodule.c | 5 ++++ 5 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index 9b697fb3cbaf503..d54d3652a0dbc85 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -23,3 +23,4 @@ PyAPI_FUNC(int) PyFrame_FastToLocalsWithError(PyFrameObject *f); PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); +PyAPI_FUNC(PyObject *) PyFrame_GetLocals(PyFrameObject *frame); diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 14fba8cd1f941c8..211831a6e497f52 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -15,6 +15,7 @@ struct _frame { int f_lineno; /* Current line number. Only valid if non-zero */ char f_trace_lines; /* Emit per-line trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */ + char f_fast_as_locals; /* Have the fast locals of this frame been converted to a dict? */ /* The frame data, if this frame object owns the frame */ PyObject *_f_frame_data[1]; }; diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst index 20ac78198ede632..ffb9fd03c402a94 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst @@ -1,5 +1,3 @@ ``PyFrame_FastToLocalsWithError`` and ``PyFrame_LocalsToFast`` are no longer called during profile nor tracing. C code that accesses the ``f_locals`` attribute - directly (which is not recommended) must now call -``PyFrame_FastToLocalsWithError`` to update locals and ``PyFrame_LocalsToFast`` -after ``f_locals`` are updated. + directly must do so by calling ``PyFrame_GetLocals``. diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 5c6a8bcb9008da2..7a3f53567e05638 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -840,6 +840,7 @@ _PyFrame_New_NoTrack(PyCodeObject *code) f->f_trace = NULL; f->f_trace_lines = 1; f->f_trace_opcodes = 0; + f->f_fast_as_locals = 0; f->f_lineno = 0; return f; } @@ -911,6 +912,9 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { PyObject **fast; PyCodeObject *co; locals = frame->f_locals; + if ((frame->f_code->co_flags & CO_NEWLOCALS) == 0) { + return 0; + } if (locals == NULL) { locals = frame->f_locals = PyDict_New(); if (locals == NULL) @@ -1004,7 +1008,11 @@ PyFrame_FastToLocalsWithError(PyFrameObject *f) PyErr_BadInternalCall(); return -1; } - return _PyFrame_FastToLocalsWithError(f->f_frame); + int err = _PyFrame_FastToLocalsWithError(f->f_frame); + if (err == 0) { + f->f_fast_as_locals = 1; + } + return err; } void @@ -1028,8 +1036,12 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; locals = frame->f_locals; - if (locals == NULL) + if ((frame->f_code->co_flags & CO_NEWLOCALS) == 0) { return; + } + if (locals == NULL) { + return; + } fast = _PyFrame_GetLocalsArray(frame); co = frame->f_code; @@ -1088,13 +1100,12 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) void PyFrame_LocalsToFast(PyFrameObject *f, int clear) { - if (f == NULL || _PyFrame_GetState(f) == FRAME_CLEARED) { - return; + if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) { + _PyFrame_LocalsToFast(f->f_frame, clear); + f->f_fast_as_locals = 0; } - _PyFrame_LocalsToFast(f->f_frame, clear); } - PyCodeObject * PyFrame_GetCode(PyFrameObject *frame) { @@ -1118,6 +1129,12 @@ PyFrame_GetBack(PyFrameObject *frame) return back; } +PyObject* +PyFrame_GetLocals(PyFrameObject *frame) +{ + return frame_getlocals(frame, NULL); +} + PyObject* _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index fe82ec2074810aa..135a4919ad10514 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -933,6 +933,11 @@ call_trampoline(PyThreadState *tstate, PyObject* callback, /* call the Python-level function */ PyObject *result = _PyObject_FastCallTstate(tstate, callback, stack, 3); + PyFrame_LocalsToFast(frame, 1); + + if (result == NULL) { + PyTraceBack_Here(frame); + } return result; } From 4cf7efa8b7bb818ff82c2da223f3874971cbd549 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 11:22:34 +0000 Subject: [PATCH 04/13] Remove unnecessary check for CO_NEWLOCALS flag. --- Objects/frameobject.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 7a3f53567e05638..13dfbf6b9db4135 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -912,9 +912,6 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { PyObject **fast; PyCodeObject *co; locals = frame->f_locals; - if ((frame->f_code->co_flags & CO_NEWLOCALS) == 0) { - return 0; - } if (locals == NULL) { locals = frame->f_locals = PyDict_New(); if (locals == NULL) @@ -1036,9 +1033,6 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) PyObject *error_type, *error_value, *error_traceback; PyCodeObject *co; locals = frame->f_locals; - if ((frame->f_code->co_flags & CO_NEWLOCALS) == 0) { - return; - } if (locals == NULL) { return; } From d41f7ea7f1c1ac808a959234b25583d78f9cef69 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 11:30:12 +0000 Subject: [PATCH 05/13] Remove empty line --- Python/sysmodule.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 135a4919ad10514..1983da3179e018c 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -934,7 +934,6 @@ call_trampoline(PyThreadState *tstate, PyObject* callback, PyObject *result = _PyObject_FastCallTstate(tstate, callback, stack, 3); PyFrame_LocalsToFast(frame, 1); - if (result == NULL) { PyTraceBack_Here(frame); } From 723f354e22c9bea485490b9c70e93da2df8328fd Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 11:33:06 +0000 Subject: [PATCH 06/13] Update 'what's new' --- Doc/whatsnew/3.11.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 938a573d5957447..8182149c7e593ab 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -964,7 +964,7 @@ Porting to Python 3.11 Code using ``f_lasti`` with ``PyCode_Addr2Line()`` must use :c:func:`PyFrame_GetLineNumber` instead. * ``f_lineno``: use :c:func:`PyFrame_GetLineNumber` - * ``f_locals``: use ``PyObject_GetAttrString((PyObject*)frame, "f_locals")``. + * ``f_locals``: use ``PyFrame_GetLocals(frame)``. * ``f_stackdepth``: removed. * ``f_state``: no public API (renamed to ``f_frame.f_state``). * ``f_trace``: no public API. From 02f061ac19d501b087756fa45a54745ee4a856e6 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 11:34:12 +0000 Subject: [PATCH 07/13] Use nicer formatting --- Doc/whatsnew/3.11.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 8182149c7e593ab..56be88845f8298d 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -964,7 +964,7 @@ Porting to Python 3.11 Code using ``f_lasti`` with ``PyCode_Addr2Line()`` must use :c:func:`PyFrame_GetLineNumber` instead. * ``f_lineno``: use :c:func:`PyFrame_GetLineNumber` - * ``f_locals``: use ``PyFrame_GetLocals(frame)``. + * ``f_locals``: use :c:func:`PyFrame_GetLocals(frame)`. * ``f_stackdepth``: removed. * ``f_state``: no public API (renamed to ``f_frame.f_state``). * ``f_trace``: no public API. From 281305bb6a39aeae0da91555fd65871415c19fea Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 11:35:11 +0000 Subject: [PATCH 08/13] Fix formatting --- Doc/whatsnew/3.11.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 56be88845f8298d..3525f3c271f0a71 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -964,7 +964,7 @@ Porting to Python 3.11 Code using ``f_lasti`` with ``PyCode_Addr2Line()`` must use :c:func:`PyFrame_GetLineNumber` instead. * ``f_lineno``: use :c:func:`PyFrame_GetLineNumber` - * ``f_locals``: use :c:func:`PyFrame_GetLocals(frame)`. + * ``f_locals``: use :c:func:`PyFrame_GetLocals`. * ``f_stackdepth``: removed. * ``f_state``: no public API (renamed to ``f_frame.f_state``). * ``f_trace``: no public API. From 8d3c911a209a93644873bb2fea3e9a9f48a3c892 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 12:26:34 +0000 Subject: [PATCH 09/13] Update Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com> --- .../2022-03-22-15-12-28.bpo-42197.SwrrFO.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst index ffb9fd03c402a94..d54002a80e44f6a 100644 --- a/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-22-15-12-28.bpo-42197.SwrrFO.rst @@ -1,3 +1,2 @@ -``PyFrame_FastToLocalsWithError`` and ``PyFrame_LocalsToFast`` are no longer -called during profile nor tracing. C code that accesses the ``f_locals`` attribute - directly must do so by calling ``PyFrame_GetLocals``. +:c:func:`PyFrame_FastToLocalsWithError` and :c:func:`PyFrame_LocalsToFast` are no longer +called during profiling nor tracing. C code can access the ``f_locals`` attribute of :c:type:`PyFrameObject` by calling :c:func:`PyFrame_GetLocals`. From 0e452a167db76ccaac954bb395cb0e9034771d9c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 12:31:33 +0000 Subject: [PATCH 10/13] Document new `PyFrame_GetLocals` C-API function. --- Doc/c-api/frame.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 0e36e6e1fd706fc..0420e0882cf5a90 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -41,6 +41,17 @@ See also :ref:`Reflection `. .. versionadded:: 3.9 +.. c:function:: PyCodeObject* PyFrame_GetLocals(PyFrameObject *frame) + + Get the *frame*'s ``f_locals`` attribute. + + Return a :term:`strong reference`. + + *frame* must not be ``NULL``. + + .. versionadded:: 3.11 + + .. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame) Return the line number that *frame* is currently executing. From b5e8004e47f13d6a1c773fa2bcdd0676894a4f5c Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Wed, 23 Mar 2022 14:13:12 +0000 Subject: [PATCH 11/13] Add accessor functions for frame attributes that can longer be accessed directly in C. --- Doc/c-api/frame.rst | 37 ++++++++++++++++++++++++++++++++++- Doc/whatsnew/3.11.rst | 8 ++++---- Include/cpython/frameobject.h | 5 +++++ Objects/frameobject.c | 21 ++++++++++++++++++++ 4 files changed, 66 insertions(+), 5 deletions(-) diff --git a/Doc/c-api/frame.rst b/Doc/c-api/frame.rst index 0420e0882cf5a90..b59a68870bcd5a8 100644 --- a/Doc/c-api/frame.rst +++ b/Doc/c-api/frame.rst @@ -30,6 +30,17 @@ See also :ref:`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,7 +52,31 @@ See also :ref:`Reflection `. .. versionadded:: 3.9 -.. c:function:: PyCodeObject* PyFrame_GetLocals(PyFrameObject *frame) +.. 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. diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 92c2abaf4602d77..ff39ed50e11a7ff 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -955,13 +955,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 d54d3652a0dbc85..ffeb8bd04a46f4b 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/Objects/frameobject.c b/Objects/frameobject.c index 13dfbf6b9db4135..c89639ab257285b 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1129,6 +1129,27 @@ PyFrame_GetLocals(PyFrameObject *frame) return frame_getlocals(frame, NULL); } +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; + } + return (PyObject *)_PyFrame_GetGenerator(frame->f_frame); +} + PyObject* _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) { From 2a43da771bd5987cd2a97f22ef71db373568a882 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 25 Mar 2022 13:43:49 +0000 Subject: [PATCH 12/13] Add news and update whatsnew --- Doc/whatsnew/3.11.rst | 3 +++ .../NEWS.d/next/C API/2022-03-25-13-40-46.bpo-40421.wJREl2.rst | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 Misc/NEWS.d/next/C API/2022-03-25-13-40-46.bpo-40421.wJREl2.rst diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 5d37511242ca3a7..9f3069ca5ce95f8 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -843,6 +843,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 ---------------------- 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 000000000000000..95b7b69347ce496 --- /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. From 4882efd62dbb29fdc7d3d879edb90f443c2cfc0f Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 25 Mar 2022 14:04:43 +0000 Subject: [PATCH 13/13] Add tests for new C-API functions and fix refcount error. --- Lib/test/test_capi.py | 20 ++++++++++++++++++ Modules/_testcapimodule.c | 44 +++++++++++++++++++++++++++++++++++++++ Objects/frameobject.c | 3 ++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index d9615430327a408..238acf94526a3fc 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -1087,5 +1087,25 @@ class Subclass(BaseException, self.module.StateAccessType): 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/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 019c2b85b6156a0..759656ae1f8a151 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 c89639ab257285b..f6dceda37e6d0d5 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1147,7 +1147,8 @@ PyFrame_GetGenerator(PyFrameObject *frame) if (frame->f_frame->owner != FRAME_OWNED_BY_GENERATOR) { return NULL; } - return (PyObject *)_PyFrame_GetGenerator(frame->f_frame); + PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame); + return Py_NewRef(gen); } PyObject*