Как отмечено в документации к 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()