[Python-ideas] Revised**12 PEP on Yield-From

Jacob Holm jh at improva.dk
Sun Apr 19 15:46:28 CEST 2009

Greg Ewing wrote:
> Draft 13 of the PEP.
> Adjusted the expansion so as not to suggest that
> a throw or close attribute with the value None
> should be treated as a missing method.

A few more details about the expansion.

First a minor nit.  There is no need to use getattr if the name is 
constant and you are going to catch AttributeError anyway.  Just use 
_i.close and _i.throw.

Next a more serious issue.  The current use of *sys.exc_info() in the 
throw handling is actually wrong.  Getting the "throw" attribute may run 
arbitrary python code which could easily replace the exception.

We should probably add

   _x = sys.exc_info()

as the first line in the "except BaseException as _e" block, and change

   _y = _m(*sys.exc_info())


   _y = _m(*_x)

Alternatively, we could get the signature of throw() fixed so that it 
matches the "raise" statement again.  Then we can drop the use of 
sys.exc_info(), and just use:

   _y = _m(_e)

But that is probably out of scope for this PEP.

Other than that, I think the expansion matches what we have agreed on.

FWIW I still consider an expansion using functools.partial to be more 
readable because it centralizes the StopIteration handling and reduces 
the code nesting.  Here is an updated version of such an expansion that 
is semantically equivalent to the PEP rev 13 + my suggested fix for the 
sys.exc_info() issue:

     _i = iter(EXPR)
     _p = functools.partial(next, _i)
     while 1:
             _y = _p()
         except StopIteration as _e:
             _r = _e.value
             _s = yield _y
         except GeneratorExit as _e:
                 _m = _i.close
             except AttributeError:
             raise _e
         except BaseException as _e:
             _x = sys.exc_info()
                 _m = _i.throw
             except AttributeError:
                 raise _e
             _p = functools.partial(_m, *_x)
             if _s is None:
                 _p = functools.partial(next, _i)
                 _p = functools.partial(_i.send, _s)
     RESULT = _r

If you don't like it, fine.  Since it is only a presentation detail, I 
won't push for it.  I *would* like to hear your reasoning, but will 
accept whatever conclusion you come to, I think :)

Anyway, it looks to me like we are almost done.  What are the chances of 
getting this into 3.1 and 2.7?

Hopefully-soon-using-yield-from-for-real-ly yours
- Jacob

More information about the Python-ideas mailing list