Python3. Как правильно вызвать исключение внутри контекстного менеджера, чтобы справиться с ним с помощью оператора? - PullRequest
2 голосов
/ 28 октября 2019

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

Итак, мой менеджер контекста:

class Test(object):
    def div(self, a, b):
        return a // b
    def __enter__(self):
        print('enter')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        return self 

И класс, в котором я хочу его использовать:

class Container(object):
    def test_exc(self, a, b):
        try:
            with Test() as test:
                try:
                    result = test.div(a, b)
                    print(a, '/', b, '=', result)
                    return result
                except Exception as e:
                    raise e
        except Exception as e:
            print(e)

Использование:

c = Container()
c.test_exc(5, 1)
c.test_exc(5, 0)

Вывод:

enter
5 / 1 = 5
exit
enter
exit

Таким образом, когда возбуждается исключение (в приведенном выше примере это ZeroDivisionError), оно не перехватывается родительским try ... catch. Как это исправить?

Ответы [ 2 ]

2 голосов
/ 28 октября 2019

Ошибка - реализация вашей функции __exit__().

В документации говорится:

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

просто удалите

return self

из функции __exit__(). Отсутствие возврата вернет None, что оценивается как False

1 голос
/ 28 октября 2019

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

Чтобы остановить исключения, метод __exit__ должен вернуть Trueself может считаться True)

Чтобы передать исключения, верните False:

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exit')
        return False

См. Эту ссылку в PEP, где некоторый псевдокод показывает, как исключение можно проглотить.

...