[Python-ideas] Tulip / PEP 3156 - subprocess events

Nick Coghlan ncoghlan at gmail.com
Sun Jan 20 07:13:39 CET 2013


On Sun, Jan 20, 2013 at 2:35 PM, Guido van Rossum <guido at python.org> wrote:
> TBH I don't see the protocol implementation getting any simpler
> because of this. There is some protocol initialization code that
> doesn't depend on the transport, and some that does. Using your
> approach, these all go in __init__(). Using the PEP's current
> proposal, the latter go in a separate method, connection_made().

When the two are separated without a clear definition of what else can
happen in between, *every other method on the protocol* needs to cope
with the fact that other calls to protocol methods may happen in
between the call to __init__ and the call to connection_made - you
simply can't write a protocol without dealing with that problem.

As you correctly figured out, my specific proposal was to move from:

    protocol = protocol_factory()
    protocol.connection_made(transport)

To a single event:

    protocol = protocol_factory(transport)

The *reason* I wanted to do this is that I *don't understand* what may
happen to my protocol implementation between construction and the call
to make_connection.

Your description of the current implementation actually worries me, as
it suggests to me that when I get a (transport, protocol) pair back
from a call to "create_connection", "connection_made" may *not* have
been called yet - the protocol may be in exactly the state I am
worried about, because the event loop is sending the notification in a
fire-and-forget fashion, instead of waiting until the call is
complete:

    protocol = protocol_factory()
    loop.call_soon(protocol.connection_made, transport)
    # The protocol isn't actually fully initialized here...


However, that description also made me realise why two distinct
operations are needed, so I'd like to change my suggestion to the
following:

    protocol = factory()
    yield from protocol.connection_made(transport) # Or callback equivalent

The protocol factory would still be used to create the protocol
object. However, the PEP would be updated to make it clear that
immediately after creation the *only* permitted method invocation on
the result is "connection_made", which will complete the protocol
initialization process.

The connection_made event handler would be redefined to return a
*Future* (or equivalent object) rather than completing synchronously.
create_connection would then call connection_made and *wait for it to
finish*, rather than using call_soon in a fire-and-forget fashion.

The advantage of this is that the rationale for the various possible
states become clear:

- the protocol factory is invoked synchronously, and is thus not
allowed to perform any blocking actions (but may trigger
"fire-and-forget" operations)
- connection_made is invoked asynchronously, and is thus able to wait
for various operations
- a protocol returned from create_connection is certain to have had
connection_made already called, thus a protocol implementation may
safely assume in other methods that both __init__ and connection_made
will have been called during the initialization process.

Cheers,
Nick.


--
Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia



More information about the Python-ideas mailing list