Do you absolutely have to "get in" after the return statement?
If changes allowed before the return
statement, sys.settrace()
is all you need.
Getting the value after return
:
I think, in stackless Python, you should be able to do that. "threads" can be pickled in stackless, and about-to-be-returned value, aka top of value stack, ought to be there.
In CPython, I couldn't find a way peek at top of value stack yet.
- dynamically changing frame.f_lasti is not allowed
- dynamically changing frame.f_code is not allowed
- dynamically changing frame.f_trace is allowed but doesn't seem to help
- set tracing function from within finally block doesn't catch actual return event after return statement was "already executed"
- with statment doesn't catch return value
- I assume caller ignores f's return value, thus introspection or tracing the caller doesn't help
- I assume whatever() has side effects and cannot be called again
- debuggers, at least those that I tried, don't get return value (?); debuggers written in Python use sys.settrace and/or last exception, neither of these contains return value on stack.
Of course, everything is possible with ctypes
or C-level extension, here's a quick demo:
"""
valuestack.py: Demo reading values from Python value stack
Offsets are derived for CPython-2.7.2, 64-bit, production build
"""
import ctypes, inspect, random
def id2obj(i):
"""convert CPython `id` back to object ref, by temporary pointer swap"""
tmp = None,
try:
ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = i
return tmp[0]
finally:
ctypes.cast(id(tmp), ctypes.POINTER(ctypes.c_ulong))[3] = id(None)
def introspect():
"""pointer on top of value stack is id of the object about to be returned
FIXME adjust for sum(vars, locals) in introspected function
"""
fr = inspect.stack()[1][0]
print "caught", id2obj(ctypes.cast(id(fr), ctypes.POINTER(ctypes.c_ulong))[47])
def value():
tmp = random.random()
print "return", tmp
return tmp
def foo():
try:
return value()
finally:
introspect()
if __name__ == "__main__":
foo()
Works with Python-2.7.2 in 64-bit mode as shipped with osx:
air:~ dima$ python valuestack.py
return 0.959725159294
caught 0.959725159294