[Python-ideas] PEP 3156 feedback

Amaury Forgeot d'Arc amauryfa at gmail.com
Tue Dec 18 11:54:40 CET 2012


2012/12/18 Antoine Pitrou <solipsis at pitrou.net>

> My own opinion about Twisted's API is that the Factory class is often
> useless, and adds a cognitive burden. If you need a place to track all
> protocols of a given kind (e.g. all connections), you can do it
> yourself. Also, the Factory implies that you don't control how exactly
> your protocol gets instantiated (unless you override some method on the
> Factory I'm missing the name of: it is cumbersome).
>
> So, when creating a client, I would pass it a protocol instance.
>

Factories are useful to implement clients that reconnect automatically:
the framework needs to spawn a new protocol object.
The connect method could take a protocol class,
but how would you implement the reconnect strategy?

When creating a server, I would pass it a protocol class. Here the base
> Protocol class comes into play, its __init__() could take the transport
> as argument and set the "transport" attribute with it. Further args
> could be optionally passed to the constructor:
>
> class MyProtocol(Protocol):
>     def __init__(self, transport, my_personal_attribute):
>         Protocol.__init__(self, transport)
>         self.my_personal_attribute = my_personal_attribute
>
    ...
>
> def listen(ioloop):
>     # Each new connection will instantiate a MyProtocol with "foobar"
>     # for my_personal_attribute.
>     ioloop.listen_tcp(("0.0.0.0", 8080), MyProtocol, "foobar")
>

This is indeed very similar to a factory function (a callback that creates
the protocol)
Anything with a __call__ would be acceptable IMO.

(The hypothetical listen_tcp() is just a name: perhaps it's actually
> start_serving(). It should accept any callable, not just a class:
> therefore, you can define complex behaviour if you like)
>
>
> I think the transport / protocol registration must be done early, not in
> connection_made(). Sometimes you will want to do things on a protocol
> before you know a connection is established, for example queue things
> to write on the transport. An use case is a reconnecting TCP client:
> the protocol will continue existing at times when the connection is
> down.
>

We should be clear on what a protocol is. In my mind, a protocol manages
the events on a given transport; it will also probably buffer data.
For example, data for the HTTP protocol always starts with "GET ...
HTTP/1.0\r\n".
If a protocol can change transports in the middle, it can be difficult to
track
which socket you write to or receive from, and manage your buffers
correctly.

An alternative could be a "reset()" method, but then we are not far from a
factory class.


> * connection_lost(): you definitely want to know whether it's you or the
>   other end who closed the connection. Typically, if the other end
>   closed the connection, you will have to run some cleanup steps, and
>   perhaps even log an error somewhere (if the connection was closed
>   unexpectedly).
>   Actually, I'm not sure it's useful to call connection_lost() when you
>   closed the connection yourself: are there any use cases?
>

The "yourself" can in another part of the code; some protocols will
certainly
close the connection when they receive unexpected data.
Also, this example from Twisted documentation:
    attempt = myEndpoint.connect(myFactory)
    reactor.callback(30, attempt.cancel)
Even if these lines appear in my code, it's easier to have all errors caught
in one place. The alternative would be:
    attempt = myEndpoint.connect(myFactory)
    def cancel_attempt_and_notify_error():
        attempt.cancel()
        notify_error("cancelled after timeout")
    reactor.callback(30, cancel_attempt_and_notify_error)


-- 
Amaury Forgeot d'Arc
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20121218/1e2fc687/attachment.html>


More information about the Python-ideas mailing list