<br><br><div class="gmail_quote">On Fri, Nov 18, 2011 at 10:24 PM, Ron Adam <span dir="ltr"><<a href="mailto:ron3200@gmail.com">ron3200@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
I was able to create a patch for testing this idea. The hard part was<br>
in getting to know cpython well enough to do it. :-)<br>
<br>
<br>
To get it to work, I made the following change in ceval.c so that the<br>
main loop will accept a pointer rather than an int for the throwflag.<br>
That allows an opcode to set it before it yields an exception. ie... a<br>
reverse throw.<br>
<br>
PyObject *<br>
PyEval_EvalFrameEx(PyFrameObject *f, int throwflag) {<br>
return PyEval_EvalFrame_Ex(f, &throwflag);<br>
}<br>
<br>
PyObject *<br>
PyEval_EvalFrame_Ex(PyFrameObject *f, int *throwflag)<br>
{<br>
...<br>
TARGET(YIELD_EXCEPT)<br>
*throwflag = 1;<br>
retval = POP();<br>
f->f_stacktop = stack_pointer;<br>
why = WHY_YIELD;<br>
goto fast_yield;<br>
...<br>
<br>
<br>
The genobject gen_send_ex() function checks the throwflag value after a<br>
send to see if it got a thrown out exception back. (Rather than one<br>
yielded out.)<br>
<br>
A new keyword 'throws' was needed to go with the YIELD_EXCEPT opcode. I<br>
didn't see anyway to do it with a function or method. It should be<br>
possible to set the exception in the ceval loop rather than yielding it<br>
out, but I think that would be more complex.<br>
<br>
Because the exception isn't raised inside the generator code object, but<br>
is yielded out first, the generator can be continued as if it was a<br>
regular yielded value. No magic required, and no fiddling with the<br>
exception stack was needed. It doesn't effect unexpected exceptions, or<br>
exceptions raised with raise. Those will terminate a generator as<br>
always.<br>
<br>
Python 3.3.0a0 (qbase qtip tip yield_except:92ac2848438f+, Nov 18 2011,<br>
21:59:57)<br>
[GCC 4.6.1] on linux<br>
Type "help", "copyright", "credits" or "license" for more information.<br>
>>> def G():<br>
... yield 1<br>
... throws ValueError<br>
... yield 2<br>
...<br>
>>> g = G()<br>
>>> next(g)<br>
1<br>
>>> next(g)<br>
Traceback (most recent call last):<br>
File "<stdin>", line 1, in <module><br>
ValueError<br>
>>> next(g)<br>
2<br>
>>> next(g)<br>
Traceback (most recent call last):<br>
File "<stdin>", line 1, in <module><br>
StopIteration<br>
<br>
<br>
The three main benefits of being able to do this are...<br>
<br>
* To use switch like exception structures for flow control in schedulers<br>
and coroutines.<br>
<br>
* For consumer type coroutines to be able reject and re-request data in<br>
a nice way without terminating. ie.. the reverse of throwing in an<br>
exception in, in order to change what a generator does.<br>
<br>
* It creates alternative channels for data input and output by being<br>
able to both throw exceptions in and out of generators. Those signals<br>
can carry objects in and out and not burden the fast yield data path<br>
with testing for special wrapper objects.<br>
<br>
<br>
Here's an example of it being used in a simple scheduler.<br>
-----------<br>
<br>
class Suspend(Exception): pass<br>
<br>
def Person(name, count, mode):<br>
n = 0<br>
while n < count:<br>
if mode == 0:<br>
# The normal data path.<br>
yield name, count<br>
else:<br>
# Use an exception as an alternative data path.<br>
throws Suspend(name, count)<br>
n += 1<br>
# return<br>
raise StopIteration(name, n)<br>
<br>
def main(data, mode):<br>
stack = [Person(*(args + (mode,))) for args in data]<br>
results = []<br>
while stack:<br>
done = []<br>
for ct in stack:<br>
try:<br>
print('yield', *next(ct)) # from yield<br>
except Suspend as exc:<br>
print('throws', *exc.args) # from throws<br>
except StopIteration as exc:<br>
results.append(exc.args)<br>
continue<br>
done.append(ct)<br>
stack = done<br>
print(results)<br>
return results<br>
<br>
if __name__ == "__main__":<br>
data = [("John", 2), ("Micheal", 3), ("Terry", 4)]<br>
results1 = main(data, 0)<br>
results2 = main(data, 1)<br>
assert(results1 == results2 == data)<br>
<br>
-------------<br>
The output looks like...<br>
<br>
yield John 2<br>
yield Micheal 3<br>
yield Terry 4<br>
yield John 2<br>
yield Micheal 3<br>
yield Terry 4<br>
yield Micheal 3<br>
yield Terry 4<br>
yield Terry 4<br>
[('John', 2), ('Micheal', 3), ('Terry', 4)]<br>
throws John 2<br>
throws Micheal 3<br>
throws Terry 4<br>
throws John 2<br>
throws Micheal 3<br>
throws Terry 4<br>
throws Micheal 3<br>
throws Terry 4<br>
throws Terry 4<br>
[('John', 2), ('Micheal', 3), ('Terry', 4)]<br>
<br>
<br>
This shows that 'throws' works a lot like 'yield'.<br>
<br></blockquote><div><br></div><div>neat!</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
Open issues:<br>
<br>
* A better name than 'throws' might be good.<br></blockquote><div><br></div><div>I don't like adding another keyword or confusing things by adding the "throw" verb to a language that already firmly uses the verb "raise" when speaking of exceptions.</div>
<div><br></div><div>A double word syntax might make sense here. It'd be good to keep 'yeild' in it to make it clear that this is offering the exception out in a non-terminal manner.</div><div><br></div><div>
yield raise MyException(x,y,z)</div>
<div><br></div><div>or if you've caught an exception from something and want to pass that on to the caller without killing the iteration the natural base form of that would be:</div><div><br></div><div> yield raise</div>
<div><br></div><div>to unset the existing exception and yeild it out instead, mirroring what a bare raise within an except: clause does.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
* Should it get the object sent in.<br>
<br>
<object> = throws <exception><br>
<br>
Or should it be ...<br>
<br>
throws <exception><br>
<br>
* What would be the best argument form.. Should it take the same<br>
arguments as raise or just a single expression.<br>
<br>
<br>
Python's test suite passes as this doesn't change anything that already<br>
works.<br>
<br>
I haven't tested it with the yield-from patch yet, but I think if it can<br>
throw out exceptions in the same way yield-from yields out, that it will<br>
make some things easier and nicer to do.<br>
<br>
If anyone is interested, I can create a tracker item and put the patch<br>
there where it can be improved further.<br>
<br>
Cheers,<br>
Ron<br>
<br>
<br>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="http://mail.python.org/mailman/listinfo/python-ideas" target="_blank">http://mail.python.org/mailman/listinfo/python-ideas</a><br>
</blockquote></div><br>