Injecting new names into the above frame

P

Peter Waller

Dear Pythoners,

I know this will probably be perceived as 'evil voodoo', and fair
enough: it probably is. I guess it is unpythonic.

... but I want to know how to do it anyway - mostly for my own
interest.

Consider the following snippet of code:

---
def Get( *names ):
if not names: return None

frame = sys._getframe(1)
prevFrameLocals = frame.f_locals

for name in names:
prevFrameLocals[ name ] = FetchObjectNamed( name )

Get("a", "b", "c")

print a, b, c
---

FetchObjectNamed() is an arbitrary function which takes a string and
returns an object it got from some store somewhere.

This works fine at the module level, because names in the locals/
globals dictionary can be played with in this way. The idea is to save
lots of typing, i.e.

a, b, c = Get("a","b","c")

...gets frustrating after much typing for many objects with long names.
This is just an example, there are other instances I have where it
would be nice to inject names into the frame above.

Of course, we hit a road block when we call 'Get' from a function
rather than a module, because the locals dictionary does not get
copied back into the code object automatically, so we have to add this
snippet before the Get() function returns:

from ctypes import pythonapi, py_object, c_int
pythonapi.PyFrame_LocalsToFast( py_object( frame ), 1 )

This copies back the names into the code object, and works fine.. that
is, if the names already exist within the code object.

def MyFunction():
a = None
Get("a")
print a # Works
Get("b")
print b # Name error, b is undefined

Is there any way for Get() to define a new variable within
MyFunction's code object? Or is there any programmatic way to, at
runtime, insert new names into functions?

I don't care how hacky it is and whether it requires making calls to
python's internals with ctypes - maybe the whole code object needs to
be replaced? is it even possible to do that when the Get() function is
about to return to this new code object?

Cheers,

- Peter
 
C

Carsten Haese

Peter said:
Dear Pythoners,

I know this will probably be perceived as 'evil voodoo', and fair
enough: it probably is. I guess it is unpythonic.

.. but I want to know how to do it anyway - mostly for my own
interest.

Well, if you're really just asking out of curiosity, it should be
sufficient to tell you that this is not possible.
[...] we have to add this
snippet before the Get() function returns:

from ctypes import pythonapi, py_object, c_int
pythonapi.PyFrame_LocalsToFast( py_object( frame ), 1 )

This copies back the names into the code object, and works fine.. that
is, if the names already exist within the code object.

def MyFunction():
a = None
Get("a")
print a # Works
Get("b")
print b # Name error, b is undefined

The answer to why this doesn't work lies in the disassembly of that
function:
0 LOAD_CONST 0 (0)
3 STORE_FAST 0 (0)
6 LOAD_GLOBAL 1 (1)
9 LOAD_CONST 1 (1)
12 CALL_FUNCTION 1
15 POP_TOP
16 LOAD_FAST 0 (0) <- This is a
19 PRINT_ITEM
20 PRINT_NEWLINE
21 LOAD_GLOBAL 1 (1)
24 LOAD_CONST 2 (2)
27 CALL_FUNCTION 1
30 POP_TOP
31 LOAD_GLOBAL 2 (2) <- This is b
34 PRINT_ITEM
35 PRINT_NEWLINE
36 LOAD_CONST 0 (0)
39 RETURN_VALUE

Since you have an assignment to the name a, a is recognized as a local
name at compile time. b is not recognized as a local name at compile
time, so even if you inject a value for b into the locals dictionary,
the byte code still looks up the value as a global name.
Is there any way for Get() to define a new variable within
MyFunction's code object? Or is there any programmatic way to, at
runtime, insert new names into functions?

Not without making fundamental changes to Python itself. The fact that
this isn't possible is a feature, in my opinion. I like the fact that I
can call a function and be *absolutely certain* that it's not going to
pollute my local namespace.

Hope this helps,
 
P

Peter Waller

The answer to why this doesn't work lies in the disassembly of that
function:

This makes me want to ask: is it difficult to modify a function's
code? Even if it weren't possible whilst the function was executing
(because then I would be returning to a function I just created, I
guess?) Could I make another function which converted this LOAD_GLOBAL
to LOAD_FAST?

I agree that in general a function should have no side effects and I
would like to know exactly what it is going to do, and that what I'm
doing is unpythonic.

My curiosity extends because I would like to get to know a bit more
about python's internals. My gut tells me there has to be a way to
achieve what I'm after - even if it the result is extremely ugly.
Maybe it might involve applying a decorator to functions who use this
functionality, who change the code so that these calls work?

Cheers for your reply,

- Pete
 
M

Michele Simionato

This makes me want to ask: is it difficult to modify a function's
code?

No, it is not difficult. Look at the byteplay module:
it makes possible all kinds of dirty hacks.
 
B

Boris Borcic

Why don't you use import and __import__() ? - They seem designed for such an
application.

I mean, I am not against vicious hacks for the fun of them, but not if they
serve the illusion that what they do can't (easily) be achieved other ways.

Cheers, BB
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,228
Members
46,817
Latest member
AdalbertoT

Latest Threads

Top