[Python-Dev] Iterator version of contextlib.nested

Nick Coghlan ncoghlan at gmail.com
Sat Jun 13 03:01:49 CEST 2009


Hagen Fürstenau wrote:
> I'm proposing to add an iterator version of "nested" to contextlib
> (possibly called "inested"), which takes an iterable of context managers
> instead of a variable number of parameters. The implementation could be
> taken over from the present "nested", only changing "def
> nested(*managers)" to "def inested(managers)".
> 
> This has the advantage that an iterator can be passed to "inested", so
> that each context managers is created in the context of all previous
> ones, which was one of the reasons for introducing the multi-with
> statement in the first place. "contextlib.inested" would therefore be
> the generalization of the multi-with statement to a variable number of
> managers (and "contextlib.nested" would stay deprecated).

The semantic change actually needed to make nested() more equivalent to
the multi-with statement is for it to accept zero-argument callables
that create context managers as arguments rather than pre-created
context managers.

Rather than changing the name of the function, this could be done by
inspecting the first argument for an "__enter__" method. If it has one,
use the old semantics (and issue a DeprecationWarning as in 3.1).
Otherwise, use the proposed new semantics.

However, the semantic equivalence still won't be complete as nested()
currently has no way of matching the multi-with behaviour of allowing
outer context managers to suppress exceptions raised by the constructors
or __enter__ methods of inner context managers. Attempting to do so will
result in a RuntimeError as the contextlib.contextmanager wrapper
complains that the generator implementing nested() didn't yield.

The further enhancement needed to address that would be to tweak
nested() to raise a custom exception in that case (e.g. ContextSkipped)
and provide an "allowskip" context manager that just catches and
suppresses that specific exception:

i.e.
  @contextmanager
  def allowskip():
    try:
      yield
    except ContextSkipped:
      pass

  with allowskip(), nested(*cm_factories):
    # Do something

I suggest putting an RFE on the tracker for this.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------


More information about the Python-Dev mailing list