[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