It seems to me that one of the intended uses of the with statement was to automate simple initialization and deinitialization, which often accompanies a try block. It wouldn't be a game changing thing by any means, but has anybody ever thought about allowing a "try with" statement on one line? So instead of: try: with context_manager(): … bunch of code … except: … exception handler … you would have: try with context_manager(): … bunch of code … except: … exception handler … I envision the two examples being equivalent, the principle benefits being readability and one less indention level for the with block code. So a similar justification to "lower < x < upper" idiom. With standard 4 space indentation, existing with statements at the top of try blocks wouldn't even be any closer to the right margin. I'm no expert in Python interpreters, but it seems like a simple one-step internal conversion whenever this proposed syntax is encountered. But obviously, it would involve that change to every interpreter in existence with no actual new functionality, so I'm sensitive to that. Anyway, just a thought. -- Alan Johnson Cofounder | Breakrs.com 347-630-2036 | alan@breakrs.com
Dnia 2013-03-02, sob o godzinie 17:45 -0500, Alan Johnson pisze:
It seems to me that one of the intended uses of the with statement was to automate simple initialization and deinitialization, which often accompanies a try block. It wouldn't be a game changing thing by any means, but has anybody ever thought about allowing a "try with" statement on one line? So instead of:
try: with context_manager(): … bunch of code … except: … exception handler …
you would have:
try with context_manager(): … bunch of code … except: … exception handler …
I envision the two examples being equivalent, the principle benefits being readability and one less indention level for the with block code. So a similar justification to "lower < x < upper" idiom. With standard 4 space indentation, existing with statements at the top of try blocks wouldn't even be any closer to the right margin.
I'm no expert in Python interpreters, but it seems like a simple one-step internal conversion whenever this proposed syntax is encountered. But obviously, it would involve that change to every interpreter in existence with no actual new functionality, so I'm sensitive to that. Anyway, just a thought.
Isn't context manager supposed to deal with exceptions by itself? If I understand things correctly with context manager you do not need try/except - context manager will deal with exceptions in __exit__. Regards. -- Tomasz Rybak GPG/PGP key ID: 2AD5 9860 Fingerprint A481 824E 7DD3 9C0E C40A 488E C654 FB33 2AD5 9860 http://member.acm.org/~tomaszrybak
Tomasz Rybak, 03.03.2013 11:32:
Dnia 2013-03-02, sob o godzinie 17:45 -0500, Alan Johnson pisze:
It seems to me that one of the intended uses of the with statement was to automate simple initialization and deinitialization, which often accompanies a try block. It wouldn't be a game changing thing by any means, but has anybody ever thought about allowing a "try with" statement on one line? So instead of:
try: with context_manager(): … bunch of code … except: … exception handler …
you would have:
try with context_manager(): … bunch of code … except: … exception handler …
I envision the two examples being equivalent, the principle benefits being readability and one less indention level for the with block code. So a similar justification to "lower < x < upper" idiom. With standard 4 space indentation, existing with statements at the top of try blocks wouldn't even be any closer to the right margin.
I'm no expert in Python interpreters, but it seems like a simple one-step internal conversion whenever this proposed syntax is encountered. But obviously, it would involve that change to every interpreter in existence with no actual new functionality, so I'm sensitive to that. Anyway, just a thought.
Isn't context manager supposed to deal with exceptions by itself? If I understand things correctly with context manager you do not need try/except - context manager will deal with exceptions in __exit__.
Yes, that's the main idea. The above example therefore strikes me as useless. If you need a try-except around a with block, then your context manager is doing something wrong. Stefan
On Mar 3, 2013, at 4:15, Stefan Behnel <stefan_ml@behnel.de> wrote:
Tomasz Rybak, 03.03.2013 11:32:
Dnia 2013-03-02, sob o godzinie 17:45 -0500, Alan Johnson pisze:
It seems to me that one of the intended uses of the with statement was to automate simple initialization and deinitialization, which often accompanies a try block. It wouldn't be a game changing thing by any means, but has anybody ever thought about allowing a "try with" statement on one line? So instead of:
try: with context_manager(): … bunch of code … except: … exception handler …
you would have:
try with context_manager(): … bunch of code … except: … exception handler …
I envision the two examples being equivalent, the principle benefits being readability and one less indention level for the with block code. So a similar justification to "lower < x < upper" idiom. With standard 4 space indentation, existing with statements at the top of try blocks wouldn't even be any closer to the right margin.
I'm no expert in Python interpreters, but it seems like a simple one-step internal conversion whenever this proposed syntax is encountered. But obviously, it would involve that change to every interpreter in existence with no actual new functionality, so I'm sensitive to that. Anyway, just a thought.
Isn't context manager supposed to deal with exceptions by itself? If I understand things correctly with context manager you do not need try/except - context manager will deal with exceptions in __exit__.
Yes, that's the main idea. The above example therefore strikes me as useless. If you need a try-except around a with block, then your context manager is doing something wrong.
A try-finally, sure, but a try-except is perfectly reasonable, idiomatic, and common. For example, if you do "with open(path) as f:" the context manager doesn't (and shouldn't) do anything to protect you from a FileNotFoundError in the open, or an IOError reading inside the block. If you want to, say, log, or try a backup file, how else would you handle that but a with inside a try? That being said, I'm not sure this is necessary. For something you do repeatedly, you can always write a wrapper function. For something you only do once, I'm not sure the extra indent is that terrible.
Andrew Barnert, 03.03.2013 14:24:
For example, if you do "with open(path) as f:" the context manager doesn't (and shouldn't) do anything to protect you from a FileNotFoundError in the open, or an IOError reading inside the block. If you want to, say, log, or try a backup file, how else would you handle that but a with inside a try?
If you really care about errors when opening the file (i.e. when creating the context manager), then the correct way to do this is to only wrap the creation of the context manager in a try-except clause, i.e. try: f = open("somefile.txt") except FileNotFoundError: do_stuff() raise # or return, or whatever with f: do_other_stuff() Otherwise, you risk accidentally catching (and potentially shadowing) exceptions that originated from the body of the with statement instead of just the context manager creation. This may look a bit overly complicated for a file, but it quickly becomes more obvious for more complex context managers, e.g. those that may raise more common errors like ValueError or AttributeError. Stefan
On Mon, Mar 4, 2013 at 5:50 AM, Stefan Behnel <stefan_ml@behnel.de> wrote:
Andrew Barnert, 03.03.2013 14:24:
For example, if you do "with open(path) as f:" the context manager doesn't (and shouldn't) do anything to protect you from a FileNotFoundError in the open, or an IOError reading inside the block. If you want to, say, log, or try a backup file, how else would you handle that but a with inside a try?
If you really care about errors when opening the file (i.e. when creating the context manager), then the correct way to do this is to only wrap the creation of the context manager in a try-except clause, i.e.
try: f = open("somefile.txt") except FileNotFoundError: do_stuff() raise # or return, or whatever
with f: do_other_stuff()
Otherwise, you risk accidentally catching (and potentially shadowing) exceptions that originated from the body of the with statement instead of just the context manager creation.
This may look a bit overly complicated for a file, but it quickly becomes more obvious for more complex context managers, e.g. those that may raise more common errors like ValueError or AttributeError.
Indeed - complex try blocks in general are a red flag when paired with exception handlers for common exceptions. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Stefan Behnel wrote:
The above example therefore strikes me as useless. If you need a try-except around a with block, then your context manager is doing something wrong.
A with statement is equivalent to try-finally, not try-except, so if you want to catch the exception you still need to put a try-except somewhere. However, I can't see why you're any more likely to want to put try-except directly around a with statement than any other kind of statement. So why ask for try-with in particular, and not try-if, try-while, try-for, ...? -- Greg
On 3 March 2013 21:36, Greg Ewing <greg.ewing@canterbury.ac.nz> wrote:
Stefan Behnel wrote:
The above example therefore strikes me as useless. If you need a try-except around a with block, then your context manager is doing something wrong.
A with statement is equivalent to try-finally, not try-except, so if you want to catch the exception you still need to put a try-except somewhere.
With statements can be used for any kind of exception handling you like, not just try/finally. For example: import contextlib @contextlib.contextmanager def ignore(errorcls): try: yield except errorcls: pass with ignore(ValueError): a = int('a') Oscar
On 3/2/2013 5:45 PM, Alan Johnson wrote:
It seems to me that one of the intended uses of the with statement was to automate simple initialization and deinitialization, which often accompanies a try block. It wouldn't be a game changing thing by any means, but has anybody ever thought about allowing a "try with" statement on one line? So instead of:
try: with context_manager(): … bunch of code … except: … exception handler …
you would have:
try with context_manager(): … bunch of code … except: … exception handler …
I envision the two examples being equivalent, the principle benefits being readability
To me it is less readable. And it only works when the with statement is the entire suite for the try: part.
and one less indention level for the with block
There is no end of possible combinations of statements and others have been proposed with the same justification - saving an indent level -- and rejected. If indent level is really a problem, use fewer spaces per indent, or pull highly indented blocks into a separate function. -- Terry Jan Reedy
When dealing with files it would be nice to avoid an extra block. This is not the same as the __exit__ method because you would have to write a function to just catch an error. BTW, why not "with...except.."? Just like "for" and "while" loops have "else" clauses. with open('foo') as f: print(f.read()) except IOError: print('Problem with the file!') -- João Bernardo
On Sat, Mar 2, 2013 at 2:45 PM, Alan Johnson <alan@breakrs.com> wrote:
try with context_manager(): … bunch of code … except: … exception handler …
This optimization saves a colon and some white space and mixes two unrelated concepts. The try/except pattern I want to optimize is try: x = expr1 except ValueError: x = expr2 For example: expr1 except ValueError else expr2 or try expr1 except ValueError else expr2 This is particularly useful in cases like this: a = ((try t.x except AttributeError else 0) + (try t.y except AttributeError else 0) + (try t.z except AttributeError else 0)) where standard try/except requires 13 lines and is much harder to read. Yes, this can be done with a function and two lambdas (and I've done it this way): try_except(lambda: expr1, ValueError, lambda: expr2) def try_except(value, exceptions, otherwise): try: return value() except exceptions or Exception: return otherwise() --- Bruce Learn how hackers think: http://j.mp/gruyere-security
On 04/03/13 09:31, Bruce Leban wrote:
The try/except pattern I want to optimize is
try: x = expr1 except ValueError: x = expr2
For example:
expr1 except ValueError else expr2 or try expr1 except ValueError else expr2
That syntax gets a big NO from me, due to confusion with the none one-line try...except...else statement. Written out in full, try blocks look something like this: try: block except ValueError: block else: block finally: block where the else clause runs if no exception occurred. Inserting "else" into the one-liner form, when the "else" doesn't have the same meaning as "else" in the multiline form, is just confusing. Also, I vote -1 on a one-line *statement* (as per the subject line). What's the point of saving one lousy line? We can already do a two-line form: try: statement1 except ValueError: statement2 which is plenty compact enough. But a try...except *expression*, I'm cautiously interested in that idea. It could be analogous to the if...else ternary operator: y = x + (expr1 if condition else expr2) Something like this perhaps? y = x + (try expr1 except Exception: expr2) If you want to catch multiple exceptions, you can use a tuple: y = x + (try expr1 except (Exception, AnotherException): expr2) If you need to refer to the exception: y = x + (try expr1 except Exception as name: expr2) Supporting multiple except clauses would soon get out of hand, I suggestion we restrict the expression form to only a single except clause. Likewise, the else and finally clauses don't really make sense in an expression. This is what I expect the full syntax should be: try_expr ::= "try" expression "except" [expression ["as" target]] ":" expression I'm conflicted about the bare except form. If I had the keys to the time machine, I'd remove bare exceptions from the language. But since they're already supported, I guess we should support it here too. If the "as target" form is used, the name only exists inside the except clause and does not otherwise become visible in the local scope. But of course you can return the exception object should you so choose.
This is particularly useful in cases like this:
a = ((try t.x except AttributeError else 0) + (try t.y except AttributeError else 0) + (try t.z except AttributeError else 0))
This example is not terribly convincing, since it can so easily be re-written: a = sum(getattr(t, name, 0) for name in "xyz") -- Steven
participants (11)
-
Alan Johnson
-
Andrew Barnert
-
Bruce Leban
-
Greg Ewing
-
João Bernardo
-
Nick Coghlan
-
Oscar Benjamin
-
Stefan Behnel
-
Steven D'Aprano
-
Terry Reedy
-
Tomasz Rybak