Добавление функции в sys.excepthook - PullRequest
4 голосов
/ 07 декабря 2011

Скажем, у меня есть что-то вроде этого, которое отправляет необработанные исключения на logging.critical():

import sys

def register_handler():
    orig_excepthook = sys.excepthook

    def error_catcher(*exc_info):
        import logging
        log = logging.getLogger(__name__)
        log.critical("Unhandled exception", exc_info=exc_info)
        orig_excepthook(*exc_info)

    sys.excepthook = error_catcher

Работает:

import logging
logging.basicConfig()

register_handler()

undefined() # logs, then runs original excepthook

Однако, если register_handler() вызывается несколько раз, в цепочке вызывается несколько error_catcher, и сообщение журнала появляется несколько раз ..

Я могу придумать несколько способов, но ни один из них не особенно хорош (например, проверка, является ли sys.excepthook функцией error_catcher, или использование атрибута have_registered) в модуле, чтобы избежать двойной регистрации)

Есть ли рекомендуемый способ сделать это?

Ответы [ 4 ]

3 голосов
/ 07 декабря 2011

Вы можете просто проверить, является ли sys.excepthook встроенной функцией до регистрации вашего обработчика:

>>> import sys, types
>>> isinstance(sys.excepthook, types.BuiltinFunctionType)
True
>>> sys.excepthook = lambda x: x
>>> isinstance(sys.excepthook, types.BuiltinFunctionType)
False
2 голосов
/ 07 декабря 2011

Если вы поместите код вашего вопроса в модуль, вы можете импортировать его много раз, но он будет выполнен только в первый раз.

1 голос
/ 04 апреля 2012

Наличие переменной уровня «уже зарегистрированный хук» кажется самым простым и надежным способом сделать это.

Другие возможные решения могут быть отклонены при определенных (довольно неясных) обстоятельствах -проверка, является ли sys.excepthook встроенной функцией, завершится неудачей, если приложение зарегистрирует пользовательский excepthook, при сохранении оригинала excepthook во время определения функции будет впоследствии зафиксирован за исключением функций hohook.

import sys

_hook_registered = False

def register_handler(force = False):
    global _hook_registered

    if _hook_registered and not force:
        return

    orig_excepthook = sys.excepthook

    def error_catcher(*exc_info):
        import logging
        log = logging.getLogger(__name__)
        log.critical("Unhandled exception", exc_info=exc_info)
        orig_excepthook(*exc_info)

    sys.excepthook = error_catcher

    _hook_registered = True
1 голос
/ 07 декабря 2011

Если вы установите orig_excepthook в качестве аргумента со значением по умолчанию, значение по умолчанию будет зафиксировано один раз во время определения.Поэтому повторные вызовы на register_handler не изменятся orig_excepthook.

import sys

def register_handler(orig_excepthook=sys.excepthook):
    def error_catcher(*exc_info):
        import logging
        log = logging.getLogger(__name__)
        log.critical("Unhandled exception", exc_info=exc_info)
        orig_excepthook(*exc_info)
    sys.excepthook = error_catcher

import logging
logging.basicConfig()

register_handler()
register_handler()
register_handler()

undefined() 

производит только один вызов на log.critical.

...