[Twisted-Python] The Trial of the DirtyReactorError

Jean-Paul Calderone exarkun at divmod.com
Fri Apr 13 11:22:05 EDT 2007


On Fri, 13 Apr 2007 10:15:28 +0100, Matthew Glubb <matt at zgroupplc.com> wrote:
>-----BEGIN PGP SIGNED MESSAGE-----
>Hash: SHA1
>
>Hi All,
>
>Following on from my DirtyReactorError errors whilst running unit  tests 
>under trial I opted, as glyph suggested, to call listenTCP  inside the 
>ServerFactory in order to keep track of the port and shut  it down cleanly.
>
>On 12 Apr 2007, at 19:56, glyph at divmod.com wrote:
>>If they are "always shut down", what event currently shuts them  down? Have 
>>your test trigger that event.  If they're one-shot, then  perhaps the 
>>method that calls listenTCP should be on the factory  itself, making it 
>>even easier to keep track of the Port instance it  is associated with.
>
>This causes me a problem. When a client connection is lost, I want to 
>shutdown the server. The only way that I can see to do this is by the 
>connectionLost event calling a method in my ServerFactory that shuts  down 
>the listening port:
>
>class Echo(basic.LineOnlyReceiver):
>
>     def connectionLost(self, reason):
>         self.factory.shutdown(reason)
>
>class EchoServerFactory(protocol.ServerFactory):
>     protocol = Echo
>     port = None
>
>     def __init__(self, port):
>         self.port = reactor.listenTCP(port, self)
>
>     def shutdown(self, reason):
>         self.port.stopListening()
>
>*However*, this results in the following error:
>
>twisted.trial.util.PendingTimedCallsError: pendingTimedCalls still  pending 
>(consider setting twisted.internet.base.DelayedCall.debug =  True): 
><DelayedCall 24706704 [-0.00161504745483s] called=0  cancelled=0 
>Port.connectionLost(<twisted.python.failure.Failure  <class 
>'twisted.internet.error.ConnectionDone'>>)
>
>Obviously, tcp.Port.stopListening() results in the  Echo.connectionLost 
>event being triggered, which in turn calls  EchoServerFactory.shutdown(), 
>which triggers an additional  Echo.connectionLost event. It seems that 
>tcp.Port.connected is not  being updated quickly enough to prevent the 
>additional pendingTimedCalls
>

Port.stopListening can return a Deferred if shutdown is not immediately
completed.  In this case, you need to have trial wait for this Deferred
to fire before letting the test finish.  Also, Port.stopListening does
not cause Echo.connectionLost to be called.  Shutting down a port only
prevents new connections from being made to it, it does not disconnect
any existing connections.  Even if it did, it would be a bug if it gave
duplication connection lost notifications to any protocol. ;)

>My question therefore is how is it possible to cleanly shut down a  server 
>when a client connection is lost?

You might also consider disabling the port when the connection is /made/.
This reduces the size of the window available for a second connection to
be made, and as I mentioned above, has no affect on the already-established
connection.

Jean-Paul




More information about the Twisted-Python mailing list