Skip to content

Commit 3859e09

Browse files
authored
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
1 parent cc5cd4d commit 3859e09

File tree

9 files changed

+104
-11
lines changed

9 files changed

+104
-11
lines changed

Doc/c-api/reflection.rst

+38
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,30 @@ Reflection
77

88
.. c:function:: PyObject* PyEval_GetBuiltins(void)
99
10+
.. deprecated:: 3.13
11+
12+
Use :c:func:`PyEval_GetFrameBuiltins` instead.
13+
1014
Return a dictionary of the builtins in the current execution frame,
1115
or the interpreter of the thread state if no frame is currently executing.
1216
1317
1418
.. c:function:: PyObject* PyEval_GetLocals(void)
1519
20+
.. deprecated:: 3.13
21+
22+
Use :c:func:`PyEval_GetFrameLocals` instead.
23+
1624
Return a dictionary of the local variables in the current execution frame,
1725
or ``NULL`` if no frame is currently executing.
1826
1927
2028
.. c:function:: PyObject* PyEval_GetGlobals(void)
2129
30+
.. deprecated:: 3.13
31+
32+
Use :c:func:`PyEval_GetFrameGlobals` instead.
33+
2234
Return a dictionary of the global variables in the current execution frame,
2335
or ``NULL`` if no frame is currently executing.
2436
@@ -31,6 +43,32 @@ Reflection
3143
See also :c:func:`PyThreadState_GetFrame`.
3244
3345
46+
.. c:function:: PyObject* PyEval_GetFrameBuiltins(void)
47+
48+
Return a dictionary of the builtins in the current execution frame,
49+
or the interpreter of the thread state if no frame is currently executing.
50+
51+
.. versionadded:: 3.13
52+
53+
54+
.. c:function:: PyObject* PyEval_GetFrameLocals(void)
55+
56+
Return a dictionary of the local variables in the current execution frame,
57+
or ``NULL`` if no frame is currently executing. Equivalent to calling
58+
:func:`locals` in Python code.
59+
60+
.. versionadded:: 3.13
61+
62+
63+
.. c:function:: PyObject* PyEval_GetFrameGlobals(void)
64+
65+
Return a dictionary of the global variables in the current execution frame,
66+
or ``NULL`` if no frame is currently executing. Equivalent to calling
67+
:func:`globals` in Python code.
68+
69+
.. versionadded:: 3.13
70+
71+
3472
.. c:function:: const char* PyEval_GetFuncName(PyObject *func)
3573
3674
Return the name of *func* if it is a function, class or instance object, else the

Doc/data/refcounts.dat

+32
Original file line numberDiff line numberDiff line change
@@ -790,6 +790,12 @@ PyEval_GetGlobals:PyObject*::0:
790790

791791
PyEval_GetFrame:PyObject*::0:
792792

793+
PyEval_GetFrameBuiltins:PyObject*::+1:
794+
795+
PyEval_GetFrameLocals:PyObject*::+1:
796+
797+
PyEval_GetFrameGlobals:PyObject*::+1:
798+
793799
PyEval_GetFuncDesc:const char*:::
794800
PyEval_GetFuncDesc:PyObject*:func:0:
795801

@@ -916,6 +922,32 @@ PyFloat_FromString:PyObject*:str:0:
916922
PyFloat_GetInfo:PyObject*::+1:
917923
PyFloat_GetInfo::void::
918924

925+
PyFrame_GetBack:PyObject*::+1:
926+
PyFrame_GetBack:PyFrameObject*:frame:0:
927+
928+
PyFrame_GetBuiltins:PyObject*::+1:
929+
PyFrame_GetBuiltins:PyFrameObject*:frame:0:
930+
931+
PyFrame_GetCode:PyObject*::+1:
932+
PyFrame_GetCode:PyFrameObject*:frame:0:
933+
934+
PyFrame_GetGenerator:PyObject*::+1:
935+
PyFrame_GetGenerator:PyFrameObject*:frame:0:
936+
937+
PyFrame_GetGlobals:PyObject*::+1:
938+
PyFrame_GetGlobals:PyFrameObject*:frame:0:
939+
940+
PyFrame_GetLocals:PyObject*::+1:
941+
PyFrame_GetLocals:PyFrameObject*:frame:0:
942+
943+
PyFrame_GetVar:PyObject*::+1:
944+
PyFrame_GetVar:PyFrameObject*:frame:0:
945+
PyFrame_GetVar:PyObject*:name:0:
946+
947+
PyFrame_GetVarString:PyObject*::+1:
948+
PyFrame_GetVarString:PyFrameObject*:frame:0:
949+
PyFrame_GetVarString:const char*:name::
950+
919951
PyFrozenSet_Check:int:::
920952
PyFrozenSet_Check:PyObject*:p:0:
921953

Doc/whatsnew/3.13.rst

+15-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Interpreter improvements:
9797
* :pep:`667`: The :func:`locals` builtin now has
9898
:ref:`defined semantics <whatsnew313-locals-semantics>` when mutating the
9999
returned mapping. Python debuggers and similar tools may now more reliably
100-
update local variables in optimized frames even during concurrent code
100+
update local variables in optimized scopes even during concurrent code
101101
execution.
102102

