[Python-Dev] Re: anonymous blocks

Nick Coghlan ncoghlan at gmail.com
Tue Apr 26 17:14:51 CEST 2005


Michael Hudson wrote:
> This is a non-starter, I hope.  I really meant what I said in PEP 310 
> about loops being loops.

The more I play with this, the more I want the 'with' construct to NOT be a loop 
construct.

The main reason is that it would be really nice to be able to write and use a 
multipart code template as:

def template():
   # pre_part_1
   yield None
   # post_part_1
   yield None
   # pre_part_2
   yield None
   # post_part_2
   yield None
   # pre_part_3
   yield None
   # post_part_3

def user():
   block = template()
   with block:
     # do_part_1
   with block:
     # do_part_2
   with block:
     # do_part_3

If 'with' is a looping construct, the above won't work, since the first usage 
will drain the template.

Accordingly, I would like to suggest that 'with' revert to something resembling 
the PEP 310 definition:

     resource = EXPR
     if hasattr(resource, "__enter__"):
         VAR = resource.__enter__()
     else:
         VAR = None
     try:
         try:
             BODY
         except:
             raise # Force realisation of sys.exc_info() for use in __exit__()
     finally:
         if hasattr(resource, "__exit__"):
             VAR = resource.__exit__()
         else:
             VAR = None

Generator objects could implement this protocol, with the following behaviour:

     def __enter__():
         try:
             return self.next()
         except StopIteration:
             raise RuntimeError("Generator exhausted, unable to enter with block")

     def __exit__():
         try:
             return self.next()
         except StopIteration:
             return None

     def __except__(*exc_info):
         pass

     def __no_except__():
         pass

Note that the code template can deal with exceptions quite happily by utilising 
sys.exc_info(), and that the result of the call to __enter__ is available 
*inside* the with block, while the result of the call to __exit__ is available 
*after* the block (useful for multi-part blocks).

If I want to drain the template, then I can use a 'for' loop (albeit without the 
cleanup guarantees).

Taking this route would mean that:
   * PEP 310 and the question of passing values or exceptions into iterators 
would again become orthogonal
   * Resources written using generator syntax aren't cluttered with the 
repetitive try/finally code PEP 310 is trying to eliminate
   * 'for' remains TOOW to write an iterative loop
   * it is possible to execute _different_ suites between each yield in the 
template block, rather than being constrained to a single suite as in the 
looping case.
   * no implications for the semantics of 'return', 'break', 'continue'
   * 'yield' would not be usable inside a with block, unless the AbortIteration 
concept was adopting for forcible generator termination.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
---------------------------------------------------------------
             http://boredomandlaziness.skystorm.net


More information about the Python-Dev mailing list