contextlib.nested()

brasse thebrasse at gmail.com
Thu Nov 6 08:57:51 EST 2008


On Nov 6, 11:43 am, Robert Lehmann <stargam... at gmail.com> wrote:
> On Thu, 06 Nov 2008 01:02:34 -0800, brasse wrote:
> > Hello!
>
> > I have been running in to some problems when using contextlib.nested().
> > My problem arises when using code similar to this:
>
> > from __future__ import with_statement
>
> > from contextlib import nested
>
> > class Foo(object):
>
> >     def __init__(self, tag, fail=False):
> >         print 'ctor', tag
> >         self.tag = tag
> >         if fail:
> >             raise Exception()
>
> >     def __enter__(self):
> >         print '__enter__', self.tag
> >         return self
>
> >     def __exit__(self, *args):
> >         print '__exit__', self.tag
>
> > with nested(Foo('a'), Foo('b', True)) as (a, b):
> >     print a.tag
> >     print b.tag
>
> > Here the construction of b fails which in turn means that the
> > contextmanager fails to be created leaving me a constructed object (a)
> > that needs to be deconstructed in some way. I realize that nested() is
> > in a tight spot here to do anything about it since it doesn't exist.
> > This behavior makes it hard for me to use the with statement (using
> > nested()) the way I want.
>
> > Has anyone else been running in to this? Any tips on how to handle
> > multiple resources?
>
> Your problem does not seem to be connected to context managers. The error
> occurs before calling `contextlib.nested` at all::
>
>    >>> foo = [Foo('a')]
>    ctor a
>    >>> with nested(*foo) as a: print a
>    ...
>    __enter__ a
>    [<__main__.Foo object at 0x7fbc29408b90>]
>    __exit__ a
>    >>> foo = [Foo('a'), Foo('b', True)]
>    ctor a
>    ctor b
>    Traceback (most recent call last):
>      File "<stdin>", line 1, in <module>
>      File "<stdin>", line 7, in __init__
>        raise Exception()
>    Exception
>
> If you need to deconstruct object `a` from your example, your staging is
> probably broken. Allocate the resource in `__init__` but only go live
> just in `__enter__`. If you do not enter the context, then, you won't
> need to deconstruct it as well.
>
> HTH,
>
> --
> Robert "Stargaming" Lehmann

Diez, Robert,

OK. The practice of "going live" or doing non-trivial initialization
in __enter__ is new to me. I'm new to Python with a C++ background, so
that shouldn't be a surprise. :-)

Ideally I would like to put all initialization in __init__ since then
I would be able to use my object right after constructing it, without
having to use it in a with statement. The reason I'm struggling with
this is probably my C++ background. I'm rally accustomed to design
with RAII in mind. Acquiring all resources in the ctor and releasing
all resources in the dtor is *really* handy.

If you had a class that wanted to acquire some external resources that
must be released at some point, how would you rewrite the code from my
example?

:.:: mattias



More information about the Python-list mailing list