<div dir="auto"><div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri., 31 May 2019, 5:20 am Xavier de Gaye, <<a href="mailto:xdegaye@gmail.com">xdegaye@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Currently f_locals is documented as readonly [1].<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Read-only in the sense that you can't rebind it to point to a different object - the dict it points to is mutable.</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
The PEP says:<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>
Do these edge cases still exist when f_locals write access is restricted to code executed by the tracing function (which is more restrictive than 'tracing mode') ?</blockquote></div></div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
We can use the condition frame->f_trace not NULL and tstate->tracing true (tstate being a pointer to the PyThreadState structure) to know when code is executed by the tracing function [2]:<br>
* The condition on tstate->tracing allows to figure out if we are running a frame executed by the trace function as opposed to a frame that is being traced or a frame executed in 'regular operation'.<br>
* The condition on frame->f_trace removes the ambiguity whether tstate->tracing is set by a tracing function or by a profiling function.<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"></blockquote></div></div><div dir="auto">Always creating the proxy and sometimes bypassing it and returning the snapshot instead would indeed have fewer edge cases than sometimes storing the snapshot directly on the frame object without creating the proxy at all.</div><div dir="auto"><br></div><div dir="auto">It's still significantly harder to document than "frame.f_locals references a proxy, locals() creates a snapshot", though.</div><div dir="auto"><br></div><div dir="auto">Cheers,</div><div dir="auto">Nick.</div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
</blockquote></div></div></div>