On Fri, Apr 9, 2021 at 6:41 AM Ethan Furman firstname.lastname@example.org wrote:
On 4/8/21 1:21 PM, Chris Angelico wrote:
On Fri, Apr 9, 2021 at 6:16 AM Ethan Furman wrote:
On 4/8/21 11:25 AM, Chris Angelico wrote:
Similar question: What would be the semantics of this?
with contextlib.suppress(BaseException): a = b / c except BaseException as e: print(e)
What types of exception could be caught and what types couldn't?
Well, if every exception is derived from BaseException (they are) and contextlib.suppress(BaseException) suppresses all BaseException-derived exceptions (it does) then the semantics of the above are:
- "a" will be the result of "b / c" if no exception occurs
- otherwise, "a" will be whatever it was before the with-block
- no exception will ever be caught by the except-clause
Generally speaking, no exception that a context manager handles (i.e. suppresses) will ever be available to be caught.
What about NameError looking up contextlib, or AttributeError looking up suppress? Will they be caught by that except clause, or not?
Ah, good point -- those two would get caught, as they happen before contextlib.suppress() gets control.
Thank you for making my point: your assumption is completely the opposite of the OP's, given the same syntactic structure.
The guidance should be: `try with` behaves exactly as `try/except with a with`, meaning that NameError and AttributeError for those two reasons would still be caught. Yes, it's easy to miss that at first glance, but it's just as easy to miss in the traditional try/except layout:
try: with contextlib.suppress(BaseException): # do stuff except BaseException as e: print(e)
How many people are going to look at that and think, "oh, NameError and AttributeError can still be caught" ?
At least in this form, it's clear that there's a sharp distinction between the stuff around the outside of the 'with' block and the stuff inside it.
The semantics, as suggested, give 'with' blocks two distinct exception-management scopes, which mostly but do not entirely overlap. It's not a problem to rigorously define the semantics, but as evidenced here, people's intuitions will vary depending on which context manager is being used. It's absolutely obvious that the OP's example should let you catch errors from open(), and equally obvious that suppressing BaseException should mean that nothing gets caught. That's the problem here.
As such, I'm -0.5 on this. It's a kinda nice feature, but all it really offers is one less indentation level, and the semantics are too confusing.