python как безопасно обрабатывать исключение внутри контекстного менеджера

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

Итак, у меня есть псевдо-код здесь, моя цель-использовать контекст блокировки, который при __enter__ регистрирует начало datetime и возвращает идентификатор блокировки, а при __exit__ записывает конец datetime и освобождает блокировку:

def main():
    raise Exception

with cron.lock() as lockid:
    print('Got lock: %i' % lockid)
    main()

как я могу по-прежнему вызывать ошибки в дополнение к существующему контексту безопасно?

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

Примечание: альтернативные / стандартные методы предотвращения параллелизма не имеют значения, я хочу применить эти знания к любому общему контекстному управлению. я не знаю, имеют ли разные контексты разные причуды.

PS. Это finally блок актуален?

1 ответов


The __exit__ метод называется как обычно если диспетчер контекста нарушен исключением. Фактически, параметры передаются в __exit__ все имеют отношение к обработке этого дела! От документы:

object.__exit__(self, exc_type, exc_value, traceback)

закройте контекст выполнения, связанный с этим объектом. Параметры описывают исключение, вызвавшее выход из контекста. Если контекст был завершен без исключения, все три аргумента будут Никто.

если предоставлено исключение, и метод хочет подавить исключение (т. е. предотвратить его распространение), он должен вернуть значение true. В противном случае исключение будет обработано нормально при выходе из этого метода.

отметим, что __exit__() методы не должны перезапускать переданное исключение; это ответственность вызывающего абонента.

так что вы можете видеть, что __exit__ метод будет выполнен, а затем, по умолчанию, любой исключение будет повторно поднято после выход из контекстного менеджера. Вы можете проверить это самостоятельно, создав простой менеджер контекста и разбив его за исключением:

DummyContextManager(object):
    def __enter__(self):
        print('Entering...')
    def __exit__(self, exc_type, exc_value, traceback):
        print('Exiting...')  
        # If we returned True here, any exception would be suppressed!

with DummyContextManager() as foo:
    raise Exception()

когда вы запускаете этот код, вы должны видеть все, что хотите (может быть не в порядке, так как print имеет тенденцию заканчиваться в середине tracebacks):

Entering...
Exiting...
Traceback (most recent call last):
  File "C:\foo.py", line 8, in <module>
    raise Exception()
Exception