[Python-ideas] Block-Scoped Exception Handlers
Kyle Lahnakoski
klahnakoski at mozilla.com
Wed May 4 19:09:04 EDT 2016
Oh dear! It appears I screwed up the indentation. Since I never see
the emails I send to this list, I have no idea what you all were looking
at. Maybe I should have used underscores for email:
def process_todo(todo):
____except Exception, e:
________raise OverallTodoError("Not expected") from e
____with Timer("todo processing"):
________# pre-processing
________for t in todo:
____________except Exception, e:
________________raise TodoError("oh dear!") from e
____________# do error prone stuff
________# post-processing
The exception handling code should be indented, but the code it applies
to is not.
On 5/4/2016 6:45 PM, Robert van Geel wrote:
> It looks frankly a bit like an "on error goto errorhandler" statement
> but with a few drawbacks to that.
>
> How can you indicate the 'exception handled block' should end, when
> there's no indentation level involved? It would force the exception
> block to service all code until the end of that indentation level but
> what happens on within the indentation level is defined by other more
> functional criteria. The proposed construct could not replace the
> normal try/catch syntax because of that, but it can also not mix with
> the normal syntax well because it's too similar to it and hence
> confusing.
>
> The gain of the proposed construct seems to be to save 1 indentation
> level. Even when this would be worth the cost it would be more logical
> to still put the except block at the end of the functional block. So
> instead of the proposed:
>
> def process_todo(todo):
> except Exception, e:
> raise OverallTodoError("Not expected") from e
> with Timer("todo processing"):
> # pre-processing
> for t in todo:
> except Exception, e:
> raise TodoError("oh dear!") from e
>
> # do error p
>
> the below is more logical, since the main code remains the focus and
> the exception the afterthought:
>
> def process_todo(todo):
> with Timer("todo processing"):
> # pre-processing
> for t in todo:
> except Exception, e:
> raise TodoError("oh dear!") from e
>
> # do error p
>
> except Exception, e:
> raise OverallTodoError("Not expected") from e
>
> But this all leaves something implicit rather then implicit: the start
> (or in the proposal: the end) of the exception block.
>
> Robert
>
>> Message: 3
>> Date: Wed, 4 May 2016 15:58:58 -0400
>> From: Kyle Lahnakoski <klahnakoski at mozilla.com>
>> To: python-ideas at python.org
>> Subject: [Python-ideas] Block-Scoped Exception Handlers
>> Message-ID: <00d21501-60fd-d756-c8a6-906a8b235221 at mozilla.com>
>> Content-Type: text/plain; charset=windows-1252
>>
>>
>> Please excuse my nomenclature. I hope the community can correct the
>> synonyms that clarify my proposal.
>>
>> Problem
>> -------
>>
>> I program defensively, and surround many of my code blocks with try
>> blocks to catch expected and unexpected errors. Those unexpected
>> errors seem to dominate in my code; I never really know how many ways my
>> SQL library can fail, nor am I really sure that a particular key is in a
>> `dict()`. Most of the time I can do nothing about those unexpected
>> errors; I simply chain them, with some extra description about what the
>> code block was attempting to do.
>>
>> I am using 2.7, so I have made my own convention for chaining
>> exceptions. 3.x chains more elegantly:
>>
>> for t in todo:
>> try:
>> # do error prone stuff
>> except Exception, e:
>> raise ToDoError("oh dear!") from e
>>
>> The ?error prone stuff? can itself have more try blocks to catch known
>> failure modes, maybe deal with them. Add some `with` blocks and a
>> conditional, and the nesting gets ugly:
>> def process_todo(todo):
>> try:
>> with Timer("todo processing"):
>> # pre-processing
>> for t in todo:
>> try:
>> # do error prone stuff
>> except Exception, e:
>> raise TodoError("oh dear!") from e
>> # post-processing
>> except Exception, e:
>> raise OverallTodoError("Not expected") from e
>>
>> Not only is my code dominated by exception handling, the meaningful code
>> is deeply nested.
>>
>>
>> Solution
>> --------
>>
>> I would like Python to have a bare `except` statement, which applies
>> from that line, to the end of enclosing block (or next `except`
>> statement). Here is the same example using the new syntax:
>>
>> def process_todo(todo):
>> except Exception, e:
>> raise OverallTodoError("Not expected") from e
>> with Timer("todo processing"):
>> # pre-processing
>> for t in todo:
>> except Exception, e:
>> raise TodoError("oh dear!") from e
>>
>> # do error prone stuff
>> # post-processing
>>
>> Larger code blocks do a better job of portraying he visual impact of the
>> reduced indentation. I would admit that some readability is lost
>> because the error handling code precedes the happy path, but I believe
>> the eye will overlook this with little practice.
>>
>> Multiple `except` statements are allowed. They apply as if they were
>> used in a `try` statement; matched in the order declared:
>>
>> def process_todo(todo):
>> pre_processing() # has no exception handling
>>
>> except SQLException, e: # effective until end of method
>> raise Exception("Not expected") from e
>> except Exception, e:
>> raise OverallTodoError("Oh dear!") from e
>>
>> processing()
>>
>> A code block can have more than one `except` statement:
>>
>> def process_todo(todo):
>> pre_processing() # no exception handling
>> except SQLException, e: # covers lines from here to
>> beginning
>> of next except statement
>> raise Exception("Not expected") from e
>> except Exception, e: # catches other exception types
>> raise Exception("Oh dear!") from e
>> processing() # Exceptions caught
>> except SQLException, e: # covers a lines to end of method
>> raise Exception("Happens, sometimes") from e
>> post_processing() # SQLException caught, but not Exception
>> In these cases, a whole new block is effectively defined. Here
>> is the
>> same in legit Python:
>>
>> def process_todo(todo):
>> pre_processing() # no exception handling
>> try:
>> processing() # Exceptions caught
>> except SQLException, e: # covers all lines from here to
>> beginning of next except statement
>> raise Exception("Not expected") from e
>> except Exception, e: # catches other exception types
>> raise Exception("Oh dear!") from e
>> try:
>> post_processing() # SQLException caught, but not Exception
>> except SQLException, e: # covers a lines to end of method
>> raise Exception("Happens, sometimes") from e
>> Other Thoughts
>> --------------
>>
>> I only propose this for replacing `try` blocks that have no `else` or
>> `finally` clause. I am not limiting my proposal to exception chaining;
>> Anything allowed in `except` clause would be allowed.
>> I could propose adding `except` clauses to each of the major statement
>> types (def, for, if, with, etc?). which would make the first example
>> look like:
>>
>> def process_todo(todo):
>> with Timer("todo processing"):
>> # pre-processing
>> for t in todo:
>> # do error prone stuff
>> except Exception, e:
>> raise TodoError("oh dear!") from e
>> # post-processing
>> except Exception, e:
>> raise OverallTodoError("Not expected") from e
>>
>> But, I am suspicious this is more complicated than it looks to
>> implement, and the `except` statement does seem visually detached from
>> the block it applies to.
>>
>>
>> Thank you for your consideration!
>>
>>
>>
>> ------------------------------
>>
>> Subject: Digest Footer
>>
>> _______________________________________________
>> Python-ideas mailing list
>> Python-ideas at python.org
>> https://mail.python.org/mailman/listinfo/python-ideas
>>
>>
>> ------------------------------
>>
>> End of Python-ideas Digest, Vol 114, Issue 35
>> *********************************************
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
More information about the Python-ideas
mailing list