How to set custom locals for function call?

Andras Tantos python-list at andras.tantosonline.com
Tue Sep 1 21:11:51 EDT 2020


On 9/1/20 5:31 PM, Chris Angelico wrote:
> On Wed, Sep 2, 2020 at 10:23 AM Andras Tantos
> <python-list at 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




More information about the Python-list mailing list