Interesting. That's certainly a nice API, but that then again (read_until) sounds like something I'd implement using dataReceived... You know, read_until clears the buffer, logs the requested callback. data_received adds something to the buffer, and checks if it triggered the (one of the?) registered callbacks.<br>

<br>Of course, I may just be rusted in my ways and trying to implement everything in terms of things I know (then again, that might be just what's needed when you're trying to make a useful general API).<br>
<br>I guess it's time for me to go deep-diving into Tornado :)<br><br><div class="gmail_quote">On Sat, Oct 13, 2012 at 7:27 PM, Ben Darnell <span dir="ltr"><<a href="mailto:ben@bendarnell.com" target="_blank">ben@bendarnell.com</a>></span> wrote:<br>


<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div>On Sat, Oct 13, 2012 at 10:18 AM, Laurens Van Houtven <_@lvh.cc> wrote:<br>
> What calls on_headers in this example? Coming from twisted, that seems like<br>
> dataReceived's responsibility, but given your introductory paragraph that's<br>
> not actually what goes on here?<br>
<br>
</div>The IOStream does, after send_request calls<br>
stream.read_until("\r\n\r\n", on_headers).  Inside IOStream, there is<br>
a _handle_read method that is registered with the IOLoop and fills up<br>
a buffer.  When the read condition is satisfied the IOStream calls<br>
back into application code.<br>
<span><font color="#888888"><br>
-Ben<br>
</font></span><div><div><br>
><br>
><br>
> On Sat, Oct 13, 2012 at 7:07 PM, Ben Darnell <<a href="mailto:ben@bendarnell.com" target="_blank">ben@bendarnell.com</a>> wrote:<br>
>><br>
>> On Fri, Oct 12, 2012 at 11:14 PM, Antoine Pitrou <<a href="mailto:solipsis@pitrou.net" target="_blank">solipsis@pitrou.net</a>><br>
>> wrote:<br>
>> > On Fri, 12 Oct 2012 15:11:54 -0700<br>
>> > Guido van Rossum <<a href="mailto:guido@python.org" target="_blank">guido@python.org</a>> wrote:<br>
>> >><br>
>> >> > 2. Method dispatch callbacks:<br>
>> >> ><br>
>> >> >     Similar to the above, the reactor or somebody has a handle on<br>
>> >> > your<br>
>> >> > object, and calls methods that you've defined when events happen<br>
>> >> >     e.g. IProtocol's dataReceived method<br>
>> >><br>
>> >> While I'm sure it's expedient and captures certain common patterns<br>
>> >> well, I like this the least of all -- calling fixed methods on an<br>
>> >> object sounds like a step back; it smells of the old Java way (before<br>
>> >> it had some equivalent of anonymous functions), and of asyncore, which<br>
>> >> (nearly) everybody agrees is kind of bad due to its insistence that<br>
>> >> you subclass its classes. (Notice how subclassing as the prevalent<br>
>> >> approach to structuring your code has gotten into a lot of discredit<br>
>> >> since 1996.)<br>
>> ><br>
>> > But how would you write a dataReceived equivalent then? Would you have<br>
>> > a "task" looping on a read() call, e.g.<br>
>> ><br>
>> > @task<br>
>> > def my_protocol_main_loop(conn):<br>
>> >     while <some_condition>:<br>
>> >         try:<br>
>> >             data = yield conn.read(1024)<br>
>> >         except ConnectionError:<br>
>> >             conn.close()<br>
>> >             break<br>
>> ><br>
>> > I'm not sure I understand the problem with subclassing. It works fine<br>
>> > in Twisted. Even in Python 3 we don't shy away from subclassing, for<br>
>> > example the IO stack is based on subclassing RawIOBase, BufferedIOBase,<br>
>> > etc.<br>
>><br>
>> Subclassing per se isn't a problem, but requiring a single<br>
>> dataReceived method per class can be awkward.  Many protocols are<br>
>> effectively state machines, and modeling each state as a function can<br>
>> be cleaner than a big if/switch block in dataReceived.  For example,<br>
>> here's a simplistic HTTP client using tornado's IOStream:<br>
>><br>
>>        from tornado import ioloop<br>
>>         from tornado import iostream<br>
>>         import socket<br>
>><br>
>>         def send_request():<br>
>>             stream.write("GET / HTTP/1.0\r\nHost: <a href="http://friendfeed.com" target="_blank">friendfeed.com</a>\r\n\r\n")<br>
>>             stream.read_until("\r\n\r\n", on_headers)<br>
>><br>
>>         def on_headers(data):<br>
>>             headers = {}<br>
>>             for line in data.split("\r\n"):<br>
>>                parts = line.split(":")<br>
>>                if len(parts) == 2:<br>
>>                    headers[parts[0].strip()] = parts[1].strip()<br>
>>             stream.read_bytes(int(headers["Content-Length"]), on_body)<br>
>><br>
>>         def on_body(data):<br>
>>             print data<br>
>>             stream.close()<br>
>>             ioloop.IOLoop.instance().stop()<br>
>><br>
>>         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)<br>
>>         stream = iostream.IOStream(s)<br>
>>         stream.connect(("<a href="http://friendfeed.com" target="_blank">friendfeed.com</a>", 80), send_request)<br>
>>         ioloop.IOLoop.instance().start()<br>
>><br>
>><br>
>> Classes allow and encourage broader interfaces, which are sometimes a<br>
>> good thing, but interact poorly with coroutines.  Both twisted and<br>
>> tornado use separate callbacks for incoming data and for the<br>
>> connection being closed, but for coroutines it's probably better to<br>
>> just treat a closed connection as an error on the read.  Futures (and<br>
>> yield from) give us a nice way to do that.<br>
>><br>
>> -Ben<br>
>> _______________________________________________<br>
>> Python-ideas mailing list<br>
>> <a href="mailto:Python-ideas@python.org" target="_blank">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>
><br>
><br>
><br>
><br>
> --<br>
> cheers<br>
> lvh<br>
><br>
</div></div></blockquote></div><br><br clear="all"><br>-- <br>cheers<div>lvh</div><br>