每次引发exception时调用一个钩子函数

比方说,我希望能够每次在我的程序中的任何地方引发exception时logging文件。 我不想修改任何现有的代码。

当然,这可以概括为能够在每次引发exception时插入钩子。

下面的代码会被认为是安全的做这样的事情?

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(); 

如果你想logging未捕获的exception,只需使用sys.excepthook即可 。

我不确定我看到logging所有引发的exception的价值,因为许多库会在内部提升/捕获exception,因为你可能不会在乎。

我所知道的代码是行不通的。

  1. __init__必须返回None,并且您试图返回备份exception的实例。 一般来说,如果您想要更改在实例化类时返回的实例,则应该覆盖__new__

  2. 不幸的是,你不能改变Exception类的任何属性。 如果这是一个选项,你可以改变Exception.__new__并把你的钩子放在那里。

  3. global Exception ”技巧只适用于当前模块中的代码。 Exception是一个内build的,如果你真的想要全局改变它,你需要import __builtin__; __builtin__.Exception = MyException import __builtin__; __builtin__.Exception = MyException

  4. 即使你改变了__builtin__.Exception它只会影响Exception将来使用,已经定义的子类将使用原来的Exception类,并且不会受到你的改变的影响。 您可以遍历Exception.__subclasses__ __bases__ ,并为其中的每一个更改__bases__以在其中插入您的Exception子类。

  5. 还有一些Exception子类也是内build的types,你也不能修改,虽然我不确定你想要钩住它们中的任何一个(认为StopIterration )。

我认为唯一可行的方法就是修补Python源代码。

这段代码不会影响在main开始之前创build的任何exception类,而且大部分发生的exception都是这种types( KeyErrorAttributeError等等)。 从最重要的意义上说,你不能真正影响这些“内置的exception” – 如果你的代码中的任何地方是例如1/0, 真正的 ZeroDivisionError将被引发(由Python自己的内部), 而不是你可能已经绑定了这个例外的名字。

所以,我不认为你的代码可以做你想做的事情(尽pipe所有的分号,它仍然是Python,对吧?) – 它可以通过修补Python运行时的C源,本质上(例如通过提供一个可能被任何exception捕获的钩子,即使它后来被捕获) – 这样的钩子当前不存在,因为它的用例将是非常罕见的(例如, StopIteration总是在每个for循环的正常结束- 也被抓到了;为什么在地球上会想要追踪这个 ,以及在Python内部和标准库中捕获exception的许多其他常规用法?!)。

下载pypy和仪器。