[Python-ideas] Block-Scoped Exception Handlers

Robert van Geel robert at bign.nl
Wed May 4 18:45:02 EDT 2016


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
> *********************************************



More information about the Python-ideas mailing list