Thanks to visit codestin.com
Credit goes to github.com

Skip to content

PEP 667: Clarify impact on PyEval_GetLocals #3809

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 58 additions & 5 deletions peps/pep-0667.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ The ``locals()`` function will act the same as it does now for class
and modules scopes. For function scopes it will return an instantaneous
snapshot of the underlying ``frame.f_locals``.


Implementation Notes
====================

When accepted, the PEP text suggested that ``PyEval_GetLocals`` would start returning a
cached instance of the new write-through proxy, while the implementation sketch indicated
it would continue to return a dictionary snapshot cached on the frame instance. This
discrepancy was identified while implementing the PEP, and
`resolved by the Steering Council <https://github.com/python/steering-council/issues/245#issuecomment-2179005461>`__
in favour of retaining the Python 3.12 behaviour of returning a dictionary snapshot
cached on the frame instance.

To avoid confusion when following the reference link from the Python 3.13 What's New
documentation, the PEP text has been updated accordingly.


Motivation
==========

Expand Down Expand Up @@ -169,8 +185,10 @@ The following functions should be used instead::

which return new references.

The semantics of ``PyEval_GetLocals()`` is changed as it now returns a
proxy for the frame locals in optimized frames, not a dictionary.
The semantics of ``PyEval_GetLocals()`` are technically unchanged, but they do change in
practice as the dictionary cached on optimized frames is no longer shared with other
mechanisms for accessing the frame locals (``locals()`` builtin, ``PyFrame_GetLocals``
function, frame ``f_locals`` attributes).

The following three functions will become no-ops, and will be deprecated::

Expand Down Expand Up @@ -207,9 +225,27 @@ C-API
PyEval_GetLocals
''''''''''''''''

Because ``PyEval_GetLocals()`` returns a borrowed reference, it requires
the proxy mapping to be cached on the frame, extending its lifetime and
creating a cycle. ``PyEval_GetFrameLocals()`` should be used instead.
``PyEval_GetLocals()`` has never historically distinguished between whether it was
emulating ``locals()`` or ``sys._getframe().f_locals`` at the Python level, as they all
returned references to the same shared cache of the local variable bindings.

With this PEP, ``locals()`` changes to return independent snapshots on each call for
optimized frames, and ``frame.f_locals`` (along with ``PyFrame_GetLocals``) changes to
return new write-through proxy instances.

Because ``PyEval_GetLocals()`` returns a borrowed reference, it isn't possible to update
its semantics to align with either of those alternatives, leaving it as the only remaining
API that requires a shared cache dictionary stored on the frame object.

While this technically leaves the semantics of the function unchanged, it no longer allows
extra dict entries to be made visible to users of the other APIs, as those APIs are no longer
accessing the same underlying cache dictionary.

Accordingly, the function will be marked as deprecated (with no specific timeline for
removal) and alternatives recommended as described below.

When ``PyEval_GetLocals()`` is being used as an equivalent to the Python ``locals()``
builtin, ``PyEval_GetFrameLocals()`` should be used instead.

This code::

Expand All @@ -226,6 +262,23 @@ should be replaced with::
goto error_handler;
}

When ``PyEval_GetLocals()`` is being used as an equivalent to calling
``sys._getframe().f_locals`` in Python, it should be replaced by calling
``PyFrame_GetLocals()`` on the result of ``PyEval_GetFrame()``.

In these cases, the original code should be replaced with::

frame = PyEval_GetFrame();
if (frame == NULL) {
goto error_handler;
}
locals = PyFrame_GetLocals(frame);
frame = NULL; // Minimise visibility of borrowed reference
if (locals == NULL) {
goto error_handler;
}


Implementation
==============

Expand Down