I'm trying to override the printed output from an Exception subclass in Python after the exception has been raised and I'm having no luck getting my override to actually be called.
def str_override(self):"""Override the output with a fixed string"""return "Override!"def reraise(exception):"""Re-raise an exception and override its output"""exception.__str__ = types.MethodType(str_override, exception, type(exception))# Re-raise and remove ourselves from the stack trace.raise exception, None, sys.exc_info()[-1]def test():"""Should output "Override!" Actually outputs "Bah Humbug""""try:try:raise Exception("Bah Humbug")except Exception, e:reraise(e, "Said Scrooge")except Exception, e:print e
Any idea why this doesn't actually override the str method? Introspecting the instance variables shows that the method is actually overridden with the method but it's like Python just refuses to call it through print.
What am I missing here?
The problem is not that __str__()
doesn't get overriden (just like you've already said, it does), but rather that str(e)
(which invisibly gets called by print) is not always equivalent to e.__str__()
. More specifically, if I get it right, str()
(and other special methods, such as repr()
), won't look for str in the instance dictionary - it would only look for it in the class dictionary. At least that is the case for the so-called new-style classes (which are the only classes in Python 3.x IIRC). You can read more about it here:
http://mail.python.org/pipermail/python-bugs-list/2005-December/031438.html
If you want to change the exception error message for a reraised exception, you can do something like this instead:
def reraise(exception):"""Re-raise an exception and override its output"""exType = type(exception)newExType = type(exType.__name__ + "_Override", (exType,), { '__str__': str_override})exception.__class__ = newExType# Re-raise and remove ourselves from the stack trace.raise exception, None, sys.exc_info()[-1]
This will dynamically derive a new exception class with the str override, and change exception to be an instance of that class. Now your code should work.