Сообщить об ошибке.

Очистка ресурсов при неудачном завершении with

Как отмечено в документации к ExitStack.push(), этот метод может быть полезен для очистки уже выделенного ресурса, если последующие шаги в реализации метода __enter__() завершаются неудачно.

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

from contextlib import contextmanager, AbstractContextManager, ExitStack

class ResourceManager(AbstractContextManager):

    def __init__(self, acquire_resource, release_resource, check_resource_ok=None):
        self.acquire_resource = acquire_resource
        self.release_resource = release_resource
        if check_resource_ok is None:
            def check_resource_ok(resource):
                return True
        self.check_resource_ok = check_resource_ok

    @contextmanager
    def _cleanup_on_error(self):
        with ExitStack() as stack:
            stack.push(self)
            yield
            # Проверка пройдена и не вызвала исключение. 
            # Соответственно, нужно сохранить ресурс и передать 
            # его вызывающей стороне.
            stack.pop_all()

    def __enter__(self):
        resource = self.acquire_resource()
        with self._cleanup_on_error():
            if not self.check_resource_ok(resource):
                msg = "Failed validation for {!r}"
                raise RuntimeError(msg.format(resource))
        return resource

    def __exit__(self, *exc_details):
        # Не нужно дублировать логику освобождения ресурса.
        self.release_resource()