<div dir="ltr"><div>This looks great.</div><div><br></div><div>I only have two nits with the text.<br></div><div><br></div><div>First, why is the snapshot called a "dynamic snapshot"? What exactly is dynamic about it?</div><div><br></div><div>Second, you use the word "mapping" a lot. Would you mind changing that to "mapping object" in most places? Especially in the phrase "each call to ``locals()`` returns the *same* mapping". To me, without the word "object" added, this *could* be interpreted as "a dict with the same key/value pairs" (since "mapping" is also an abstract mathematical concept describing anything that maps keys to values).</div><div><br></div><div>Other than that, go for it! (Assuming Nathaniel agrees, of course.)</div><div><br></div><div>--Guido<br></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, May 21, 2019 at 7:54 AM Nick Coghlan <<a href="mailto:ncoghlan@gmail.com">ncoghlan@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi folks,<br>
<br>
After a couple of years hiatus, I finally got back to working on the<br>
reference implementation for PEP 558, my proposal to resolve some<br>
weird interactions between closures and trace functions in CPython by<br>
formally defining the expected semantics of the locals() builtin at<br>
function scope, and then ensuring that CPython adheres to those<br>
clarified semantics.<br>
<br>
The full text of the PEP is included below, and the rendered version<br>
is available at <a href="https://www.python.org/dev/peps/pep-0558/" rel="noreferrer" target="_blank">https://www.python.org/dev/peps/pep-0558/</a><br>
<br>
The gist of the PEP is that:<br>
<br>
1. The behaviour when no trace functions are installed and no frame<br>
introspection APIs are invoked is fine, so nothing should change in<br>
that regard (except that we elevate that behaviour from "the way<br>
CPython happens to work" to "the way the language and library<br>
reference says that Python implementations are supposed to work", and<br>
make sure CPython continues to behave that way even when a trace hook<br>
*is* installed)<br>
2. If you get hold of a frame object in CPython (or another<br>
implementation that emulates the CPython frame API), whether via a<br>
trace hook or via a frame introspection API, then writing to the<br>
returned mapping will update the actual function locals and/or closure<br>
reference immediately, rather than relying on the<br>
FastToLocals/LocalsToFast APIs<br>
3. The LocalsToFast C API changes to always produce RuntimeError<br>
(since there's no way for us to make it actually work correctly and<br>
consistently in the presence of closures, and the trace hook<br>
implementation won't need it any more given the write-through proxy<br>
behaviour on frame objects' "f_locals" attribute)<br>
<br>
The reference implementation still isn't quite done yet, but it's far<br>
enough along that I'm happy with the semantics and C API updates<br>
proposed in the current version of the PEP.<br>
<br>
Cheers,<br>
Nick.<br>
<br>
P.S. I'm away this weekend, so I expect the reference implementation<br>
to be done late next week, and I'd be submitting the PEP to Nathaniel<br>
for formal pronouncement at that point. However, I'm posting this<br>
thread now so that there's more time for discussion prior to the 3.8b1<br>
deadline.<br>
<br>
==============<br>
PEP: 558<br>
Title: Defined semantics for locals()<br>
Author: Nick Coghlan <<a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>><br>
BDFL-Delegate: Nathaniel J. Smith<br>
Status: Draft<br>
Type: Standards Track<br>
Content-Type: text/x-rst<br>
Created: 2017-09-08<br>
Python-Version: 3.8<br>
Post-History: 2017-09-08, 2019-05-22<br>
<br>
<br>
Abstract<br>
========<br>
<br>
The semantics of the ``locals()`` builtin have historically been underspecified<br>
and hence implementation dependent.<br>
<br>
This PEP proposes formally standardising on the behaviour of the CPython 3.6<br>
reference implementation for most execution scopes, with some adjustments to the<br>
behaviour at function scope to make it more predictable and independent of the<br>
presence or absence of tracing functions.<br>
<br>
<br>
Rationale<br>
=========<br>
<br>
While the precise semantics of the ``locals()`` builtin are nominally undefined,<br>
in practice, many Python programs depend on it behaving exactly as it behaves in<br>
CPython (at least when no tracing functions are installed).<br>
<br>
Other implementations such as PyPy are currently replicating that behaviour,<br>
up to and including replication of local variable mutation bugs that<br>
can arise when a trace hook is installed [1]_.<br>
<br>
While this PEP considers CPython's current behaviour when no trace hooks are<br>
installed to be acceptable (and largely desirable), it considers the current<br>
behaviour when trace hooks are installed to be problematic, as it causes bugs<br>
like [1]_ *without* even reliably enabling the desired functionality of allowing<br>
debuggers like ``pdb`` to mutate local variables [3]_.<br>
<br>
<br>
Proposal<br>
========<br>
<br>
The expected semantics of the ``locals()`` builtin change based on the current<br>
execution scope. For this purpose, the defined scopes of execution are:<br>
<br>
* module scope: top-level module code, as well as any other code executed using<br>
  ``exec()`` or ``eval()`` with a single namespace<br>
* class scope: code in the body of a ``class`` statement, as well as any other<br>
  code executed using ``exec()`` or ``eval()`` with separate local and global<br>
  namespaces<br>
* function scope: code in the body of a ``def`` or ``async def`` statement<br>
<br>
We also allow interpreters to define two "modes" of execution, with only the<br>
first mode being considered part of the language specification itself:<br>
<br>
* regular operation: the way the interpreter behaves by default<br>
* tracing mode: the way the interpreter behaves when a trace hook has been<br>
  registered in one or more threads via an implementation dependent mechanism<br>
  like ``sys.settrace`` ([4]_) in CPython's ``sys`` module or<br>
  ``PyEval_SetTrace`` ([5]_) in CPython's C API<br>
<br>
For regular operation, this PEP proposes elevating the current behaviour of<br>
the CPython reference implementation to become part of the language<br>
specification.<br>
<br>
For tracing mode, this PEP proposes changes to CPython's behaviour at function<br>
scope that bring the ``locals()`` builtin semantics closer to those used in<br>
regular operation, while also making the related frame API semantics clearer<br>
and easier for interactive debuggers to rely on.<br>
<br>
The proposed tracing mode changes also affect the semantics of frame object<br>
references obtained through other means, such as via a traceback, or via the<br>
``sys._getframe()`` API.<br>
<br>
<br>
New ``locals()`` documentation<br>
------------------------------<br>
<br>
The heart of this proposal is to revise the documentation for the ``locals()``<br>
builtin to read as follows:<br>
<br>
    Return a dictionary representing the current local symbol table, with<br>
    variable names as the keys, and their currently bound references as the<br>
    values. This will always be the same dictionary for a given runtime<br>
    execution frame.<br>
<br>
    At module scope, as well as when using ``exec()`` or ``eval()`` with a<br>
    single namespace, this function returns the same namespace as ``globals()``.<br>
<br>
    At class scope, it returns the namespace that will be passed to the<br>
    metaclass constructor.<br>
<br>
    When using ``exec()`` or ``eval()`` with separate local and global<br>
    namespaces, it returns the local namespace passed in to the function call.<br>
<br>
    At function scope (including for generators and coroutines), it returns a<br>
    dynamic snapshot of the function's local variables and any nonlocal cell<br>
    references. In this case, changes made via the snapshot are *not* written<br>
    back to the corresponding local variables or nonlocal cell references, and<br>
    any such changes to the snapshot will be overwritten if the snapshot is<br>
    subsequently refreshed (e.g. by another call to ``locals()``).<br>
<br>
    CPython implementation detail: the dynamic snapshot for the current frame<br>
    will be implicitly refreshed before each call to the trace function when a<br>
    trace function is active.<br>
<br>
For reference, the current documentation of this builtin reads as follows:<br>
<br>
    Update and return a dictionary representing the current local symbol table.<br>
    Free variables are returned by locals() when it is called in function<br>
    blocks, but not in class blocks.<br>
<br>
    Note: The contents of this dictionary should not be modified; changes may<br>
    not affect the values of local and free variables used by the interpreter.<br>
<br>
(In other words: the status quo is that the semantics and behaviour of<br>
``locals()`` are currently formally implementation defined, whereas the proposed<br>
state after this PEP is that the only implementation defined behaviour will be<br>
that encountered at function scope when a tracing function is defined, with the<br>
behaviour in all other cases being defined by the language and library<br>
references)<br>
<br>
<br>
Module scope<br>
------------<br>
<br>
At module scope, as well as when using ``exec()`` or ``eval()`` with a<br>
single namespace, ``locals()`` must return the same object as ``globals()``,<br>
which must be the actual execution namespace (available as<br>
``inspect.currentframe().f_locals`` in implementations that provide access<br>
to frame objects).<br>
<br>
Variable assignments during subsequent code execution in the same scope must<br>
dynamically change the contents of the returned mapping, and changes to the<br>
returned mapping must change the values bound to local variable names in the<br>
execution environment.<br>
<br>
The semantics at module scope are required to be the same in both tracing<br>
mode (if provided by the implementation) and in regular operation.<br>
<br>
To capture this expectation as part of the language specification, the following<br>
paragraph will be added to the documentation for ``locals()``:<br>
<br>
   At module scope, as well as when using ``exec()`` or ``eval()`` with a<br>
   single namespace, this function returns the same namespace as ``globals()``.<br>
<br>
This part of the proposal does not require any changes to the reference<br>
implementation - it is standardisation of the current behaviour.<br>
<br>
<br>
Class scope<br>
-----------<br>
<br>
At class scope, as well as when using ``exec()`` or ``eval()`` with separate<br>
global and local namespaces, ``locals()`` must return the specified local<br>
namespace (which may be supplied by the metaclass ``__prepare__`` method<br>
in the case of classes). As for module scope, this must be a direct reference<br>
to the actual execution namespace (available as<br>
``inspect.currentframe().f_locals`` in implementations that provide access<br>
to frame objects).<br>
<br>
Variable assignments during subsequent code execution in the same scope must<br>
change the contents of the returned mapping, and changes to the returned mapping<br>
must change the values bound to local variable names in the<br>
execution environment.<br>
<br>
The mapping returned by ``locals()`` will *not* be used as the actual class<br>
namespace underlying the defined class (the class creation process will copy<br>
the contents to a fresh dictionary that is only accessible by going through the<br>
class machinery).<br>
<br>
For nested classes defined inside a function, any nonlocal cells referenced from<br>
the class scope are *not* included in the ``locals()`` mapping.<br>
<br>
The semantics at class scope are required to be the same in both tracing<br>
mode (if provided by the implementation) and in regular operation.<br>
<br>
To capture this expectation as part of the language specification, the following<br>
two paragraphs will be added to the documentation for ``locals()``:<br>
<br>
   When using ``exec()`` or ``eval()`` with separate local and global<br>
   namespaces, [this function] returns the given local namespace.<br>
<br>
   At class scope, it returns the namespace that will be passed to the metaclass<br>
   constructor.<br>
<br>
This part of the proposal does not require any changes to the reference<br>
implementation - it is standardisation of the current behaviour.<br>
<br>
<br>
Function scope<br>
--------------<br>
<br>
At function scope, interpreter implementations are granted significant freedom<br>
to optimise local variable access, and hence are NOT required to permit<br>
arbitrary modification of local and nonlocal variable bindings through the<br>
mapping returned from ``locals()``.<br>
<br>
Historically, this leniency has been described in the language specification<br>
with the words "The contents of this dictionary should not be modified; changes<br>
may not affect the values of local and free variables used by the interpreter."<br>
<br>
This PEP proposes to change that text to instead say:<br>
<br>
    At function scope (including for generators and coroutines), [this function]<br>
    returns a dynamic snapshot of the function's local variables and any<br>
    nonlocal cell references. In this case, changes made via the snapshot are<br>
    *not* written back to the corresponding local variables or nonlocal cell<br>
    references, and any such changes to the snapshot will be overwritten if the<br>
    snapshot is subsequently refreshed (e.g. by another call to ``locals()``).<br>
<br>
    CPython implementation detail: the dynamic snapshot for the currently<br>
    executing frame will be implicitly refreshed before each call to the trace<br>
    function when a trace function is active.<br>
<br>
This part of the proposal *does* require changes to the CPython reference<br>
implementation, as while it accurately describes the behaviour in regular<br>
operation, the "write back" strategy currently used to support namespace changes<br>
from trace functions doesn't comply with it (and also causes the quirky<br>
behavioural problems mentioned in the Rationale).<br>
<br>
<br>
CPython Implementation Changes<br>
==============================<br>
<br>
The current cause of CPython's tracing mode quirks (both the side effects from<br>
simply installing a tracing function and the fact that writing values back to<br>
function locals only works for the specific function being traced) is the way<br>
that locals mutation support for trace hooks is currently implemented: the<br>
``PyFrame_LocalsToFast`` function.<br>
<br>
When a trace function is installed, CPython currently does the following for<br>
function frames (those where the code object uses "fast locals" semantics):<br>
<br>
1. Calls ``PyFrame_FastToLocals`` to update the dynamic snapshot<br>
2. Calls the trace hook (with tracing of the hook itself disabled)<br>
3. Calls ``PyFrame_LocalsToFast`` to capture any changes made to the dynamic<br>
   snapshot<br>
