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();
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 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.
Method 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
Exceptionclass. 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.Exceptionis a builtin and if you really want to change it globally you need toimport __builtin__; __builtin__.Exception = MyException -
Even if you changed
__builtin__.Exceptionit 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 yourExceptionsubclass there. -
There are subclasses of
Exceptionthat 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.
Method 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?!).
Method 4
Download pypy and instrument it.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0