
On 2021-09-29 11:46 p.m., Steven D'Aprano wrote:
In Soni's original code snippet, there is a clear separation of code that is inside the try block from code that is outside the try block:
def a_potentially_recursive_function(some, args): try: some.user_code() except ExceptionWeCareAbout as exc: raise RuntimeError from exc code_we_assume_is_safe() # and more code following...
This is good exception hygiene.
Only some.user_code is guarded by the try block. If it turns out that code_we_assume_is_safe is not actually safe, and fails with an exception, it won't be caught by the try block and you will know about it.
Except no, because ExceptionWeCareAbout is part of the public API. If it's not actually safe, it might raise an ExceptionWeCareAbout... ... which gets promptly swallowed by the API consumer, who thinks it was intentional. But any ExceptionWeCareAbout in user code, because it's explicitly guarded against/wrapped, doesn't get silently swallowed the same way. [no comments on the rest of your points because they're all based on this core misunderstanding.]
In your re-written syntactic sugar, you have:
# new syntax
def a_potentially_recursive_function(some, args) with ExceptionWeCareAbout: some.user_code() code_we_assume_is_safe() # and more code following...
which becomes:
def a_potentially_recursive_function(some, args): try: some.user_code() code_we_assume_is_safe() # and more code following... except ExceptionWeCareAbout as exc:
which is the opposite of good exception hygiene. Too much code, including the wrong code, is guarded by the try block, which means that the compiler has to *guess your meaning* and set a flag to decide whether to re-raise the exception or run the except block.
Its not clear how the compiler guesses that. Is it only because you have an explicit `raise ExceptionWeCareAbout` in the code? What if the exception is not explicit?
# explicitly raising ExceptionWeCareAbout raise ExceptionWeCareAbout
# not explicit raise some_exception_object_we_prepared_earlier
raise prepare_exception(*args) # returns an ExceptionWeCareAbout instance
verify_or_raise(condition) # raises ExceptionWeCareAbout
Obviously this doesn't solve exception handling, doesn't require the caller to catch the exceptions, etc etc.
So that's two points against it.
It does, however, encourage better exception hygiene.
Except it doesn't, it makes it worse. So that's three points against it.
Chances are something like this would significantly reduce the amount of swallowed exceptions, if it gets widely adopted.
Or based on your examples, increase the number of swallowed exceptions, and be much harder to refactor code safely.