with statement: multiple context manager

Hello fellow Pythonistas! On a regularly basis I'm bothered and annoyed by the fact that the with statement takes only one context manager. Often I need to open two files to read from one and write to the other. I propose to modify the with statement to accept multiple context manangers. Example ======= The nested block:: with lock: with open(infile) as fin: with open(outfile, 'w') as fout: fout.write(fin.read()) could be written as:: with lock, open(infile) as fin, open(outfile, 'w') as fout: fout.write(fin.read()) The context managers' __enter__() and __exit__() methods are called FILO (first in, last out). When an exception is raised by the __enter__() method, the right handed context managers are omitted. Grammar ======= I'm not sure if I got the grammar right but I *think* the new grammar should look like:: with_stmt: 'with' with_vars ':' suite with_var: test ['as' expr] with_vars: with_var (',' with_var)* [','] Christian

Why not use this? from contextlib import nested with nested(lock, open(infile), open(outfile, 'w')) as (_, fin, fout): fout.write(fin.read()) Ok, the _ is ugly, but is it ugly enough so we need this extension to the with statement? -panzi

On Sun, Mar 1, 2009 at 5:49 AM, Christian Heimes <lists@cheimes.de> wrote:
I am sympathetic to this desire -- I think we almost added this to the original PEP but decided to hold off until a clear need was found. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Sun, Mar 1, 2009 at 2:46 PM, Guido van Rossum <guido@python.org> wrote:
I am sympathetic to this desire -- I think we almost added this to the original PEP but decided to hold off until a clear need was found.
I second the motion to have this syntax added to the language. I've often had to write nested with blocks to open one file for reading and another for writing. - Eli

On Sun, Mar 1, 2009 at 11:53 AM, Eli Courtwright <eli@courtwright.org> wrote:
It does seem slightly incongruous though, given that the for-statement, which is quite similar to the with-statement in that they both bind new variables in a subsidiary block of code, does not directly support multiple simultaneous bindings. To put it more concretely, currently one must write: for a, b, c in zip(seq1, seq2, seq3): #body Rather than: for a in seq1, b in seq2, c in seq3: #body But for some reason we're proposing to, in a way, make nested() built into `with` but not make zip() likewise built into `for`. While I still mostly like the idea, it does seem to undermine Python's uniformity a bit. Cheers, Chris -- Follow the path of the Iguana... http://rebertia.com

Chris Rebert wrote:
While I still mostly like the idea, it does seem to undermine Python's uniformity a bit.
I played with both possible versions before I wrote the proposal. Both ways have their pros and cons. I'm preferring the proposed way:: with a, b as x, d as y: ... over the other possibility:: with a, b, c as _, x, y: ... for two reasons. For one I dislike the temporary variable that is required for some cases, e.g. the case I used in my initial proposal. It doesn't feel quite right to use a useless placeholder. The proposed way follows the example of the import statement, too:: from module import a, b as x, d as y Christian

On Sun, Mar 1, 2009 at 12:57 PM, Christian Heimes <lists@cheimes.de> wrote:
You misunderstand me. My quibble isn't over the exact syntax (in fact, I completely agree about the superiority of the proposed ordering), but rather that we're introducing syntax to do something that can already be done with a function (nested()) and is /extremely/ similar to another case (parallel for-loop) where we are opting to still require the use of a function (zip()). This proposed asymmetry concerns me.
This parallel does quell my concern somewhat. Cheers, Chris -- Follow the path of the Iguana... http://rebertia.com

On Sun, Mar 1, 2009 at 1:35 PM, Chris Rebert <pyideas@rebertia.com> wrote:
Hm. While we can indeed write the equivalent of the proposed "with a, b:" today as "with nested(a, b):", I don't think that the situation is quite comparable to a for-loop over a zip() call. The nested() context manager isn't particularly intuitive to me (and Nick just found a problem in a corner case of its semantics). Compared to nested(), I find "with a, b:" very obvious as a shorthand for nested with-statements: with a: with b: ... -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Sun, Mar 1, 2009 at 2:14 PM, Guido van Rossum <guido@python.org> wrote:
Ok, fine by me. Just wanted to ensure the point was brought up and adaquately responded to. On another note, someone ought to draft a revision to PEP 343 to document this proposal. Cheers, Chris -- Follow the path of the Iguana... http://rebertia.com

