This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author ncoghlan
Recipients Mark.Shannon, arigo, belopolsky, benjamin.peterson, gvanrossum, ncoghlan, njs, xdegaye, xgdomingo, yselivanov
Date 2017-10-14.09:12:27
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1507972349.81.0.213398074469.issue30744@psf.upfronthosting.co.za>
In-reply-to
Content
The current pdb misbehaviour when navigating the stack is indeed a valid argument in favour of allowing immediate write-through from trace functions for regular local variables as well - I don't recall reading that bug report when Xavier first linked to it, so thanks for bringing it up again.

Another useful point that bug report highlights is that one of the assumptions I'd been making (that typical trace hooks would only need to manipulate the frame they were called for) is incorrect: via frame.f_back, the trace hook also has access to all of the parent frames, so any delayed write-back based approach would need to traverse the whole call stack to be truly robust. At that point, granting trace functions access to a write-through proxy as f_locals for *every* frame starts looking like a far more attractive option.

This suggests to me that rather than only switching behaviours while the traceback function is running, we would instead need to make it so that we check for whether or not a trace function is installed for the process whenever f_locals is looked up. If it is, and the frame uses fast locals, then f_locals will be a _FunctionLocalsProxy instance. Otherwise, it will be a regular dictionary.

Making a dynamic decision on whether to return a proxy or not is needed to handle the pdb stack traversal use case properly: debuggers are often going to be confronted with frames that were created *before* their trace hook was installed. Right now, as per the issue Xavier linked, attempted writes back to those don't tend to work properly (due to when and where the write-back happens), but dynamically upgrading to write-through proxies in the presence of a trace hook should help resolve that.

While that's a more pervasive change than what I was considering, I think the cited pdb bug provides a strong enough rationale to justify the related risk of unintended consequences.

Interestingly, I think it also gives us a way to reduce the degree to which installation of a trace hook affects the semantics of locals(): when f_locals on a frame is a _FunctionLocalsProxy instance, the locals() builtin can still bypass it and return the underlying dict. The difference between this and my earlier proposal for two different snapshot namespaces is that there'd still only be one snapshot, it's that f_locals would either reference it directly (the default state), or else via a write-through proxy (the "tracing mode" state).

I think that approach will give us the best possible outcome available:

* for code using locals(), the behaviour in non-tracing mode will be completely unchanged, and the semantic differences between tracing and non-tracing mode while a function is running will be reduced, since the post-trace-hook writeback code will be removed, and locals() itself never return a reference to the write-through proxy (only the underlying namespace snapshot). (There will still be some semantic differences, since the snapshot will be implicitly refreshed in tracing mode, whereas you have to call locals() explicitly to refresh it in non-tracing mode)

* for code using frame.f_locals, it will either behave the same as locals() if no tracing hook is installed when you retrieve a reference from the frame (again, no change from the status quo), *or* it will immediately write through to the active locals if a tracing hook *is* installed (replacing the current post-trace-hook writeback code)

(Note: if folks want to instead argue for the compatibility break option, you're going to have to write your own PEP, as I have zero plans to implement that myself, and will oppose it as a gratuitously unnecessary compatibility break if it does get proposed. Toggling this behaviour change on "Tracing or not?" won't be hard, and it ensures any  unforeseen negative outcomes will only impact situations that are currently broken anyway)
History
Date User Action Args
2017-10-14 09:12:29ncoghlansetrecipients: + ncoghlan, gvanrossum, arigo, belopolsky, benjamin.peterson, njs, xdegaye, Mark.Shannon, yselivanov, xgdomingo
2017-10-14 09:12:29ncoghlansetmessageid: <1507972349.81.0.213398074469.issue30744@psf.upfronthosting.co.za>
2017-10-14 09:12:29ncoghlanlinkissue30744 messages
2017-10-14 09:12:27ncoghlancreate