On 9/1/20 5:31 PM, Chris Angelico wrote:
On Wed, Sep 2, 2020 at 10:23 AM Andras Tantos
<python-l...@andras.tantosonline.com> wrote:
I did see these macros in the CPython source code. What it seems to
imply is that if I wanted to do what I intend, that is to hook every
local variable assignment, I would have to modify the AST. That seems
rather more effort than simply supplying my own locals dictionary. I
will try to look myself as well, but do you have an inkling as to how
complex would it be to convince CPython to optimize locals only if no
custom locals is provided during exec?

To my untrained eye, it seems to be an unsafe optimization that breaks
the contract of the exec API.
By the time you're calling exec, it's too late to decide whether to
optimize, since you're using a precompiled code object. If you want
that behaviour, try exec'ing a string of code instead.

ChrisA

I guess, it depends. I can think of several ways of dealing with this.

Probably the highest performant version would be to generate byte-code along these lines for every function body:

    if <<custom locals is specified>>:
         <<code with no SETLOCAL/GETLOCAL optimization>>
    else:
         <<code with SETLOCAL/GETLOCAL optimization>>

This of course makes the byte-code about twice as large as today.

One could also patch up the SETLOCAL/GETLOCAL macros to test for this condition, in which case no byte-code changes are needed, but performance would degrade because of all the - normally unnecessary - checks.

There's also a possibility of detecting this condition within the underlying C implementation of exec() and trigger a re-generation of the byte-code from source.

I don't feel confident enough to even attempt anything but the middle approach, but even then: this is a change to CPython, obviously something that needs community vetting and approval.

Reacting to your last sentence: I have investigated how to get the source code, given a function object, but the best I could come up with is:

    1. Retrieve the source code for the function, using inspect.getsource()

    2. massage it to contain only the function body (that is to remove the def ... line)

    3. pass that to exec.

With that, I finally have been able to get locals work, but here, I'm back to the other case, where I essentially promoted the body of the function into the global namespace, so now locals() and globals() are the same thing. This is hardly the same context in which the function would originally execute.

Andras


--
https://mail.python.org/mailman/listinfo/python-list

Reply via email to