The code in this notebook helps with handling errors. Normally, an error in notebook code causes the execution of the code to stop; while an infinite loop in notebook code causes the notebook to run without end. This notebook provides two classes to help address these concerns.
Prerequisites
with
statementThe class ExpectError
allows to express that some code produces an exception. A typical usage looks as follows:
from ExpectError import ExpectError
with ExpectError():
function_that_is_supposed_to_fail()
If an exception occurs, it is printed on standard error; yet, execution continues.
import traceback
import sys
class ExpectError(object):
def __init__(self, print_traceback=True, mute=False):
self.print_traceback = print_traceback
self.mute = mute
# Begin of `with` block
def __enter__(self):
return self
# End of `with` block
def __exit__(self, exc_type, exc_value, tb):
if exc_type is None:
# No exception
return
# An exception occurred
if self.print_traceback:
lines = ''.join(
traceback.format_exception(
exc_type,
exc_value,
tb)).strip()
else:
lines = traceback.format_exception_only(
exc_type, exc_value)[-1].strip()
if not self.mute:
print(lines, "(expected)", file=sys.stderr)
return True # Ignore it
Here's an example:
def fail_test():
# Trigger an exception
x = 1 / 0
with ExpectError():
fail_test()
with ExpectError(print_traceback=False):
fail_test()
The class ExpectTimeout(seconds)
allows to express that some code may run for a long or infinite time; execution is thus interrupted after seconds
seconds. A typical usage looks as follows:
from ExpectError import ExpectTimeout
with ExpectTimeout(2) as t:
function_that_is_supposed_to_hang()
If an exception occurs, it is printed on standard error (as with ExpectError
); yet, execution continues.
Should there be a need to cancel the timeout within the with
block, t.cancel()
will do the trick.
The implementation uses sys.settrace()
, as this seems to be the most portable way to implement timeouts. It is not very efficient, though. Also, it only works on individual lines of Python code and will not interrupt a long-running system function.
import sys
import time
try:
# Should be defined in Python 3
x = TimeoutError
except:
# For Python 2
class TimeoutError(Exception):
def __init__(self, value="Timeout"):
self.value = value
def __str__(self):
return repr(self.value)
class ExpectTimeout(object):
def __init__(self, seconds, print_traceback=True, mute=False):
self.seconds_before_timeout = seconds
self.original_trace_function = None
self.end_time = None
self.print_traceback = print_traceback
self.mute = mute
# Tracing function
def check_time(self, frame, event, arg):
if self.original_trace_function is not None:
self.original_trace_function(frame, event, arg)
current_time = time.time()
if current_time >= self.end_time:
raise TimeoutError
return self.check_time
# Begin of `with` block
def __enter__(self):
start_time = time.time()
self.end_time = start_time + self.seconds_before_timeout
self.original_trace_function = sys.gettrace()
sys.settrace(self.check_time)
return self
# End of `with` block
def __exit__(self, exc_type, exc_value, tb):
self.cancel()
if exc_type is None:
return
# An exception occurred
if self.print_traceback:
lines = ''.join(
traceback.format_exception(
exc_type,
exc_value,
tb)).strip()
else:
lines = traceback.format_exception_only(
exc_type, exc_value)[-1].strip()
if not self.mute:
print(lines, "(expected)", file=sys.stderr)
return True # Ignore it
def cancel(self):
sys.settrace(self.original_trace_function)
Here's an example:
def long_running_test():
print("Start")
for i in range(10):
time.sleep(1)
print(i, "seconds have passed")
print("End")
with ExpectTimeout(5, print_traceback=False):
long_running_test()
Note that it is possible to nest multiple timeouts.
with ExpectTimeout(5):
with ExpectTimeout(3):
long_running_test()
long_running_test()
That's it, folks – enjoy!