..you wanted it DRY? Try this then :)
class ReportExceptions(object):
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
print("Sending mail to admin about {0!r}".format(exc_value))
And use it like this:
with ReportExceptions():
# your code here..
With a context manager, you don't have to care about re-raising exceptions, saving tracebacks or other things: the __exit__()
method will be executed no matter what, passing you the necessary information. If you do nothing about it (eg. return True
) the exception will just continue its way out..
Side note: you can instantiate the context manager only once, for example:
class ReportExceptions(object):
def __init__(self, email):
self.email = email
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
print("Sending mail to {0!r} about {1!r}"
"".format(self.email, exc_value))
report_exceptions = ReportExceptions('foo@bar.com')
then:
with report_exceptions:
# ..your code here..
FYI: raising an exception with custom traceback
In case you really need to re-raise an exception, you can save the traceback and keep it for later..
try:
100 / 0
except ZeroDivisionError, e:
exc_info = sys.exc_info()
...later on...
raise exc_info[0], exc_info[1], exc_info[2]
(The syntax is actually raise expression, expression, expression
, I couln't figure out a nicer way to use the tuple directly...)
Inspecting frames along the path
You can access tb
attributes to inspect the execution frames along the path, for example the locals from the "most external" point would be tb.tb_frame.f_locals
, the inner frame is at tb.tb_next.tb_frame
, etc...
(you can also use the inspect
module, see https://stackoverflow.com/a/10115462/148845)