<br>
This approach is problematic for a few different reasons:<br>
<br>
* Even if the trace function doesn't mutate the snapshot, the final step resets<br>
  any cell references back to the state they were in before the trace function<br>
  was called (this is the root cause of the bug report in [1]_)<br>
* If the trace function *does* mutate the snapshot, but then does something<br>
  that causes the snapshot to be refreshed, those changes are lost (this is<br>
  one aspect of the bug report in [3]_)<br>
* If the trace function attempts to mutate the local variables of a frame other<br>
  than the one being traced (e.g. ``frame.f_back.f_locals``), those changes<br>
  will almost certainly be lost (this is another aspect of the bug report in<br>
  [3]_)<br>
* If a ``locals()`` reference is passed to another function, and *that*<br>
  function mutates the snapshot namespace, then those changes *may* be written<br>
  back to the execution frame *if* a trace hook is installed<br>
<br>
The proposed resolution to this problem is to take advantage of the fact that<br>
whereas functions typically access their *own* namespace using the language<br>
defined ``locals()`` builtin, trace functions necessarily use the implementation<br>
dependent ``frame.f_locals`` interface, as a frame reference is what gets<br>
passed to hook implementations.<br>
<br>
Instead of being a direct reference to the dynamic snapshot returned by<br>
``locals()``, ``frame.f_locals`` will be updated to instead return a dedicated<br>
proxy type (implemented as a private subclass of the existing<br>
``types.MappingProxyType``) that has two internal attributes not exposed as<br>
part of either the Python or public C API:<br>
<br>
* *mapping*: the dynamic snapshot that is returned by the ``locals()`` builtin<br>
* *frame*: the underlying frame that the snapshot is for<br>
<br>
``__setitem__`` and ``__delitem__`` operations on the proxy will affect not only<br>
the dynamic snapshot, but *also* the corresponding fast local or cell reference<br>
on the underlying frame.<br>
<br>
The ``locals()`` builtin will be made aware of this proxy type, and continue to<br>
return a reference to the dynamic snapshot rather than to the write-through<br>
proxy.<br>
<br>
At the C API layer, ``PyEval_GetLocals()`` will implement the same semantics<br>
as the Python level ``locals()`` builtin, and a new<br>
``PyFrame_GetPyLocals(frame)`` accessor API will be provided to allow the<br>
function level proxy bypass logic to be encapsulated entirely inside the frame<br>
implementation.<br>
<br>
The C level equivalent of accessing ``pyframe.f_locals`` in Python will be a<br>
new ``PyFrame_GetLocalsAttr(frame)`` API. Like the Python level descriptor, the<br>
new API will implicitly refresh the dynamic snapshot at function scope before<br>
returning a reference to the write-through proxy.<br>
<br>
The ``PyFrame_LocalsToFast()`` function will be changed to always emit<br>
``RuntimeError``, explaining that it is no longer a supported operation, and<br>
affected code should be updated to rely on the write-through tracing mode<br>
proxy instead.<br>
<br>
<br>
Design Discussion<br>
=================<br>
<br>
Ensuring ``locals()`` returns a shared snapshot at function scope<br>
-----------------------------------------------------------------<br>
<br>
The ``locals()`` builtin is a required part of the language, and in the<br>
reference implementation it has historically returned a mutable mapping with<br>
the following characteristics:<br>
<br>
* each call to ``locals()`` returns the *same* mapping<br>
* for namespaces where ``locals()`` returns a reference to something other than<br>
  the actual local execution namespace, each call to ``locals()`` updates the<br>
  mapping with the current state of the local variables and any referenced<br>
  nonlocal cells<br>
