I want to have a piece of code run after each line of another block of code. For example, want to be able to evaluate a global variable before or after executing the next line of a function.
For example, below I try to print 'hello' before each line of the foo()
function. I think that a decorator can help me but it would need some introspection feature in order to edit each line of my foo()
function and add what I want before or after it.
I am trying to perform something like this :
>>> def foo():
... print 'bar'
... print 'barbar'
... print 'barbarbar'
>>> foo()
hello
bar
hello
barbar
hello
barbarbar
How can I perform this? would the __code__
object help? do I need a decorator & introspection at the same time?
EDIT: Here is another example of the goal of this thread:
>>> def foo():
... for i in range(0,3):
... print 'bar'
>>> foo()
hello
bar
hello
bar
hello
bar
In this new case, before printing each "bar", I want to print a "hello".
The main goal of this is to be able to execute another function or test any kind of global variable before executing the next line of the code. Imagine if a global variable is True
, then the code goes to the next line; while if the global variable is False
, then it stops the function execution.
EDIT: In a way, i am looking for a tool to inject code inside another block of code.
EDIT: Thank's to unutbu i have achieved this code :
import sys
import time
import threading
class SetTrace(object):
"""
with SetTrace(monitor):
"""
def __init__(self, func):
self.func = func
def __enter__(self):
sys.settrace(self.func)
return self
def __exit__(self, ext_type, exc_value, traceback):
sys.settrace(None)
# http://effbot.org/zone/python-with-statement.htm
# When __exit__ returns True, the exception is swallowed.
# When __exit__ returns False, the exception is reraised.
# This catches Sentinel, and lets other errors through
# return isinstance(exc_value, Exception)
def monitor(frame, event, arg):
if event == "line":
if not running:
raise Exception("global running is False, exiting")
return monitor
def isRunning(function):
def defaultBehavior(*args):
with SetTrace(monitor):
ret = function(*args)
return ret
return defaultBehavior
@isRunning
def foo():
while True:
time.sleep(1)
print 'bar'
global running
running = True
thread = threading.Thread(target = foo)
thread.start()
time.sleep(3)
running = False
Perhaps you are looking for sys.settrace:
import sys
class SetTrace(object):
def __init__(self, func):
self.func = func
def __enter__(self):
sys.settrace(self.func)
return self
def __exit__(self, ext_type, exc_value, traceback):
sys.settrace(None)
def monitor(frame, event, arg):
if event == "line":
print('hello')
# print(frame.f_globals)
# print(frame.f_locals)
return monitor
def foo():
print 'bar'
print 'barbar'
print 'barbarbar'
with SetTrace(monitor):
foo()
yields
hello
bar
hello
barbar
hello
barbarbar
hello
Inside monitor
, you can access the foo
's locals and globals using frame.f_locals
and frame.f_globals
.
See this post for an example of how sys.settrace
can be used for debugging.
How to stop foo
from within monitor
:
The most graceful way to do this would be to place a conditional statement inside foo
so that foo
checks when to exit. Then you can manipulate the value of the conditional from within monitor
to control when foo
exits.
However, if you do not want to or can not change foo
, then an alternative is to raise an exception from within monitor
. The exception will bubble up through the frame stack until it is caught. If you catch it in SetTrace.__exit__
, then the flow of control will continue as though foo
had just exited.
import sys
class Sentinel(Exception): pass
class SetTrace(object):
"""
with SetTrace(monitor):
...
"""
def __init__(self, func):
self.func = func
def __enter__(self):
sys.settrace(self.func)
return self
def __exit__(self, ext_type, exc_value, traceback):
sys.settrace(None)
# http://effbot.org/zone/python-with-statement.htm
# When __exit__ returns True, the exception is swallowed.
# When __exit__ returns False, the exception is reraised.
# This catches Sentinel, and lets other errors through
return isinstance(exc_value, Sentinel)
def monitor(frame, event, arg):
if event == "line":
l = frame.f_locals
x = l.get('x', 0)
print('x = {}'.format(x))
if x > 3:
raise Sentinel()
return monitor
def foo():
x = 0
while True:
print 'bar'
x += 1
with SetTrace(monitor):
foo()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With