From 67f645e6da8da878a0efb27471bb3f8b99b78e17 Mon Sep 17 00:00:00 2001 From: Nick Coghlan Date: Wed, 22 May 2024 16:31:42 +1000 Subject: [PATCH 1/5] gh-74929: PEP 667 C API documentation --- Doc/c-api/reflection.rst | 37 +++++++++++++++++++++++++++++++++++++ Doc/data/refcounts.dat | 32 ++++++++++++++++++++++++++++++++ Doc/whatsnew/3.13.rst | 10 ++++++++++ Lib/test/test_sys.py | 13 +++++++++---- Objects/frameobject.c | 9 +++++++-- Python/bytecodes.c | 2 +- Python/executor_cases.c.h | 2 +- Python/generated_cases.c.h | 2 +- Python/sysmodule.c | 1 - 9 files changed, 98 insertions(+), 10 deletions(-) diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst index 4b1c4770848a30..5696bb8da329b5 100644 --- a/Doc/c-api/reflection.rst +++ b/Doc/c-api/reflection.rst @@ -7,18 +7,30 @@ Reflection .. c:function:: PyObject* PyEval_GetBuiltins(void) + .. deprecated:: 3.13 + + Use :c:func:`PyEval_GetFrameBuiltins` instead. + Return a dictionary of the builtins in the current execution frame, or the interpreter of the thread state if no frame is currently executing. .. c:function:: PyObject* PyEval_GetLocals(void) + .. deprecated:: 3.13 + + Use :c:func:`PyEval_GetFrameLocals` instead. + Return a dictionary of the local variables in the current execution frame, or ``NULL`` if no frame is currently executing. .. c:function:: PyObject* PyEval_GetGlobals(void) + .. deprecated:: 3.13 + + Use :c:func:`PyEval_GetFrameGlobals` instead. + Return a dictionary of the global variables in the current execution frame, or ``NULL`` if no frame is currently executing. @@ -30,6 +42,31 @@ Reflection See also :c:func:`PyThreadState_GetFrame`. +.. c:function:: PyObject* PyEval_GetFrameBuiltins(void) + + Return a dictionary of the builtins in the current execution frame, + or the interpreter of the thread state if no frame is currently executing. + + .. versionadded:: 3.13 + + +.. c:function:: PyObject* PyEval_GetFrameLocals(void) + + Return a dictionary of the local variables in the current execution frame, + or ``NULL`` if no frame is currently executing. Equivalent to calling + :func:`globals` in Python code. + + .. versionadded:: 3.13 + + +.. c:function:: PyObject* PyEval_GetFrameGlobals(void) + + Return a dictionary of the global variables in the current execution frame, + or ``NULL`` if no frame is currently executing. Equivalent to calling + :func:`locals` in Python code. + + .. versionadded:: 3.13 + .. c:function:: const char* PyEval_GetFuncName(PyObject *func) diff --git a/Doc/data/refcounts.dat b/Doc/data/refcounts.dat index 62a96146d605ff..a7d06e076a1b55 100644 --- a/Doc/data/refcounts.dat +++ b/Doc/data/refcounts.dat @@ -790,6 +790,12 @@ PyEval_GetGlobals:PyObject*::0: PyEval_GetFrame:PyObject*::0: +PyEval_GetFrameBuiltins:PyObject*::+1: + +PyEval_GetFrameLocals:PyObject*::+1: + +PyEval_GetFrameGlobals:PyObject*::+1: + PyEval_GetFuncDesc:const char*::: PyEval_GetFuncDesc:PyObject*:func:0: @@ -916,6 +922,32 @@ PyFloat_FromString:PyObject*:str:0: PyFloat_GetInfo:PyObject*::+1: PyFloat_GetInfo::void:: +PyFrame_GetBack:PyObject*::+1: +PyFrame_GetBack:PyFrameObject*:frame:0: + +PyFrame_GetBuiltins:PyObject*::+1: +PyFrame_GetBuiltins:PyFrameObject*:frame:0: + +PyFrame_GetCode:PyObject*::+1: +PyFrame_GetCode:PyFrameObject*:frame:0: + +PyFrame_GetGenerator:PyObject*::+1: +PyFrame_GetGenerator:PyFrameObject*:frame:0: + +PyFrame_GetGlobals:PyObject*::+1: +PyFrame_GetGlobals:PyFrameObject*:frame:0: + +PyFrame_GetLocals:PyObject*::+1: +PyFrame_GetLocals:PyFrameObject*:frame:0: + +PyFrame_GetVar:PyObject*::+1: +PyFrame_GetVar:PyFrameObject*:frame:0: +PyFrame_GetVar:PyObject*:name:0: + +PyFrame_GetVarString:PyObject*::+1: +PyFrame_GetVarString:PyFrameObject*:frame:0: +PyFrame_GetVarString:const char*:name:: + PyFrozenSet_Check:int::: PyFrozenSet_Check:PyObject*:p:0: diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 7edfdd4f8167a0..d624be9b7c7d5c 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -2128,6 +2128,11 @@ New Features destruction the same way the :mod:`tracemalloc` module does. (Contributed by Pablo Galindo in :gh:`93502`.) +* Add :c:func:`PyEval_GetFrameBuiltins`, :c:func:`PyEval_GetFrameGlobals`, and + :c:func:`PyEval_GetFrameLocals` to the C API. These replacements for + :c:func:`PyEval_GetBuiltins`, :c:func:`PyEval_GetGlobals`, and + :c:func:`PyEval_GetLocals` return :term:`strong references ` + rather than borrowed references. (Added as part of :pep:`667`) Build Changes ============= @@ -2299,6 +2304,11 @@ Changes in the C API to :c:func:`PyUnstable_Code_GetFirstFree`. (Contributed by Bogdan Romanyuk in :gh:`115781`.) +* :c:func:`!PyFrame_FastToLocals`, :c:func:`!PyFrame_FastToLocalsWithError`, and + :c:func:`!PyFrame_LocalsToFast` no longer have any effect. Note that calling + them has been redundant since Python 3.11, when :c:func:`PyFrame_GetLocals` + was first introduced. (Changed as part of :pep:`667`.) + Removed C APIs -------------- diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 8fe1d77756866a..1e5823f8883957 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -394,10 +394,15 @@ def test_dlopenflags(self): @test.support.refcount_test def test_refcount(self): - # n here must be a global in order for this test to pass while - # tracing with a python function. Tracing calls PyFrame_FastToLocals - # which will add a copy of any locals to the frame object, causing - # the reference count to increase by 2 instead of 1. + # n here originally had to be a global in order for this test to pass + # while tracing with a python function. Tracing used to call + # PyFrame_FastToLocals, which would add a copy of any locals to the + # frame object, causing the ref count to increase by 2 instead of 1. + # While that no longer happens (due to PEP 667), this test case retains + # its original global-based implementation + # PEP 683's immortal objects also made this point moot, since the + # refcount for None doesn't change anyway. Maybe this test should be + # using a different constant value? (e.g. an integer) global n self.assertRaises(TypeError, sys.getrefcount) c = sys.getrefcount(None) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index fc8d6c7a7aee89..5c65007dae46d2 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1888,8 +1888,7 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i, } // (likely) Otherwise it is an arg (kind & CO_FAST_LOCAL), // with the initial value set when the frame was created... - // (unlikely) ...or it was set to some initial value by - // an earlier call to PyFrame_LocalsToFast(). + // (unlikely) ...or it was set via the f_locals proxy. } } } @@ -2002,18 +2001,24 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name) int PyFrame_FastToLocalsWithError(PyFrameObject *f) { + // Nothing to do here, as f_locals is now a write-through proxy in + // optimized frames. Soft-deprecated, since there's no maintenance hassle. return 0; } void PyFrame_FastToLocals(PyFrameObject *f) { + // Nothing to do here, as f_locals is now a write-through proxy in + // optimized frames. Soft-deprecated, since there's no maintenance hassle. return; } void PyFrame_LocalsToFast(PyFrameObject *f, int clear) { + // Nothing to do here, as f_locals is now a write-through proxy in + // optimized frames. Soft-deprecated, since there's no maintenance hassle. return; } diff --git a/Python/bytecodes.c b/Python/bytecodes.c index b48f913b456064..ffd6731c08fb0f 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1553,7 +1553,7 @@ dummy_func( inst(MAKE_CELL, (--)) { // "initial" is probably NULL but not if it's an arg (or set - // via PyFrame_LocalsToFast() before MAKE_CELL has run). + // via the f_locals proxy before MAKE_CELL has run). PyObject *initial = GETLOCAL(oparg); PyObject *cell = PyCell_New(initial); if (cell == NULL) { diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index a3d7af250316e3..4f219c8d4e13ad 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1569,7 +1569,7 @@ case _MAKE_CELL: { oparg = CURRENT_OPARG(); // "initial" is probably NULL but not if it's an arg (or set - // via PyFrame_LocalsToFast() before MAKE_CELL has run). + // via the f_locals proxy before MAKE_CELL has run). PyObject *initial = GETLOCAL(oparg); PyObject *cell = PyCell_New(initial); if (cell == NULL) { diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index 07d9965b299e12..7a5f9d6aac1435 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -4757,7 +4757,7 @@ next_instr += 1; INSTRUCTION_STATS(MAKE_CELL); // "initial" is probably NULL but not if it's an arg (or set - // via PyFrame_LocalsToFast() before MAKE_CELL has run). + // via the f_locals proxy before MAKE_CELL has run). PyObject *initial = GETLOCAL(oparg); PyObject *cell = PyCell_New(initial); if (cell == NULL) { diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 4da13e4552e786..00aa95531026b5 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -35,7 +35,6 @@ Data members: #include "pycore_sysmodule.h" // export _PySys_GetSizeOf() #include "pycore_tuple.h" // _PyTuple_FromArray() -#include "frameobject.h" // PyFrame_FastToLocalsWithError() #include "pydtrace.h" // PyDTrace_AUDIT() #include "osdefs.h" // DELIM #include "stdlib_module_names.h" // _Py_stdlib_module_names From 60917a53dcaa03f70138cb1ff818dab27ebebb1b Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Sat, 25 May 2024 15:16:47 +1000 Subject: [PATCH 2/5] Update Doc/whatsnew/3.13.rst --- Doc/whatsnew/3.13.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index d624be9b7c7d5c..16de1c8c1dab1b 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -2304,10 +2304,14 @@ Changes in the C API to :c:func:`PyUnstable_Code_GetFirstFree`. (Contributed by Bogdan Romanyuk in :gh:`115781`.) -* :c:func:`!PyFrame_FastToLocals`, :c:func:`!PyFrame_FastToLocalsWithError`, and - :c:func:`!PyFrame_LocalsToFast` no longer have any effect. Note that calling - them has been redundant since Python 3.11, when :c:func:`PyFrame_GetLocals` - was first introduced. (Changed as part of :pep:`667`.) +* :c:func:`!PyFrame_FastToLocals` and :c:func:`!PyFrame_FastToLocalsWithError` + no longer have any effect. Calling these functions has been redundant since + Python 3.11, when :c:func:`PyFrame_GetLocals` was first introduced. + (Changed as part of :pep:`667`.) + +* :c:func:`!PyFrame_LocalsToFast` no longer has any effect. Calling this function + is redundant now that :c:func:`PyFrame_GetLocals` returns a write-through proxy + on :term:`optimized frames `. (Changed as part of :pep:`667`.) Removed C APIs -------------- From 560229e9aa875807c90c3c47300f65b2e3dd8788 Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Mon, 27 May 2024 17:11:45 +1000 Subject: [PATCH 3/5] Fix incorrect reference to glossary term --- Doc/whatsnew/3.13.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 16de1c8c1dab1b..ef04ab6936f0c9 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -97,7 +97,7 @@ Interpreter improvements: * :pep:`667`: The :func:`locals` builtin now has :ref:`defined semantics ` when mutating the returned mapping. Python debuggers and similar tools may now more reliably - update local variables in optimized frames even during concurrent code + update local variables in optimized scopes even during concurrent code execution. New typing features: @@ -2310,8 +2310,8 @@ Changes in the C API (Changed as part of :pep:`667`.) * :c:func:`!PyFrame_LocalsToFast` no longer has any effect. Calling this function - is redundant now that :c:func:`PyFrame_GetLocals` returns a write-through proxy - on :term:`optimized frames `. (Changed as part of :pep:`667`.) + is redundant now that :c:func:`PyFrame_GetLocals` returns a write-through proxy + for :term:`optimized scopes `. (Changed as part of :pep:`667`.) Removed C APIs -------------- From b827c7e92b63b88a0c876f926bf6a114a176c155 Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Fri, 31 May 2024 00:23:59 +1000 Subject: [PATCH 4/5] Fix locals/globals equivalence --- Doc/c-api/reflection.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst index 5696bb8da329b5..6c3a08e46a3158 100644 --- a/Doc/c-api/reflection.rst +++ b/Doc/c-api/reflection.rst @@ -54,7 +54,7 @@ Reflection Return a dictionary of the local variables in the current execution frame, or ``NULL`` if no frame is currently executing. Equivalent to calling - :func:`globals` in Python code. + :func:`locals` in Python code. .. versionadded:: 3.13 @@ -63,7 +63,7 @@ Reflection Return a dictionary of the global variables in the current execution frame, or ``NULL`` if no frame is currently executing. Equivalent to calling - :func:`locals` in Python code. + :func:`globals` in Python code. .. versionadded:: 3.13 From 3c04ea9dd11b4280c6bae2a081219a4011ca8f19 Mon Sep 17 00:00:00 2001 From: Alyssa Coghlan Date: Fri, 31 May 2024 20:43:05 +1000 Subject: [PATCH 5/5] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Doc/c-api/reflection.rst | 1 + Doc/whatsnew/3.13.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Doc/c-api/reflection.rst b/Doc/c-api/reflection.rst index 6c3a08e46a3158..5dcfe40c2ce92b 100644 --- a/Doc/c-api/reflection.rst +++ b/Doc/c-api/reflection.rst @@ -42,6 +42,7 @@ Reflection See also :c:func:`PyThreadState_GetFrame`. + .. c:function:: PyObject* PyEval_GetFrameBuiltins(void) Return a dictionary of the builtins in the current execution frame, diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index ef04ab6936f0c9..b298b9c8b6a67d 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -2132,7 +2132,7 @@ New Features :c:func:`PyEval_GetFrameLocals` to the C API. These replacements for :c:func:`PyEval_GetBuiltins`, :c:func:`PyEval_GetGlobals`, and :c:func:`PyEval_GetLocals` return :term:`strong references ` - rather than borrowed references. (Added as part of :pep:`667`) + rather than borrowed references. (Added as part of :pep:`667`.) Build Changes =============