Commit 27228270 authored by Nick Coghlan's avatar Nick Coghlan

Additional ExitStack examples, and a few other cleanups for the ExitStack docs

parent 48c2e4d0
...@@ -188,15 +188,15 @@ Functions and classes provided: ...@@ -188,15 +188,15 @@ Functions and classes provided:
Each instance maintains a stack of registered callbacks that are called in Each instance maintains a stack of registered callbacks that are called in
reverse order when the instance is closed (either explicitly or implicitly reverse order when the instance is closed (either explicitly or implicitly
at the end of a ``with`` statement). Note that callbacks are *not* invoked at the end of a :keyword:`with` statement). Note that callbacks are *not*
implicitly when the context stack instance is garbage collected. invoked implicitly when the context stack instance is garbage collected.
This stack model is used so that context managers that acquire their This stack model is used so that context managers that acquire their
resources in their ``__init__`` method (such as file objects) can be resources in their ``__init__`` method (such as file objects) can be
handled correctly. handled correctly.
Since registered callbacks are invoked in the reverse order of Since registered callbacks are invoked in the reverse order of
registration, this ends up behaving as if multiple nested ``with`` registration, this ends up behaving as if multiple nested :keyword:`with`
statements had been used with the registered set of callbacks. This even statements had been used with the registered set of callbacks. This even
extends to exception handling - if an inner callback suppresses or replaces extends to exception handling - if an inner callback suppresses or replaces
an exception, then outer callbacks will be passed arguments based on that an exception, then outer callbacks will be passed arguments based on that
...@@ -216,7 +216,7 @@ Functions and classes provided: ...@@ -216,7 +216,7 @@ Functions and classes provided:
manager's own :meth:`__enter__` method. manager's own :meth:`__enter__` method.
These context managers may suppress exceptions just as they normally These context managers may suppress exceptions just as they normally
would if used directly as part of a ``with`` statement. would if used directly as part of a :keyword:`with` statement.
.. method:: push(exit) .. method:: push(exit)
...@@ -234,7 +234,7 @@ Functions and classes provided: ...@@ -234,7 +234,7 @@ Functions and classes provided:
same way context manager :meth:`__exit__` methods can. same way context manager :meth:`__exit__` methods can.
The passed in object is returned from the function, allowing this The passed in object is returned from the function, allowing this
method to be used is a function decorator. method to be used as a function decorator.
.. method:: callback(callback, *args, **kwds) .. method:: callback(callback, *args, **kwds)
...@@ -245,14 +245,14 @@ Functions and classes provided: ...@@ -245,14 +245,14 @@ Functions and classes provided:
exceptions (as they are never passed the exception details). exceptions (as they are never passed the exception details).
The passed in callback is returned from the function, allowing this The passed in callback is returned from the function, allowing this
method to be used is a function decorator. method to be used as a function decorator.
.. method:: pop_all() .. method:: pop_all()
Transfers the callback stack to a fresh :class:`ExitStack` instance Transfers the callback stack to a fresh :class:`ExitStack` instance
and returns it. No callbacks are invoked by this operation - instead, and returns it. No callbacks are invoked by this operation - instead,
they will now be invoked when the new stack is closed (either they will now be invoked when the new stack is closed (either
explicitly or implicitly). explicitly or implicitly at the end of a :keyword:`with` statement).
For example, a group of files can be opened as an "all or nothing" For example, a group of files can be opened as an "all or nothing"
operation as follows:: operation as follows::
...@@ -280,6 +280,74 @@ This section describes some examples and recipes for making effective use of ...@@ -280,6 +280,74 @@ This section describes some examples and recipes for making effective use of
the tools provided by :mod:`contextlib`. the tools provided by :mod:`contextlib`.
Supporting a variable number of context managers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The primary use case for :class:`ExitStack` is the one given in the class
documentation: supporting a variable number of context managers and other
cleanup operations in a single :keyword:`with` statement. The variability
may come from the number of context managers needed being driven by user
input (such as opening a user specified collection of files), or from
some of the context managers being optional::
with ExitStack() as stack:
for resource in resources:
stack.enter_context(resource)
if need_special resource:
special = acquire_special_resource()
stack.callback(release_special_resource, special)
# Perform operations that use the acquired resources
As shown, :class:`ExitStack` also makes it quite easy to use :keyword:`with`
statements to manage arbitrary resources that don't natively support the
context management protocol.
Simplifying support for single optional context managers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the specific case of a single optional context manager, :class:`ExitStack`
instances can be used as a "do nothing" context manager, allowing a context
managers to easily be omitted without affecting the overall structure of
the source code::
def debug_trace(details):
if __debug__:
return TraceContext(details)
# Don't do anything special with the context in release mode
return ExitStack()
with debug_trace():
# Suite is traced in debug mode, but runs normally otherwise
Catching exceptions from ``__enter__`` methods
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
It is occasionally desirable to catch exceptions from an ``__enter__``
method implementation, *without* inadvertently catching exceptions from
the :keyword:`with` statement body or the context manager's ``__exit__``
method. By using :class:`ExitStack` the steps in the context management
protocol can be separated slightly in order to allow this::
stack = ExitStack()
try:
x = stack.enter_context(cm)
except Exception:
# handle __enter__ exception
else:
with stack:
# Handle normal case
Actually needing to do this is likely to indicate that the underlying API
should be providing a direct resource management interface for use with
:keyword:`try`/:keyword:`except`/:keyword:`finally` statements, but not
all APIs are well designed in that regard. When a context manager is the
only resource management API provided, then :class:`ExitStack` can make it
easier to handle various situations that can't be handled directly in a
:keyword:`with` statement.
Cleaning up in an ``__enter__`` implementation Cleaning up in an ``__enter__`` implementation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment