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
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)* [',']
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
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
On Sun, Mar 1, 2009 at 2:46 PM, Guido van Rossum
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.
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
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: ...
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.
The proposed way follows the example of the import statement, too::
from module import a, b as x, d as y
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
On Sun, Mar 1, 2009 at 12:57 PM, Christian Heimes
wrote: 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: ...
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.
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
On Sun, Mar 1, 2009 at 1:35 PM, Chris Rebert
wrote: On Sun, Mar 1, 2009 at 12:57 PM, Christian Heimes
wrote: 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: ...
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.
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/)
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:
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:
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
Sturla Molden wrote:
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:
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).
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
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?
No, the parens will be syntactically *illegal*. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
On Sun, Mar 1, 2009 at 11:46 AM, Guido van Rossum
On Sun, Mar 1, 2009 at 5:49 AM, Christian Heimes
wrote: 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())
Alternatively if closer conformity with for loop syntax is desirable consider this: with lock, open(infile), open(outfile) as lock, fin, fout: fout.fwrite(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)* [',']
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/) _______________________________________________ Python-ideas mailing list Python-ideas@python.org http://mail.python.org/mailman/listinfo/python-ideas
On Sun, Mar 1, 2009 at 4:01 PM, Gregory P. Smith
Alternatively if closer conformity with for loop syntax is desirable consider this: with lock, open(infile), open(outfile) as lock, fin, fout: fout.fwrite(fin.read())
+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
Calvin Spealman wrote:
On Sun, Mar 1, 2009 at 4:01 PM, Gregory P. Smith
wrote: Alternatively if closer conformity with for loop syntax is desirable consider this: with lock, open(infile), open(outfile) as lock, fin, fout: fout.fwrite(fin.read())
+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.
I prefer this also, for the same reasons. tjr
On Sun, Mar 1, 2009 at 1:15 PM, Calvin Spealman
On Sun, Mar 1, 2009 at 4:01 PM, Gregory P. Smith
wrote: Alternatively if closer conformity with for loop syntax is desirable consider this: with lock, open(infile), open(outfile) as lock, fin, fout: fout.fwrite(fin.read())
+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.
-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/)
Guido van Rossum schrieb:
On Sun, Mar 1, 2009 at 1:15 PM, Calvin Spealman
wrote: On Sun, Mar 1, 2009 at 4:01 PM, Gregory P. Smith
wrote: Alternatively if closer conformity with for loop syntax is desirable consider this: with lock, open(infile), open(outfile) as lock, fin, fout: fout.fwrite(fin.read())
+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.
-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.
Also, the "a as b, c as d" syntax better conveys the fact that the managers are called sequentially, and not somehow "in parallel". Georg
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