* changes to the returned mapping *usually* aren't written back to the<br>
  local variable bindings or the nonlocal cell references, but write backs<br>
  can be triggered by doing one of the following:<br>
<br>
  * installing a Python level trace hook (write backs then happen whenever<br>
    the trace hook is called)<br>
  * running a function level wildcard import (requires bytecode<br>
injection in Py3)<br>
  * running an ``exec`` statement in the function's scope (Py2 only, since<br>
    ``exec`` became an ordinary builtin in Python 3)<br>
<br>
The proposal in this PEP aims to retain the first two properties (to maintain<br>
backwards compatibility with as much code as possible) while ensuring that<br>
simply installing a trace hook can't enable rebinding of function locals via<br>
the ``locals()`` builtin (whereas enabling rebinding via<br>
``frame.f_locals`` inside the tracehook implementation is fully intended).<br>
<br>
<br>
Keeping ``locals()`` as a dynamic snapshot at function scope<br>
------------------------------------------------------------<br>
<br>
It would theoretically be possible to change the semantics of the ``locals()``<br>
builtin to return the write-through proxy at function scope, rather than<br>
continuing to return a dynamic snapshot.<br>
<br>
This PEP doesn't (and won't) propose this as it's a backwards incompatible<br>
change in practice, even though code that relies on the current behaviour is<br>
technically operating in an undefined area of the language specification.<br>
<br>
Consider the following code snippet::<br>
<br>
    def example():<br>
        x = 1<br>
        locals()["x"] = 2<br>
        print(x)<br>