103103
New typing features:
@@ -2143,6 +2143,11 @@ New Features
21432143
destruction the same way the :mod:`tracemalloc` module does. (Contributed
21442144
by Pablo Galindo in :gh:`93502`.)
21452145

2146+
* Add :c:func:`PyEval_GetFrameBuiltins`, :c:func:`PyEval_GetFrameGlobals`, and
2147+
:c:func:`PyEval_GetFrameLocals` to the C API. These replacements for
2148+
:c:func:`PyEval_GetBuiltins`, :c:func:`PyEval_GetGlobals`, and
2149+
:c:func:`PyEval_GetLocals` return :term:`strong references <strong reference>`
2150+
rather than borrowed references. (Added as part of :pep:`667`.)
21462151

21472152
Build Changes
21482153
=============
@@ -2318,6 +2323,15 @@ Changes in the C API
23182323
to :c:func:`PyUnstable_Code_GetFirstFree`.
23192324
(Contributed by Bogdan Romanyuk in :gh:`115781`.)
23202325

2326+
* :c:func:`!PyFrame_FastToLocals` and :c:func:`!PyFrame_FastToLocalsWithError`
2327+
no longer have any effect. Calling these functions has been redundant since
2328+
Python 3.11, when :c:func:`PyFrame_GetLocals` was first introduced.
2329+
(Changed as part of :pep:`667`.)
2330+
2331+
* :c:func:`!PyFrame_LocalsToFast` no longer has any effect. Calling this function
2332+
is redundant now that :c:func:`PyFrame_GetLocals` returns a write-through proxy
2333+
for :term:`optimized scopes <optimized scope>`. (Changed as part of :pep:`667`.)
2334+
23212335
Removed C APIs
23222336
--------------
23232337

Lib/test/test_sys.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,15 @@ def test_dlopenflags(self):
394394

395395
@test.support.refcount_test
396396
def test_refcount(self):
397-
# n here must be a global in order for this test to pass while
398-
# tracing with a python function. Tracing calls PyFrame_FastToLocals
399-
# which will add a copy of any locals to the frame object, causing
400-
# the reference count to increase by 2 instead of 1.
397+
# n here originally had to be a global in order for this test to pass
398+
# while tracing with a python function. Tracing used to call
399+
# PyFrame_FastToLocals, which would add a copy of any locals to the
400+
# frame object, causing the ref count to increase by 2 instead of 1.
401+
# While that no longer happens (due to PEP 667), this test case retains
402+
# its original global-based implementation
403+
# PEP 683's immortal objects also made this point moot, since the
404+
# refcount for None doesn't change anyway. Maybe this test should be
405+
# using a different constant value? (e.g. an integer)
401406
global n
402407
self.assertRaises(TypeError, sys.getrefcount)
403408
c = sys.getrefcount(None)

Objects/frameobject.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -1888,8 +1888,7 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
18881888
}
18891889
// (likely) Otherwise it is an arg (kind & CO_FAST_LOCAL),
18901890
// with the initial value set when the frame was created...
1891-
// (unlikely) ...or it was set to some initial value by
1892-
// an earlier call to PyFrame_LocalsToFast().
1891+
// (unlikely) ...or it was set via the f_locals proxy.
18931892
}
18941893
}
18951894
}
@@ -2002,18 +2001,24 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name)
20022001
int
20032002
PyFrame_FastToLocalsWithError(PyFrameObject *f)
20042003
{
2004+
// Nothing to do here, as f_locals is now a write-through proxy in
2005+
// optimized frames. Soft-deprecated, since there's no maintenance hassle.
20052006
return 0;
20062007
}
20072008

20082009
void
20092010
PyFrame_FastToLocals(PyFrameObject *f)
20102011
{
2012+
// Nothing to do here, as f_locals is now a write-through proxy in
2013+
// optimized frames. Soft-deprecated, since there's no maintenance hassle.
20112014
return;
20122015
}
20132016

20142017
void
20152018
PyFrame_LocalsToFast(PyFrameObject *f, int clear)
20162019
{
2020+
// Nothing to do here, as f_locals is now a write-through proxy in
2021+
// optimized frames. Soft-deprecated, since there's no maintenance hassle.
20172022
return;
20182023
}
20192024

Python/bytecodes.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,7 @@ dummy_func(
15701570

15711571
inst(MAKE_CELL, (--)) {
15721572
// "initial" is probably NULL but not if it's an arg (or set
1573-
// via PyFrame_LocalsToFast() before MAKE_CELL has run).
1573+
// via the f_locals proxy before MAKE_CELL has run).
15741574
PyObject *initial = GETLOCAL(oparg);
15751575
PyObject *cell = PyCell_New(initial);
15761576
if (cell == NULL) {

Python/executor_cases.c.h

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/sysmodule.c

-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ Data members:
3535
#include "pycore_sysmodule.h" // export _PySys_GetSizeOf()
3636
#include "pycore_tuple.h" // _PyTuple_FromArray()
3737

38-
#include "frameobject.h" // PyFrame_FastToLocalsWithError()
3938
#include "pydtrace.h" // PyDTrace_AUDIT()
4039
#include "osdefs.h" // DELIM
4140
#include "stdlib_module_names.h" // _Py_stdlib_module_names

0 commit comments

Comments
 (0)