On Fri, Apr 9, 2021 at 1:38 AM anthony.flury via Python-ideas
<python-ideas@python.org> wrote:
> ... indented twice just to accommodate the outer try block.
>
> Treating the 'with' as an implied `try` would reduce the march to the right - 
> now the key processing of the resource is now indented only one level - and 
> the association of the exception
> from the `with` block is syntactically clear.
>

Normally I'd be -1 on proposals whose main benefit is "one fewer
indentation level". We don't need complexity that serves no other
purpose than that. But the with/try combination is somewhat special -
the 'with' block is inherently about exception handling.

But there's this big question:

On Fri, Apr 9, 2021 at 2:24 AM Paul Bryan <pbr...@anode.ca> wrote:
> Q. Safe to assume this would catch exceptions from both the call to `open` as 
> well as the call to `open.__enter__`?

No, not safe to assume. It's equally reasonable to define it as
guarding only the body of the statement, or as guarding the header as
well. The semantics have to be locked in one way or the other, and
half the time that's going to be incorrect.

IMO this would be a bug magnet on the difference between "exceptions
that get handed to __exit__" and "exceptions that get caught by the
associated except clause". For instance, what happens in this
situation:

with database_transaction(conn) as self.trn:
    self.trn.execute("insert blah blah blah")
else:
    with database_transaction(conn) as self.trn:
        self.trn.execute("update set blah blah blah")

(Yes, I know a good few database engines have a robust upsert/merge
operation, but bear with me here.) The else clause implies that its
body will not be executed if any exception was raised. But which of
these exceptions would prevent that else from happening?

* NameError on database_transaction or conn
* ConnectionLostError inside database_transaction()
* MemoryError in the transaction's __enter__()
* AttributeError assigning to self.trn
* ResourceError during the __del__ of the previous trn (freebie - that
should be ignored regardless)
* KeyboardInterrupt after assigning to self.trn but before beginning the block
* QueryConflictError inside execute() (another freebie - obviously
this one should)
* CommitFailedError in __exit__, after no other exception
* RollbackFailedError in __exit__, after some other exception

(And yes, RollbackFailed is almost a freebie as well, since I have an
"else" and no "except" here, but that'd be different with some actual
except clauses, so it's worth clarifying.)

If it's defined as simply nesting the with inside a try, then the
answer is "all of the above". But that would be inconsistent with the
existing meaning, which wouldn't cover nearly as much.

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?

ChrisA
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/6TWIPTXPKYMQ7D5RT3YOYMBMI5DHZU32/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to