<br>
Even with a trace hook installed, that function will consistently print ``1``<br>
on the current reference interpreter implementation::<br>
<br>
    >>> example()<br>
    1<br>
    >>> import sys<br>
    >>> def basic_hook(*args):<br>
    ...     return basic_hook<br>
    ...<br>
    >>> sys.settrace(basic_hook)<br>
    >>> example()<br>
    1<br>
<br>
Similarly, ``locals()`` can be passed to the ``exec()`` and ``eval()`` builtins<br>
at function scope without risking unexpected rebinding of local variables.<br>
<br>
Provoking the reference interpreter into incorrectly mutating the local variable<br>
state requires a more complex setup where a nested function closes over a<br>
variable being rebound in the outer function, and due to the use of either<br>
threads, generators, or coroutines, it's possible for a trace function to start<br>
running for the nested function before the rebinding operation in the outer<br>
function, but finish running after the rebinding operation has taken place (in<br>
which case the rebinding will be reverted, which is the bug reported in [1]_).<br>
<br>
In addition to preserving the de facto semantics which have been in place since<br>
PEP 227 introduced nested scopes in Python 2.1, the other benefit of restricting<br>
the write-through proxy support to the implementation-defined frame object API<br>
is that it means that only interpreter implementations which emulate the full<br>
frame API need to offer the write-through capability at all, and that<br>
JIT-compiled implementations only need to enable it when a frame introspection<br>
API is invoked, or a trace hook is installed, not whenever ``locals()`` is<br>
accessed at function scope.<br>
<br>
<br>
What happens with the default args for ``eval()`` and ``exec()``?<br>
-----------------------------------------------------------------<br>
<br>
These are formally defined as inheriting ``globals()`` and ``locals()`` from<br>
the calling scope by default.<br>
<br>
There isn't any need for the PEP to change these defaults, so it doesn't.<br>
<br>
<br>
Changing the frame API semantics in regular operation<br>
-----------------------------------------------------<br>
<br>
Earlier versions of this PEP proposed having the semantics of the frame<br>
``f_locals`` attribute depend on whether or not a tracing hook was currently<br>
installed - only providing the write-through proxy behaviour when a tracing hook<br>
was active, and otherwise behaving the same as the ``locals()`` builtin.<br>
<br>
That was adopted as the original design proposal for a couple of key reasons,<br>
one pragmatic and one more philosophical:<br>
<br>
* Object allocations and method wrappers aren't free, and tracing functions<br>
  aren't the only operations that access frame locals from outside the function.<br>
  Restricting the changes to tracing mode meant that the additional memory and<br>
  execution time overhead of these changes would as close to zero in regular<br>
  operation as we can possibly make them.<br>
