On 5/5/2016 10:17 PM, Nick Coghlan wrote:
On 6 May 2016 at 05:17, Kyle Lahnakoski <klahnakoski@mozilla.com> wrote:
May you provide me with an example of how contextmanager would help with
the indentation?
contextmanager doesn't, ExitStack does (which is in the standard
library for 3.3+, and available via contextlib2 for earlier versions).

Thank you for the examples, and they do reduce indention when you have callbacks defined, or dealing with resources.  This does not seem to help in the case of exception handlers.   Exception handlers have access to block scope variables, and have code blocks that do not require a `def`.

I should have added different types of exception handlers to my initial email to distract from the chaining exception.   Consider the exception handler that simply stops trying to process the todo items:
____def process_todo(todo):
________pre_process()
________for t in todo:
____________except Exception, e:
________________break
____________process(t)
________post_process()    


As an alternative to 

____def process_todo(todo):
________pre_process()
________for t in todo:
____________try
________________process(t)
____________except Exception, e:
________________break
________post_process()    

Of course, a couple `try` nested statements make the indentation worse.  For example, we wish to ignore problems in dealing with todo items, like above, but the todo items are complicated; each is a dict() of details.  Failure to deal with one of the details is ignored, but the rest of the details are still attempted:

def process_todo(todo):
____pre_process()
____for t in todo:
________except Exception, e:
____________break
________for u, v in t.items():
____________except Exception, e:
________________continue 
____________process()
____post_process()   

Which is better than what I do now:

def process_todo(todo):
____pre_process()
____for t in todo:
________try:
____________for u, v in t.items():
________________try:
____________________process()
________________except Exception, e:
____________________continue 
________except Exception, e:
____________break
____post_process()   

I have not touched on more complicated except clauses; ones that have multiple lines, and ones that use, or update block-scoped variables.

ExitStack is good for manipulating `with` clauses as first order objects, but they are limited to what you can do with `with` statements: Limited to naive exception handling, or simple chaining.   If was to use ExitStack in the example above (using `continue` and `exit`), maybe I would write something like:

def process_todo(todo):
____with ExitStack() as stack:
________pre_process()
________for t in todo:
____________stack.enter_context(BreakOnException())
____________for u, v in t.items():
________________stack.enter_context(ContinueOnException())
________________process()
________________stack.pop()  # DOES THIS EXIST?  IT SHOULD
________post_process()  

This terrible piece of (not working) code assumes it is even possible to write a `BreakOnException` and `ContinueOnException`. It also requires a `pop` method, which is not documented, but really should exist.  Without a `pop` method, I would need another ExitStack instance, and a `with` statement to hold it.

I hope I have convinced you that ExitStack, and `with` block magic, does a poor job of covering the use cases for `try` statements.  Maybe I should make examples that use the loop variables in the `except` clause for more examples?

At a high level:  The `try/except/finally` is powerful structure.  Realizing that the `try/finally` pair is very common leads to the creation of `with`. I am proposing the same logic for `try/catch` pair:  They are commonly paired together and should have an optimized syntax.