On 3/1/2009 9:57 PM, Christian Heimes wrote:
with a, b as x, d as y:
I'd like to add that parentheses improve readability here: with a, (b as x), (d as y): I am worried the proposed syntax could be a source of confusion and errors. E.g. when looking at with a,b as c,d: my eyes read with nested(a,b) as c,d: when Python would read with a,(b as c),d: It may actually be better to keep the current implementation with contextlib.nested. If contextlib.nested is not well known (I only learned of its existence recently), maybe it should be better documented? Tutorial examples of the with statement should cover contextlib.nested as well. Sturla Molden

Sturla Molden wrote:
Good point. Maybe that would be better: with a,b as c,d: reads as: with nested(a,b) as c,d: This means there can only be one "as" in a with statement with the further implication that even unneeded values have to be assigned: with a,b,c as x,unused,y: Not as nice, but much more unambiguous. Unambiguity is what we need, I think. You can always assing to _, wich is very commonly used for unneeded values (well, or for the l10n hook - so using that name would not be very unambiguous). -panzi

On Thu, Mar 26, 2009 at 8:07 AM, Mathias Panzenböck <grosser.meister.morti@gmx.net> wrote:
No, we should maintain the parallel with" import a, b as c, d". -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On 3/26/2009 4:07 PM, Mathias Panzenböck wrote:
Good point. Maybe that would be better:
with a,b as c,d:
No. See Guido's reply. I was just trying to say that with nested(a,b) as c,d: is more readble than with a as c, b as d: which would argue against new syntax and better documentation of contextlib.nested. However, as the tuple (a,b) is built prior to the call to nested, new syntax is needed. It still does not hurt to put in parentheses for readability here: with (a as c), (b as d): Perhaps parentheses should be recommended in the documentation, even though they are syntactically superfluous here? Sturla Molden

On Thu, Mar 26, 2009 at 8:42 AM, Sturla Molden <sturla@molden.no> wrote:
No, the parens will be syntactically *illegal*. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Sun, Mar 1, 2009 at 4:01 PM, Gregory P. Smith <greg@krypto.org> wrote:
+1 We don't have multi-assignment statements in favor of the unpacking concept, and I think it carries over here. Also, as mentioned, this goes along with the lack of any multi-for statement. The `x as y` part of the with statement is basically an assignment with extras, and the original suggestion then combines multiple assignments on one line. This option, I think, is more concise and readable. -- Read my blog! I depend on your acceptance of my opinion! I am interesting! http://techblog.ironfroggy.com/ Follow me if you're into that sort of thing: http://www.twitter.com/ironfroggy

On Sun, Mar 1, 2009 at 1:15 PM, Calvin Spealman <ironfroggy@gmail.com> wrote:
-1 for this variant. The syntactic model is import: import foo as bar, bletch, quuz as frobl. If we're doing this it should be like this. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

Why not use this? from contextlib import nested with nested(lock, open(infile), open(outfile, 'w')) as (_, fin, fout): fout.write(fin.read()) Ok, the _ is ugly, but is it ugly enough so we need this extension to the with statement? -panzi

On Sun, Mar 1, 2009 at 5:49 AM, Christian Heimes <lists@cheimes.de> wrote:
I am sympathetic to this desire -- I think we almost added this to the original PEP but decided to hold off until a clear need was found. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Sun, Mar 1, 2009 at 2:46 PM, Guido van Rossum <guido@python.org> wrote:
I am sympathetic to this desire -- I think we almost added this to the original PEP but decided to hold off until a clear need was found.
I second the motion to have this syntax added to the language. I've often had to write nested with blocks to open one file for reading and another for writing. - Eli

On Sun, Mar 1, 2009 at 11:53 AM, Eli Courtwright <eli@courtwright.org> wrote:
It does seem slightly incongruous though, given that the for-statement, which is quite similar to the with-statement in that they both bind new variables in a subsidiary block of code, does not directly support multiple simultaneous bindings. To put it more concretely, currently one must write: for a, b, c in zip(seq1, seq2, seq3): #body Rather than: for a in seq1, b in seq2, c in seq3: #body But for some reason we're proposing to, in a way, make nested() built into `with` but not make zip() likewise built into `for`. While I still mostly like the idea, it does seem to undermine Python's uniformity a bit. Cheers, Chris -- Follow the path of the Iguana... http://rebertia.com

Chris Rebert wrote:
While I still mostly like the idea, it does seem to undermine Python's uniformity a bit.
I played with both possible versions before I wrote the proposal. Both ways have their pros and cons. I'm preferring the proposed way:: with a, b as x, d as y: ... over the other possibility:: with a, b, c as _, x, y: ... for two reasons. For one I dislike the temporary variable that is required for some cases, e.g. the case I used in my initial proposal. It doesn't feel quite right to use a useless placeholder. The proposed way follows the example of the import statement, too:: from module import a, b as x, d as y Christian