* "Don't change what isn't broken": the current tracing mode problems are caused<br>
  by a requirement that's specific to tracing mode (support for external<br>
  rebinding of function local variable references), so it made sense to also<br>
  restrict any related fixes to tracing mode<br>
<br>
However, actually attempting to implement and document that dynamic approach<br>
highlighted the fact that it makes for a really subtle runtime state dependent<br>
behaviour distinction in how ``frame.f_locals`` works, and creates several<br>
new edge cases around how ``f_locals`` behaves as trace functions are added<br>
and removed.<br>
<br>
Accordingly, the design was switched to the current one, where<br>
``frame.f_locals`` is always a write-through proxy, and ``locals()`` is always<br>
a dynamic snapshot, which is both simpler to implement and easier to explain.<br>
<br>
Regardless of how the CPython reference implementation chooses to handle this,<br>
optimising compilers and interpreters also remain free to impose additional<br>
restrictions on debuggers, by making local variable mutation through frame<br>
objects an opt-in behaviour that may disable some optimisations (just as the<br>
emulation of CPython's frame API is already an opt-in flag in some Python<br>
implementations).<br>
<br>
<br>
Historical semantics at function scope<br>
--------------------------------------<br>
<br>
The current semantics of mutating ``locals()`` and ``frame.f_locals`` in CPython<br>
are rather quirky due to historical implementation details:<br>
<br>
* actual execution uses the fast locals array for local variable bindings and<br>
  cell references for nonlocal variables<br>
