views:

96

answers:

2

Suppose you have three objects you acquire via context manager, for instance A lock, a db connection and an ip socket. You can acquire them by:

with lock:
   with db_con:
       with socket:
            #do stuff

But is there a way to do it in one block? something like

with lock,db_con,socket:
   #do stuff

Furthermore, is it possible, given an array of unknown length of objects that have context managers, is it possible to somehow do:

a=[lock1, lock2, lock3, db_con1, socket, db_con2]
with a as res:
    #now all objects in array are acquired

If the answer is "no", is it because the need for such a feature implies bad design, or maybe I should suggest it in a pep? :-P

+7  A: 

The first part of your question is possible in Python 3.1.

With more than one item, the context managers are processed as if multiple with statements were nested:

with A() as a, B() as b:
    suite

is equivalent to

with A() as a:
    with B() as b:
        suite

Changed in version 3.1: Support for multiple context expressions

Mark Byers
thanks! but that still didn't answer my whole question:what about the 2nd case I mentioned, where the context managers are given in an array, without knowing how many mangers are there in the array. will it be possible in some python3.X to do`with [cm1,cm2,cm3,cm4,cm5] as result: ....`
noam
@noam: To solve the second part of your question you could write a class to wrap a number of resources and implement `__enter__` and `__exit__` for that class. I'm not sure if there's a standard library class that does this already.
Mark Byers
+8  A: 

You can use contextlib.nested:

from contextlib import nested

with nested(A(), B(), C()) as (X, Y, Z):
    do_something()

is equivalent to:

m1, m2, m3 = A(), B(), C()
with m1 as X:
    with m2 as Y:
        with m3 as Z:
            do_something()

Note that this isn't exactly the same as normally using nested with, because A(), B(), and C() will all be called initially, before entering the context managers. But usually this makes no difference.

In Python 2.7 and 3.1, syntax has been added for this, and contextlib.nested has been deprecated:

with A() as X, B() as Y, C() as Z:
    do_something()
interjay
Awesome and useful response, thanks! :)
jathanism
thanks! So I can use this for an array of context managers with `contextlib.nested(*arr)`.<br>Is this possible somehow in python 2.7 and 3.1's new syntax?
noam
@noam: No, in fact the docstring for `nested` in 3.1 says: "The one advantage of this function over the multiple manager form of the with statement is that argument unpacking allows it to be used with a variable number of context managers as follows: `with nested(*managers): do_something()`"
interjay
@interjay - Odd, on one hand it's deprecated, but on the other they acknowledge an advantage of the deprecated module over the replacement?
noam