views:

93

answers:

3

Does python have an equivalent to Tcl's uplevel command? For those who don't know, the "uplevel" command lets you run code in the context of the caller. Here's how it might look in python:

def foo():
    answer = 0
    print "answer is", answer # should print 0
    bar()
    print "answer is", answer # should print 42


def bar():
    uplevel("answer = 42")

It's more than just setting variables, however, so I'm not looking for a solution that merely alters a dictionary. I want to be able to execute any code.

+1  A: 

In general, what you ask is not possible (with the results you no doubt expect). E.g., imagine the "any code" is x = 23. Will this add a new variable x to your caller's set of local variables, assuming you do find a black-magical way to execute this code "in the caller"? No it won't -- the crucial optimization performed by the Python compiler is to define once and for all, when def executes, the exact set of local variables (all the barenames that get assigned, or otherwise bound, in the function's body), and turn every access and setting to those barenames into very fast indexing into the stackframe. (You could systematically defeat that crucial optimization e.g. by having an exec '' at the start of every possible caller -- and see your system's performance crash through the floor in consequence).

Except for assigning to the caller's local barenames, exec thecode in thelocals, theglobals may do roughly what you want, and the inspect module lets you get the locals and globals of the caller in a semi-reasonable way (in as far as deep black magic -- which would make me go postal on any coworker suggesting it be perpetrated in production code -- can ever be honored with the undeserved praise of calling it "semi-reasonable", that is;-).

But you do specify "I want to be able to execute any code." and the only solution to that unambiguous specification (and thanks for being so precise, as it makes answering easier!) is: then, use a different programming language.

Alex Martelli
Modyfing locals is indeed not possible, at least not without going to C level. Let me just add those two links for further read: http://bugs.python.org/issue1654367 and http://mail.python.org/pipermail/python-dev/2007-February/070885.html
Michał Kwiatkowski
@Alex Martelli: this answer is a bit too preachy to be useful IMHO.
Bryan Oakley
@Bryan, you're entitled to your opinion and to express it, of course, but I am similarly entitled to disagree vehemently -- and to absolutely detest your actions, summarized as: you claim you want to run **any** code; I show you precisely that you can't do so and why; you post a self-answer (dubious practice indeed anyway) which does **not** meet your own reqs, and instead of thanking me for stopping you from wasting your time trying otherwise, criticize and downvote me. Proper non-preachy reactions would require violence, physical or at least verbal, so I'd rather be preachy instead.
Alex Martelli
@Alex Martelli: dubious to answer my own question? Doesn't SO have a badge for that? I think it's also mentioned in the FAQ it's OK. As to it not answering my original question, you're right. If I could have created a fully working answer I would, but I couldn't. I posted what I had and asked if anyone can improve on it. As for thanking you for stopping me from wasting my time.. ? My time wasn't wasted; I learned something. And how do you know it was me that downvoted you? Someone upvoted my comment so I'm not the only one who didn't like your answer.
Bryan Oakley
@Alex Martelli: I didn't upvote your answer because, frankly I wasn't sure it was authoritative or correct. Like I said, it came off a bit preachy which lead me to mistrust the answer a little. I just need facts here, not opinions.
Bryan Oakley
+1  A: 

Is the third party library written in Python? If yes, you could rewrite and rebind the function "foo" at runtime with your own implementation. Like so:

import third_party

original_foo = third_party.foo
def my_foo(*args, **kwds):
    # do your magic...
    original_foo(*args, **kwds)
third_party.foo = my_foo

I guess monkey-patching is slighly better than rewriting frame locals. ;)

Michał Kwiatkowski
yes, it is written in python. Monkeypatching is not the solution I'm looking for though.
Bryan Oakley
As Alex explained to you, you're not going to find a way to write `uplevel` because of the way CPython is implemented. You need approach the problem differently to find a solution.
Michał Kwiatkowski
@Michal Kwiatkowski: Thanks, but I'm not looking for a solution to my problem, I'm looking for the answer to the question "Does python have uplevel-like functionality?". The use of uplevel-like functionality is one of several possible solutions I'm mulling over. I'm not a newbie asking you to do my work for me, I'm just trying to gather facts about a language I do not yet have a deep understanding of.
Bryan Oakley
OK, so the answer to your question is "not in pure Python" and details are in Alex's answer. For even more detail you'd have to dig into CPython source code, which is quite readable. I'm guessing you *could* do some evil stuff on the C level (or with ctypes), but locals access is a low level implementation detail. For example, an optimizing interpreter could inline locals and there would be nothing left to assign to. It's best not to rely on them being there.
Michał Kwiatkowski
A: 

After some experimentation I have something that somewhat works. It won't let me set variables that are local, but it does let me set instance variables and call methods that belong to the caller which -- I think -- is all I need for my current problem.

Can anyone improve on this?

def uplevel(script):
    '''emulate Tcl's 'uplevel' command'''
    import sys
    f = sys._getframe(2)
    locals = f.f_locals
    globals = f.f_globals
    exec script in globals, locals
Bryan Oakley
Import should be at the top of the module and you can pass locals and globals directly, like so: "exec script in f.f_globals, f.f_locals". Other than that you obviously badly explained the problem if you don't really need the assignment to locals.
Michał Kwiatkowski
@Michal Kwiatkowski: at this point I don't know if I need to modify the locals or not. Regardless, I want to know if it's possible to do so. As for your coding style comments, yes I'm aware of all that you said. This was just a quick hack and makes the code easy to cut and paste, and arguably a bit more readable than a more condensed version.
Bryan Oakley