PEP 343 - multiple context managers in one statement
I have a deep suspicion that this has been done to death already, but my searching ability isn't up to finding the reference. So I'll simply ask the question, and not offer a long discussion: Has the option of letting the with statement admit multiple context managers been considered (and presumably rejected)? I'm thinking of with expr1, expr2, expr3: # whatever In some ways, this doesn't even need an extension to the PEP - giving tuples suitable __enter__ and __exit__ methods would do it. Or, I suppose a user-defined manager which combined a list of others: class combining: def __init__(*mgrs): self.mgrs = mgrs def __with__(self): return self def __enter__(self): return tuple(mgr.__enter__() for mgr in self.mgrs) def __exit__(self, type, value, tb): # first in, last out for mgr in reversed(self.mgrs): mgr.__exit__(type, value, tb) Would that be worth using as an example in the PEP? Sorry - it got a bit long anyway... Paul. PS The signature of __with__ in example 4 in the PEP is wrong - it has an incorrect "lock" parameter.
Paul Moore wrote:
I have a deep suspicion that this has been done to death already, but my searching ability isn't up to finding the reference. So I'll simply ask the question, and not offer a long discussion:
Has the option of letting the with statement admit multiple context managers been considered (and presumably rejected)?
I'm thinking of
with expr1, expr2, expr3: # whatever
Not rejected - deliberately left as a future option (this is the reason why the RHS of an as clause has to be parenthesised if you want tuple unpacking).
In some ways, this doesn't even need an extension to the PEP - giving tuples suitable __enter__ and __exit__ methods would do it. Or, I suppose a user-defined manager which combined a list of others:
class combining: def __init__(*mgrs): self.mgrs = mgrs def __with__(self): return self def __enter__(self): return tuple(mgr.__enter__() for mgr in self.mgrs) def __exit__(self, type, value, tb): # first in, last out for mgr in reversed(self.mgrs): mgr.__exit__(type, value, tb)
Would that be worth using as an example in the PEP?
The issue with that implementation is that the semantics are wrong - it doesn't actually mirror *nested* with statements. If one of the later __enter__ methods, or one of the first-executed __exit__ methods throws an exception, there are a lot of __exit__ methods that get skipped. Getting it right is more complicated (and this probably still has mistakes): class nested(object): def __init__(*mgrs): self.mgrs = mgrs self.entered = None def __context__(self): return self def __enter__(self): self.entered = deque() vars = [] try: for mgr in self.mgrs: var = mgr.__enter__() self.entered.push_front(mgr) vars.append(var) except: self.__exit__(*sys.exc_info()) raise return vars def __exit__(self, *exc_info): # first in, last out # Behave like nested with statements ex = exc_info for mgr in self.entered: try: mgr.__exit__(*ex) except: ex = sys.exc_info() if ex is not exc_info: raise ex[0], ex[1], ex[2]
PS The signature of __with__ in example 4 in the PEP is wrong - it has an incorrect "lock" parameter.
Thanks - I'll fix that when I incorporate the resolutions of the open issues (which will be post the SVN migration). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://boredomandlaziness.blogspot.com
On 10/25/05, Nick Coghlan <ncoghlan@gmail.com> wrote:
Has the option of letting the with statement admit multiple context managers been considered (and presumably rejected)? [...] Not rejected - deliberately left as a future option (this is the reason why
Paul Moore wrote: [...] the RHS of an as clause has to be parenthesised if you want tuple unpacking).
Thanks. I now see that note in the PEP - apologies for missing it in the first instance. [...]
The issue with that implementation is that the semantics are wrong - it doesn't actually mirror *nested* with statements. If one of the later __enter__ methods, or one of the first-executed __exit__ methods throws an exception, there are a lot of __exit__ methods that get skipped.
Getting it right is more complicated (and this probably still has mistakes):
Bah. You're right, of course (about it being more complicated - I can't see any mistakes :-)) I'd argue that precisely because a naive approach gets it wrong, having your version as an example in the PEP (and possibly the documentation, much like the itertools module has a recipes section) is that much more useful. Anyway, thanks for the help. Paul.
participants (2)
-
Nick Coghlan -
Paul Moore