Надежный бесконечный цикл для сервера, написанного на Python
Я пишу сервер, который обрабатывает события и необработанные исключения во время обработки события, не должен завершать сервер.
сервер представляет собой один непоточный процесс python.
Я хочу завершить работу над этими типами ошибок:
- KeyboardInterrupt
- MemoryError
- ...
список встроенных исключений давно: https://docs.python.org/2/library/exceptions.html
Я не хочу заново изобретать эту обработку исключений, так как я думаю, что это было сделано несколько раз раньше.
Как поступить?
- есть белый список: список исключений, которые в порядке, и обработка следующего события-правильный выбор
- есть черный список: список исключений, которые указывают, что завершение сервера является правильным выбор.
подсказка: этот вопрос не о запуске демона unix в фоновом режиме. Речь идет не о двойной вилке и не о перенаправлении stdin /stdout: -)
2 ответов
самым главным исключением является BaseException
. Есть две группы под этим:
-
Exception
производные - все остальное
такие вещи, как Stopiteration
, ValueError
, TypeError
, etc. все примеры Exception
.
такие вещи, как GeneratorExit
, SystemExit
и KeyboardInterrupt
не являются потомками Execption
.
Итак, первый шаг-поймать Exception
, а не BaseException
что позволит вам легко завершить программу. Я рекомендую также ловить GeneratorExit
как 1) он никогда не должен быть замечен, если он не поднимается вручную; 2) Вы можете войти в него и перезапустить цикл; и 3) он предназначен для сигнала генератора вышел и может быть очищен, не то, что программа должна выйти.
следующий шаг-зарегистрировать каждое исключение с достаточной детализацией, чтобы у вас была возможность выяснить, что пошло не так (когда вы позже приступите к отладке).
наконец, вы должны решить для себя что, если Exception
производные исключения, которые вы хотите завершить: я бы предложил RuntimeError
и MemoryError
, хотя вы можете обойти их, просто остановив и перезапустив цикл сервера.
так что, на самом деле, это зависит от вас.
если есть какая-то другая ошибка (например,IOError
при попытке загрузить файл конфигурации), который достаточно серьезен, чтобы выйти, тогда код, ответственный за загрузку файла конфигурации, должен быть достаточно умен, чтобы поймать это IOError
и поднять SystemExit
вместо.
что касается белого списка / черного списка - используйте черный список, так как должно быть только несколько, если таковые имеются,Exception
-основанные исключения, которые вам нужно фактически завершить работу сервера.
я бы сделал это так же, как вы думаете, используя "Вы не должны пройти" Гендальф обработчик исключений except Exception
чтобы поймать все несистемные исключения при создании черного списка set
исключений, которые должны проходить и заканчиваться, будут повторно подняты.
С помощью обработчик Гендальф убеждаемся GeneratorExit
, SystemExit
и KeyboardInterrupt
(все исключения выхода из системы) передайте и завершите программу, если в стеке вызовов нет других обработчиков. Вот где вы можете проверить с type(e)
и __class__
пойманного исключения e
фактически принадлежит к набору исключений из черного списка и re -raise
его.
в качестве небольшой демонстрации:
import exceptions # Py2.x only
# dictionary holding {exception_name: exception_class}
excptDict = vars(exceptions)
exceptionNames = ['MemoryError', 'OSError', 'SystemError'] # and others
# set containing black-listed exceptions
blackSet = {excptDict[exception] for exception in exceptionNames}
теперь blackSet = {OSError, SystemError, MemoryError}
держа классов несистемные исключения, которые мы хотим не ручки.
A try-except
блок теперь может выглядеть так:
try:
# calls that raise exceptions:
except Exception as e:
if type(e) in blackSet: raise e # re-raise
# else just handle it
An пример, который ловит все исключения, используя BaseException
может помочь проиллюстрировать то, что я имею в виду. (это сделано только для демонстрационных целей, чтобы увидеть, как это повышение в конечном итоге завершит вашу программу). обратите внимание: я не предлагаешь вы используете BaseException
; я использую его для того, чтобы показать какое исключение фактически "пройдет" и вызовет прекращение (я.е все, что BaseException
ловит):
for i, j in excptDict.iteritems():
if i.startswith('__'): continue # __doc__ and other dunders
try:
try:
raise j
except Exception as ex:
# print "Handler 'Exception' caught " + str(i)
if type(ex) in blackSet:
raise ex
except BaseException:
print "Handler 'BaseException' caught " + str(i)
# prints exceptions that would cause the system to exit
Handler 'BaseException' caught GeneratorExit
Handler 'BaseException' caught OSError
Handler 'BaseException' caught SystemExit
Handler 'BaseException' caught SystemError
Handler 'BaseException' caught KeyboardInterrupt
Handler 'BaseException' caught MemoryError
Handler 'BaseException' caught BaseException
наконец, чтобы сделать этот Python 2/3 агностиком, вы можете try
и import exceptions
и если это не удается (что он делает в Python 3), откат к импорту builtins
, который содержит все Exceptions
; мы искать словарь, имя значит нет разница:
try:
import exceptions
excDict = vars(exceptions)
except ImportError:
import builtins
excDict = vars(builtins)
я не знаю, есть ли более умный способ сделать это, другое решение может быть вместо try-except
со знаком except
, имея 2 обработчика, один для исключений из черного списка, а другой для общего случая:
try:
# calls that raise exceptions:
except tuple(blackSet) as be: # Must go first, of course.
raise be
except Exception as e:
# handle the rest