On Sun, Mar 1, 2009 at 12:57 PM, Christian Heimes <lists@cheimes.de> wrote:
You misunderstand me. My quibble isn't over the exact syntax (in fact, I completely agree about the superiority of the proposed ordering), but rather that we're introducing syntax to do something that can already be done with a function (nested()) and is /extremely/ similar to another case (parallel for-loop) where we are opting to still require the use of a function (zip()). This proposed asymmetry concerns me.
This parallel does quell my concern somewhat. Cheers, Chris -- Follow the path of the Iguana... http://rebertia.com

On Sun, Mar 1, 2009 at 1:35 PM, Chris Rebert <pyideas@rebertia.com> wrote:
Hm. While we can indeed write the equivalent of the proposed "with a, b:" today as "with nested(a, b):", I don't think that the situation is quite comparable to a for-loop over a zip() call. The nested() context manager isn't particularly intuitive to me (and Nick just found a problem in a corner case of its semantics). Compared to nested(), I find "with a, b:" very obvious as a shorthand for nested with-statements: with a: with b: ... -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Sun, Mar 1, 2009 at 2:14 PM, Guido van Rossum <guido@python.org> wrote:
Ok, fine by me. Just wanted to ensure the point was brought up and adaquately responded to. On another note, someone ought to draft a revision to PEP 343 to document this proposal. Cheers, Chris -- Follow the path of the Iguana... http://rebertia.com

On 3/1/2009 9:57 PM, Christian Heimes wrote:
with a, b as x, d as y:
I'd like to add that parentheses improve readability here: with a, (b as x), (d as y): I am worried the proposed syntax could be a source of confusion and errors. E.g. when looking at with a,b as c,d: my eyes read with nested(a,b) as c,d: when Python would read with a,(b as c),d: It may actually be better to keep the current implementation with contextlib.nested. If contextlib.nested is not well known (I only learned of its existence recently), maybe it should be better documented? Tutorial examples of the with statement should cover contextlib.nested as well. Sturla Molden

Sturla Molden wrote:
Good point. Maybe that would be better: with a,b as c,d: reads as: with nested(a,b) as c,d: This means there can only be one "as" in a with statement with the further implication that even unneeded values have to be assigned: with a,b,c as x,unused,y: Not as nice, but much more unambiguous. Unambiguity is what we need, I think. You can always assing to _, wich is very commonly used for unneeded values (well, or for the l10n hook - so using that name would not be very unambiguous). -panzi

On Thu, Mar 26, 2009 at 8:07 AM, Mathias Panzenböck <grosser.meister.morti@gmx.net> wrote:
No, we should maintain the parallel with" import a, b as c, d". -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On 3/26/2009 4:07 PM, Mathias Panzenböck wrote:
Good point. Maybe that would be better:
with a,b as c,d:
No. See Guido's reply. I was just trying to say that with nested(a,b) as c,d: is more readble than with a as c, b as d: which would argue against new syntax and better documentation of contextlib.nested. However, as the tuple (a,b) is built prior to the call to nested, new syntax is needed. It still does not hurt to put in parentheses for readability here: with (a as c), (b as d): Perhaps parentheses should be recommended in the documentation, even though they are syntactically superfluous here? Sturla Molden

On Thu, Mar 26, 2009 at 8:42 AM, Sturla Molden <sturla@molden.no> wrote:
No, the parens will be syntactically *illegal*. -- --Guido van Rossum (home page: http://www.python.org/~guido/)

On Sun, Mar 1, 2009 at 4:01 PM, Gregory P. Smith <greg@krypto.org> wrote:
+1 We don't have multi-assignment statements in favor of the unpacking concept, and I think it carries over here. Also, as mentioned, this goes along with the lack of any multi-for statement. The `x as y` part of the with statement is basically an assignment with extras, and the original suggestion then combines multiple assignments on one line. This option, I think, is more concise and readable. -- Read my blog! I depend on your acceptance of my opinion! I am interesting! http://techblog.ironfroggy.com/ Follow me if you're into that sort of thing: http://www.twitter.com/ironfroggy

On Sun, Mar 1, 2009 at 1:15 PM, Calvin Spealman <ironfroggy@gmail.com> wrote:
-1 for this variant. The syntactic model is import: import foo as bar, bletch, quuz as frobl. If we're doing this it should be like this. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (10)
-
Calvin Spealman
-
Chris Rebert
-
Christian Heimes
-
Eli Courtwright
-
Georg Brandl
-
Gregory P. Smith
-
Guido van Rossum
-
Mathias Panzenböck
-
Sturla Molden
-
Terry Reedy