Calling a hook function every time an Exception is raised
Let's say I want to be able to log to file every time any exception is raised, anywhere in my program. I don't want to modify any existing code.
Of course, this could be generalized to being able to insert a hook every time an exception is raised.
Would the following code be considered safe for doing such a thing?
class MyException(Exception):
def my_hook(self):
print('---> my_hook() was called');
def __init__(self, *args, **kwargs):
global BackupException;
self.my_hook();
return BackupException.__init__(self, *args, **kwargs);
def main():
global BackupException;
global Exception;
BackupException = Exception;
Exception = MyException;
raise Exception('Contrived Exception');
if __name__ == '__main__':
main();
Solution 1:
If you want to log uncaught exceptions, just use sys.excepthook.
I'm not sure I see the value of logging all raised exceptions, since lots of libraries will raise/catch exceptions internally for things you probably won't care about.
Solution 2:
Your code as far as I can tell would not work.
__init__
has to return None and you are trying to return an instance of backup exception. In general if you would like to change what instance is returned when instantiating a class you should override__new__
.Unfortunately you can't change any of the attributes on the
Exception
class. If that was an option you could have changedException.__new__
and placed your hook there.the "
global Exception
" trick will only work for code in the current module.Exception
is a builtin and if you really want to change it globally you need toimport __builtin__; __builtin__.Exception = MyException
Even if you changed
__builtin__.Exception
it will only affect future uses ofException
, subclasses that have already been defined will use the original Exception class and will be unaffected by your changes. You could loop overException.__subclasses__
and change the__bases__
for each one of them to insert yourException
subclass there.There are subclasses of
Exception
that are also built-in types that you also cannot modify, although I'm not sure you would want to hook any of them (thinkStopIterration
).
I think that the only decent way to do what you want is to patch the Python sources.
Solution 3:
This code will not affect any exception classes that were created before the start of main
, and most of the exceptions that happen will be of such kinds (KeyError
, AttributeError
, and so forth). And you can't really affect those "built-in exceptions" in the most important sense -- if anywhere in your code is e.g. a 1/0, the real ZeroDivisionError will be raised (by Python's own internals), not whatever else you may have bound to that exceptions' name.
So, I don't think your code can do what you want (despite all the semicolons, it's still supposed to be Python, right?) -- it could be done by patching the C sources for the Python runtime, essentially (e.g. by providing a hook potentially caught on any exception even if it's later caught) -- such a hook currently does not exist because the use cases for it would be pretty rare (for example, a StopIteration
is always raised at the normal end of every for
loop -- and caught, too; why on Earth would one want to trace that, and the many other routine uses of caught exceptions in the Python internals and standard library?!).