gh-74929: PEP 667 C API documentation (gh-119379)

* Add docs for new APIs
* Add soft-deprecation notices
* Add What's New porting entries
* Update comments referencing `PyFrame_LocalsToFast()` to mention the proxy instead
* Other related cleanups found when looking for refs to the deprecated APIs
This commit is contained in:
Alyssa Coghlan 2024-06-01 13:59:35 +10:00 committed by GitHub
parent cc5cd4d93e
commit 3859e09e3d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 104 additions and 11 deletions

View File

@ -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.
@ -31,6 +43,32 @@ 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:`locals` 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:`globals` in Python code.
.. versionadded:: 3.13
.. c:function:: const char* PyEval_GetFuncName(PyObject *func)
Return the name of *func* if it is a function, class or instance object, else the

View File

@ -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:

View File

@ -97,7 +97,7 @@ Interpreter improvements:
* :pep:`667`: The :func:`locals` builtin now has
:ref:`defined semantics <whatsnew313-locals-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:
@ -2143,6 +2143,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 <strong reference>`
rather than borrowed references. (Added as part of :pep:`667`.)
Build Changes
=============
@ -2318,6 +2323,15 @@ Changes in the C API
to :c:func:`PyUnstable_Code_GetFirstFree`.
(Contributed by Bogdan Romanyuk in :gh:`115781`.)
* :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
for :term:`optimized scopes <optimized scope>`. (Changed as part of :pep:`667`.)
Removed C APIs
--------------

View File

@ -394,10 +394,15 @@ class SysModuleTest(unittest.TestCase):
@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)

View File

@ -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;
}

View File

@ -1570,7 +1570,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) {

View File

@ -1541,7 +1541,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) {

View File

@ -4784,7 +4784,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) {

View File

@ -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