I prefer to use a try/finally statement. One of the points of using yield-from is that you can always tell where your code may be suspended by searching for "yield from". With your proposed change that would no longer be true -- any with statement would also have to be inspected, and there would no longer be a way to know from the source alone whether it might yield or not (because it would dynamic -- there's no way to be sure at compile time which context manager is being used).


On Sun, Apr 6, 2014 at 12:02 PM, Andrew Svetlov <andrew.svetlov@gmail.com> wrote:
Literally it may be generator itself or function that returns generator object.

Now I'm working on postgres library for asyncio (http://aiopg.readthedocs.org/).

And I would to use *with statement* for transaction handling like:

with (yield from cursor.transaction()):
   yield from cursor.execute(sql)

The problem is: at exit of *with statement* I need to call `yield from
cursor.execute('COMMIT')` or `yield from cursor.execute('ROLLBACK')`.

I can do it only in __exit__ in *context manager*, but python
understand only if __exit__:
- returns true value, that suppresses exception from code block
- returns None or any false value to propagate exception if any
- raises exception itself

I propose to add new rule:
IF the code object is generator (co_flags & CO_GENERATOR) and __exit__
returns generator object (isinstance(ret, types.GeneratorType))
THEN do `yield from ret`.

That's work fine if __exit__ itself is a *generator function*
(contains `yield` or `yield from` statements): call to *generator
function* returns *generator object*.

The proposal:
1. Doesn't break any existing code except if user accidentally
returns generator object instead of True from __exit__ call (he should
not to do this and I sure this is very rare case).
2. Don't requires new syntax.

asyncio itself uses:

with (yield from lock):
   BLOCK

for locking etc but unlocking for asyncio objects doesn't requires any
`yield from`, so __exit__ code is just plain function but not
generator.

Also I can live with asyncio trick for __enter__:
https://code.google.com/p/tulip/source/browse/asyncio/locks.py#156
The way is a but annoying but unrolling a value returned by __enter__
if the value is generator object will break existing code, sure.

Thoughts?


--
Thanks,
Andrew Svetlov
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/



--
--Guido van Rossum (python.org/~guido)