[Twisted-Python] The Trial of the DirtyReactorError
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi All, I have to say that I am largely getting along famously with Twisted (and Python) now. I have started writing unit tests for my application and when I run them using trial I sometimes get DirtyReactorErrors ('reactor left in unclean state'). This is a nice error. It tells me that I am not shutting down resources correctly which would otherwise be left hanging around and be difficult to debug. I understand why this is occurring, its a simple matter of calling tcp.Port.stopListening() to suppress the error. However, I am more concerned with learning about where the most appropriate place to run this routine would be. I am not happy about running it in the unit test as it doesn't solve the problem, it just suppresses it. I can't seem to find an easy way to stop a port from listening from within a ServerFactory. Should I be writing a class that wraps the ServerFactory to run tcp.Port.stopListening() at the appropriate time? Please bear in mind that I am creating multiple 'one-shot' servers that are always shut down as soon as they have done their business. I am guessing that the stopService methods would be more appropriate for a conventional server that serves multiple clients / requests. Your thoughts? Thanks a lot for your time :) Matt m a t t h e w g l u b b ________________________________________________________________________ Z Group PLC Tel: +44 (0) 8700 111 173 Fax: +44 (0) 8707 051 393 Txt: +44 (0) 7800 140 877 Web: <http://www.zgroupplc.com/> This email and any files transmitted with it are confidential and intended solely for the use of the individual or entity to whom they are addressed. The opinions expressed in this mail are those of the author and do not necessarily represent the views of the company. If you have received this email in error please notify <service@zgroupplc.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Darwin) iD8DBQFGHmQKyI6MkdKPngkRAlkwAJ4zebpjOsfItDtqIaRJbj0kyN7jhACdHvYC 17YRmuZNefQfCxCesMStUWI= =dh4Z -----END PGP SIGNATURE-----
On 04:53 pm, matt@zgroupplc.com wrote:
I understand why this is occurring, its a simple matter of calling tcp.Port.stopListening() to suppress the error. However, I am more concerned with learning about where the most appropriate place to run this routine would be. I am not happy about running it in the unit test as it doesn't solve the problem, it just suppresses it.
Putting the stopListening call into the middle of the actual test method would definitely be wrong. However, putting the listenTCP in the setUp and the stopListening in the tearDown would be entirely appropriate. If the code under test dynamically calls stopListening and might fail before then, then a tearDown by itself might be appropriate.
I can't seem to find an easy way to stop a port from listening from within a ServerFactory. Should I be writing a class that wraps the ServerFactory to run tcp.Port.stopListening() at the appropriate time?
This is, in part, a weakness of the IProtocolFactory interface. Calls to doStart and doStop should really receive an IListeningPort argument. However, this is a minor wart. listenTCP returns the Port, and hooking this up to your server factory in application code should be easy enough.
Please bear in mind that I am creating multiple 'one-shot' servers that are always shut down as soon as they have done their business. I am guessing that the stopService methods would be more appropriate for a conventional server that serves multiple clients / requests.
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.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Thanks for your advice, glyph. I'll probably end up calling listenTCP from within the factory and shutting it down on call or errback. Regards, Matt On 12 Apr 2007, at 19:56, glyph@divmod.com wrote:
On 04:53 pm, matt@zgroupplc.com wrote:
I understand why this is occurring, its a simple matter of calling tcp.Port.stopListening() to suppress the error. However, I am more concerned with learning about where the most appropriate place to run this routine would be. I am not happy about running it in the unit test as it doesn't solve the problem, it just suppresses it.
Putting the stopListening call into the middle of the actual test method would definitely be wrong.
However, putting the listenTCP in the setUp and the stopListening in the tearDown would be entirely appropriate. If the code under test dynamically calls stopListening and might fail before then, then a tearDown by itself might be appropriate.
I can't seem to find an easy way to stop a port from listening from within a ServerFactory. Should I be writing a class that wraps the ServerFactory to run tcp.Port.stopListening() at the appropriate time?
This is, in part, a weakness of the IProtocolFactory interface. Calls to doStart and doStop should really receive an IListeningPort argument.
However, this is a minor wart. listenTCP returns the Port, and hooking this up to your server factory in application code should be easy enough.
Please bear in mind that I am creating multiple 'one-shot' servers that are always shut down as soon as they have done their business. I am guessing that the stopService methods would be more appropriate for a conventional server that serves multiple clients / requests.
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. _______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
m a t t h e w g l u b b ________________________________________________________________________ Z Group PLC Tel: +44 (0) 8700 111 173 Fax: +44 (0) 8707 051 393 Txt: +44 (0) 7800 140 877 Web: <http://www.zgroupplc.com/> This email and any files transmitted with it are confidential and intended solely for the use of the individual or entity to whom they are addressed. The opinions expressed in this mail are those of the author and do not necessarily represent the views of the company. If you have received this email in error please notify <service@zgroupplc.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Darwin) iD8DBQFGHy8UyI6MkdKPngkRAlH6AJ9bKzXeD2resG9USuAnWwCvoEaVEgCePCDy rGROSFHTjmkQgBDHdDQ09o0= =k9Gq -----END PGP SIGNATURE-----
-----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@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 My question therefore is how is it possible to cleanly shut down a server when a client connection is lost? Apologies if I am being a complete idiot about this. I am still a relatively twisted newbie ;) Regards, Matt m a t t h e w g l u b b ________________________________________________________________________ Z Group PLC Tel: +44 (0) 8700 111 173 Fax: +44 (0) 8707 051 393 Txt: +44 (0) 7800 140 877 Web: <http://www.zgroupplc.com/> This email and any files transmitted with it are confidential and intended solely for the use of the individual or entity to whom they are addressed. The opinions expressed in this mail are those of the author and do not necessarily represent the views of the company. If you have received this email in error please notify <service@zgroupplc.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Darwin) iD8DBQFGH0ozyI6MkdKPngkRAk0xAJ0ZmZk6FsRAkrzP2WJYy4lrIOvJiACeJSwP AEAEWw/w2vexi4AHOqdba4M= =NSmv -----END PGP SIGNATURE-----
On Fri, 13 Apr 2007 10:15:28 +0100, Matthew Glubb <matt@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@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
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hi Jean-Paul, On 13 Apr 2007, at 16:22, Jean-Paul Calderone wrote:
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.
Ah. That's handy to know. Thanks.
Also, Port.stopListening does not cause Echo.connectionLost to be called. Shutting down a port only
Yes, I have realised this now. I was getting confused with another event, and thought connectionLost was getting called as a result of port shutdown. tcp.Port.stopListening() now works fine from within the ServerFactory.
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.
I have actually implemented the ListenOverflowProtocol in the server. This seems to do what it says on the tin! Thanks for you help and patience :) Matt m a t t h e w g l u b b ________________________________________________________________________ Z Group PLC Tel: +44 (0) 8700 111 173 Fax: +44 (0) 8707 051 393 Txt: +44 (0) 7800 140 877 Web: <http://www.zgroupplc.com/> This email and any files transmitted with it are confidential and intended solely for the use of the individual or entity to whom they are addressed. The opinions expressed in this mail are those of the author and do not necessarily represent the views of the company. If you have received this email in error please notify <service@zgroupplc.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Darwin) iD8DBQFGH6nWyI6MkdKPngkRAnoqAKCm2tZzlNn53dhXTM9ivBcUk2B1qACfdY6W Dg2YtME3powiuyEoXACvXu0= =XM/y -----END PGP SIGNATURE-----
On Thu, 2007-04-12 at 17:53 +0100, Matthew Glubb wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
Hi All,
I have to say that I am largely getting along famously with Twisted (and Python) now.
I have started writing unit tests for my application and when I run them using trial I sometimes get DirtyReactorErrors ('reactor left in unclean state'). This is a nice error. It tells me that I am not shutting down resources correctly which would otherwise be left hanging around and be difficult to debug.
From memory, the start() will kindof automatically get called when you set up the object, but close() will not. If you don't explicitly call close(), trial will 'hang' because it's actually waiting for the
On this note, I have a little tip to anyone new to writing Trial tests, as I am: If you use adbapi to talk to a database, be aware that it uses a threadpool in order to make something that is synchronous into something asynchronous. In an ordinary program, the threadpool is started and shut down for you by the reactor, but it probably won't be in your tests because of the way the reactor works with trial. I am using a twisted.enterprise.adbapi.ConnectionPool, for example. What you need is to call ConnectionPool.start() from within your unittest setUp(), and to call ConnectionPool.close() in tearDown(). This explicitly starts and stops the threadpool at the appropriate times. threadpool to exit, which never happens. You don't get a 'reactor left in unclean state' error. I hope that saves someone the couple of hours of head scratching and reading code that I went through. -- Justin Warren <daedalus@eigenmagic.com>
participants (4)
-
glyph@divmod.com -
Jean-Paul Calderone -
Justin Warren -
Matthew Glubb