On Sun, Apr 22, 2018 at 12:04 PM, Mike Miller <python-...@mgmiller.net> wrote: > Round 2 (Changed order, see below): > > 1. with open(fn) as f: # current behavior > 2. with (open(fn) as f): # same > > 3. with closing(urlopen(url)) as dl: # current behavior > 5. with (closing(urlopen(url)) as dl): # same > > 4. with closing(urlopen(url) as dl): # urlopener named early > > > On 2018-04-20 17:15, Chris Angelico wrote: >> >> The second and fifth could be special cased as either the same as >> first and third, or as SyntaxErrors. (But which?) > > > If they are expressions, they should be the same once evaluated, no? > > (I had a brief episode where I wrote that "as" was required with "with", > instead of the CM object, sorry. :) > >> The fourth one is very tricky. If 'expr as name' is allowed inside >> arbitrary >> expressions, why shouldn't it be allowed there? > > > Yes, they should be allowed there. > >> The disconnect between viable syntax and useful statements is problematic >> here. > > > Number 4 appears to name the urlopener early. Since closing() returns it as > well, might it work anyway? > > Might be missing something else, but #4 looks like a mistake with the layout > of the parentheses, which can happen anywhere. I don't get the sense it > will happen often.
It's actually semantically identical to option 3, but *not* semantically identical to option 5, unless there is a magical special case that says that a 'with' statement is permitted to have parentheses for no reason. The 'closing' context manager returns the *inner* CM, not the closing CM itself. If we rewrite these into approximate equivalents without the 'with' statement, what we have is this: > 1. with open(fn) as f: # current behavior file = open(fn) f = file.__enter__() assert file is f # passes for file objects > 2. with (open(fn) as f): # same f = open(fn) f.__enter__() # The return value from enter is discarded > 3. with closing(urlopen(url)) as dl: # current behavior downloader = urlopen(url) closer = closing(downloader) dl = closer.__enter__() assert dl is downloader # passes for closing objects > 5. with (closing(urlopen(url)) as dl): # same downloader = urlopen(url) dl = closing(downloader) dl.__enter__() # Return value from __enter__ is discarded > 4. with closing(urlopen(url) as dl): # urlopener named early dl = urlopen(url) closer = closing(dl) closer.__enter__() # Return value is discarded again Notice how there are five distinctly different cases here. When people say there's a single obvious way to solve the "with (expr as name):" case, they generally haven't thought about all the different possibilities. (And I haven't mentioned the possibility that __enter__ returns something that you can't easily reference from inside the expression, though it's not materially different from closing().) There are a few ways to handle it. One is to create a special case in the grammar for 'with' statement parentheses: with_stmt: 'with' with_item (',' with_item)* ':' suite with_item: (test ['as' expr]) | ('(' test ['as' expr] ')') which will mean that these two do the same thing: with spam as ham: with (spam as ham): but this won't: with ((spam as ham)): And even with that special case, the use of 'as' inside a 'with' statement is subtly different from its behaviour anywhere else, so it would be confusing. So a better way is to straight-up disallow 'as' expressions inside 'with' headers (meaning you get a SyntaxError if the behaviour would be different from the unparenthesized form). Still confusing ("why can't I do this?"). And another way is to just not use 'as' at all, and pick a different syntax. That's why the PEP now recommends ':='. ChrisA _______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe: https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com