[Python-Dev] Integrating PEP 310 with PEP 340

Nick Coghlan ncoghlan at iinet.net.au
Wed Apr 27 15:27:35 CEST 2005


This is my attempt at a coherent combination of what I like about both proposals 
(as opposed to my assortment of half-baked attempts scattered through the 
existing discussion).

PEP 340 has many ideas I like:
   - enhanced yield statements and yield expressions
   - enhanced continue and break
   - generator finalisation
   - 'next' builtin and associated __next__() slot
   - changes to 'for' loop

One restriction I don't like is the limitation to ContinueIteration and 
StopIteration as arguments to next(). The proposed semantics and conventions for 
ContinueIteration and StopIteration are fine, but I would like to be able to 
pass _any_ exception in to the generator, allowing the generator to decide if a 
given exception justifies halting the iteration.

The _major_ part I don't like is that the block statement's semantics are too 
similar to those of a 'for' loop. I would like to see a new construct that can 
do things a for loop can't do, and which can be used in _conjunction_ with a for 
loop, to provide greater power than either construct on their own.

PEP 310 forms the basis for a block construct that I _do_ like. The question 
then becomes whether or not generators can be used to write useful PEP 310 style 
block managers (I think they can, in a style very similar to that of the looping 
block construct from PEP 340).

Block statement syntax from PEP 340:

     block EXPR1 [as VAR1]:
             BLOCK1

Proposed semantics (based on PEP 310, with some ideas stolen from PEP 340):

     blk_mgr = EXPR1
     VAR1 = blk_mgr.__enter__()
     try:
         try:
             BLOCK1
         except Exception, exc:
             blk_mgr.__except__(exc)
         else:
             blk_mgr.__else__()
     finally:
         blk_mgr.__exit__()

'blk_mgr' is a hidden variable (as per PEP 340).

Note that nothing special happens to 'break', 'return' or 'continue' statements 
with this proposal.

Generator methods to support the block manager protocol used by the block statement:

     def __enter__(self):
         try:
             return next(self)
         except StopIteration:
             raise RuntimeError("Generator exhausted before block statement")


     def __except__(self, exc):
         try:
             next(self, exc)
         except StopIteration:
             pass

     def __no_except__(self):
         try:
             next(self)
         except StopIteration:
             pass

     def __exit__(self):
         pass

Writing simple block managers with this proposal (these should be identical to 
the equivalent PEP 340 block managers):

   def opening(name):
       opened = open(name)
       try:
           yield opened
       finally:
           opened.close()

   def logging(logger, name):
       logger.enter_scope(name)
       try:
           try:
               yield
           except Exception, exc:
               logger.log_exception(exc)
       finally:
           logger.exit_scope()

   def transacting(ts):
       ts.begin()
       try:
           yield
       except:
           ts.abort()
       else:
           ts.commit()

Using simple block managers with this proposal (again, identical to PEP 340):

   block opening(name) as f:
     pass

   block logging(logger, name):
     pass

   block transacting(ts):
     pass

Obviously, the more interesting block managers are those like auto_retry (which 
is a loop, and hence an excellent match for PEP 340), and using a single 
generator in multiple block statements (which PEP 340 doesn't allow at all). 
I'll try to get to those tomorrow (and if I can't find any good use cases for 
the latter trick, then this idea can be summarily discarded in favour of PEP 340).

Cheers,
Nick.

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


More information about the Python-Dev mailing list