Given the frame object (as returned by sys._getframe, for instance), can I get the underlying callable object?
Code explanation:
def foo():
frame = s
Not really an answer, but a comment. I'd add it as a comment, but I don't have enough "reputation points".
For what it's worth, here's a reasonable (I think) use case for wanting to do this sort of thing.
My app uses gtk, and spins a lot of threads. As anyone whose done both of these at once knows, you can't touch the GUI outside the main thread. A typical workaround is to hand the callable that will touch the GUI to idle_add()
, which will run it later, in the main thread, where it is safe. So I have a lot of occurrences of:
def threaded_gui_func(self, arg1, arg2):
if threading.currentThread().name != 'MainThread':
gobject.idle_add(self.threaded_gui_func, arg1, arg2)
return
# code that touches the GUI
It would be just a bit shorter and easier (and more conducive to cut-n-paste) if I could just do
def thread_gui_func(self, arg1, arg2):
if idleIfNotMain(): return
# code that touches the GUI
where idleIfNotMain() just returns False if we are in the main thread, but if not, it uses inspect (or whatever) to figure out the callable and args to hand off to idle_add()
, then returns True. Getting the args I can figure out. Getting the callable appears not to be too easy. :-(
To support all cases, including the function being part of a class or just a global function, there is no straight-forward way of doing this. You might be able to get the complete call stack and iterate your way down through globals()
, but it wouldn't be nice...
The closest I can get you is this:
import sys, types
def magic():
# Get the frame before the current one (i.e. frame of caller)
frame = sys._getframe(1)
# Default values and closure is lost here (because they belong to the
# function object.)
return types.FunctionType(frame.f_code, frame.f_globals)
class MyClass(object):
def foo(self, bar='Hello World!'):
print bar
return magic()
test = MyClass()
new_foo = test.foo()
new_foo(test, 'Good Bye World!')
You'll be executing the exact same code, but it'll be in a new code wrapper (e.g., FunctionType
.)
I suspect you want to be able to restore the state of your application based on a stack... Here's something that will at least call the functions as similarly as possible to the original calls (the closure is still left out, because if you could get closures from the frames, getting the function that was called would be pretty easy):
import sys, types
class MyClass(object):
def __init__(self, temp):
self.temp = temp
def foo(self, bar):
print self.temp, bar
return sys._getframe()
def test(hello):
print hello, 'World!'
return sys._getframe()
def recall(frame):
code = frame.f_code
fn = types.FunctionType(
code, frame.f_globals, code.co_name,
# This is one BIG assumption that arguments are always last.
tuple(frame.f_locals.values()[-code.co_argcount:]))
return fn()
test1 = MyClass('test1')
frame1 = test1.foo('Hello World!')
test2 = MyClass('test2')
frame2 = test2.foo('Good Bye World!')
frame3 = test2.foo('Sayonara!')
frame4 = test('HI')
print '-'
recall(frame4)
recall(frame3)
recall(frame2)
recall(frame1)
A little ugly but here it is:
frame.f_globals[frame.f_code.co_name]
Full example:
#!/usr/bin/env python
import sys
def foo():
frame = sys._getframe()
x = frame.f_globals[frame.f_code.co_name]
print foo is x
foo()
Prints 'True'.