Вызов функции hook каждый раз, когда возникает исключение

Скажем, я хочу иметь возможность записывать в файл каждый раз, когда любое исключение возникает в моей программе. Я не хочу изменять какой-либо существующий код.

Конечно, это может быть обобщено на возможность вставлять крючок каждый раз, когда возникает исключение.

Будет ли считаться безопасным для такого действия следующий код?

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

Ответ 1

Если вы хотите регистрировать неперехваченные исключения, просто используйте sys.excepthook.

Я не уверен, что вижу значение регистрации всех возбужденных исключений, так как многие библиотеки будут вызывать/ловить исключения внутри себя для вещей, которые, вероятно, вас не интересуют.

Ответ 2

Ваш код, насколько я могу судить, не сработает.

  • __init__ должен возвращать None, и вы пытаетесь вернуть экземпляр исключения резервного копирования. В общем случае, если вы хотите изменить экземпляр экземпляра при создании экземпляра класса, вы должны переопределить __new__.

  • К сожалению, вы не можете изменить какие-либо атрибуты класса Exception. Если бы это был вариант, вы могли бы изменить Exception.__new__ и поместили туда свой крючок.

  • трюк "global Exception" будет работать только для кода в текущем модуле. Exception является встроенным, и если вы действительно хотите его изменить глобально, вам нужно import __builtin__; __builtin__.Exception = MyException

  • Даже если вы изменили __builtin__.Exception, это повлияет только на будущие использования Exception, подклассы, которые уже были определены, будут использовать исходный класс Exception и не будут затронуты вашими изменениями. Вы можете выполнить цикл Exception.__subclasses__ и изменить __bases__ для каждого из них, чтобы вставить в него подкласс Exception.

  • Существуют подклассы Exception, которые также являются встроенными типами, которые вы также не можете изменить, хотя я не уверен, что вы захотите подключить их (подумайте StopIterration).

Я думаю, что единственный достойный способ сделать то, что вы хотите, - это исправить источники Python.

Ответ 3

Этот код не будет влиять на классы исключений, которые были созданы до начала main, и большинство исключений, которые происходят, будут такого типа (KeyError, AttributeError и т.д.). И вы не можете реально повлиять на эти "встроенные исключения" в самом важном смысле - если где-нибудь в вашем коде есть, например. a 1/0, реальный ZeroDivisionError будет поднят (с помощью собственных внутренних элементов Python), а не все, что вы могли бы связать с этим именем исключений.

Итак, я не думаю, что ваш код может делать то, что вы хотите (несмотря на все точки с запятой, он все еще должен быть Python, правильно?) - это может быть сделано путем исправления исходных текстов C для среды выполнения Python, по существу (например, при условии, что крюк потенциально попал в любое исключение, даже если он был позже пойман) - такого крючка в настоящее время не существует, потому что варианты использования для него были бы довольно редкими (например, a StopIteration всегда поднимается в нормальном конец каждого цикла for - и поймал тоже: почему на Земле кто-то хотел бы проследить это, и многие другие рутинные использования исключенных ловушек в внутренних компонентах Python и стандартной библиотеке?!).

Ответ 4

Загрузите pypy и примените его.