* there's a ``PyFrame_FastToLocals`` operation that populates the frame's<br>
  ``f_locals`` attribute based on the current state of the fast locals array<br>
  and any referenced cells. This exists for three reasons:<br>
<br>
  * allowing trace functions to read the state of local variables<br>
  * allowing traceback processors to read the state of local variables<br>
  * allowing ``locals()`` to read the state of local variables<br>
* a direct reference to ``frame.f_locals`` is returned from ``locals()``, so if<br>
  you hand out multiple concurrent references, then all those references will be<br>
  to the exact same dictionary<br>
* the two common calls to the reverse operation, ``PyFrame_LocalsToFast``, were<br>
  removed in the migration to Python 3: ``exec`` is no longer a statement (and<br>
  hence can no longer affect function local namespaces), and the compiler now<br>
  disallows the use of ``from module import *`` operations at function scope<br>
* however, two obscure calling paths remain: ``PyFrame_LocalsToFast`` is called<br>
  as part of returning from a trace function (which allows debuggers to make<br>
  changes to the local variable state), and you can also still inject the<br>
  ``IMPORT_STAR`` opcode when creating a function directly from a code object<br>
  rather than via the compiler<br>
<br>
This proposal deliberately *doesn't* formalise these semantics as is, since they<br>
only make sense in terms of the historical evolution of the language and the<br>
reference implementation, rather than being deliberately designed.<br>
<br>
<br>
Implementation<br>
==============<br>
<br>
The reference implementation update is in development as a draft pull<br>
request on GitHub ([6]_).<br>
<br>
<br>
Acknowledgements<br>
================<br>
<br>
Thanks to Nathaniel J. Smith for proposing the write-through proxy idea in<br>
[1]_ and pointing out some critical design flaws in earlier iterations of the<br>
PEP that attempted to avoid introducing such a proxy.<br>
<br>
<br>
References<br>
==========<br>
<br>
.. [1] Broken local variable assignment given threads + trace hook + closure<br>
   (<a href="https://bugs.python.org/issue30744" rel="noreferrer" target="_blank">https://bugs.python.org/issue30744</a>)<br>
<br>
.. [2] Clarify the required behaviour of ``locals()``<br>
   (<a href="https://bugs.python.org/issue17960" rel="noreferrer" target="_blank">https://bugs.python.org/issue17960</a>)<br>
<br>
.. [3] Updating function local variables from pdb is unreliable<br>
   (<a href="https://bugs.python.org/issue9633" rel="noreferrer" target="_blank">https://bugs.python.org/issue9633</a>)<br>
<br>
.. [4] CPython's Python API for installing trace hooks<br>
   (<a href="https://docs.python.org/dev/library/sys.html#sys.settrace" rel="noreferrer" target="_blank">https://docs.python.org/dev/library/sys.html#sys.settrace</a>)<br>
<br>
.. [5] CPython's C API for installing trace hooks<br>
   (<a href="https://docs.python.org/3/c-api/init.html#c.PyEval_SetTrace" rel="noreferrer" target="_blank">https://docs.python.org/3/c-api/init.html#c.PyEval_SetTrace</a>)<br>
<br>
.. [6] PEP 558 reference implementation<br>
   (<a href="https://github.com/python/cpython/pull/3640/files" rel="noreferrer" target="_blank">https://github.com/python/cpython/pull/3640/files</a>)<br>
<br>
<br>
Copyright<br>
=========<br>
<br>
This document has been placed in the public domain.<br>
<br>
<br>
-- <br>
Nick Coghlan   |   <a href="mailto:ncoghlan@gmail.com" target="_blank">ncoghlan@gmail.com</a>   |   Brisbane, Australia<br>
_______________________________________________<br>
Python-Dev mailing list<br>
<a href="mailto:Python-Dev@python.org" target="_blank">Python-Dev@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-dev" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-dev</a><br>
Unsubscribe: <a href="https://mail.python.org/mailman/options/python-dev/guido%40python.org" rel="noreferrer" target="_blank">https://mail.python.org/mailman/options/python-dev/guido%40python.org</a><br>
</blockquote></div><br clear="all"><br>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div>--Guido van Rossum (<a href="http://python.org/~guido" target="_blank">python.org/~guido</a>)</div><div><i style="font-family:Arial,Helvetica,sans-serif;font-size:small;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255);color:rgb(136,136,136)"><span>Pronouns</span>: he/him/his </i><a href="http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-change-the-world/" style="color:rgb(17,85,204);font-family:Arial,Helvetica,sans-serif;font-size:small;font-style:normal;font-weight:400;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(255,255,255)" target="_blank"><i>(why is my <span>pronoun</span> here?)</i></a></div></div></div>