[Twisted-Python] LoopingCalls and unclean reactor

Brad Milne brad.milne at devx.runthered.com
Wed Mar 16 17:13:17 EDT 2011


Thanks Drew

Yes, you spotted an error in my email, which is not replicated in my code.
ie My AdapterQueue in the code inherits from Service. It in turn is a
service of a parent which *is* a MultiService instance.

The tests are Integration tests, not unit tests, and as such run up an
instance of our entire messaging gateway. To date we have run it up once at
the start of the test run, run all test suites, then torn down (with unclean
reactor). The Integration tests have been notoriously brittle, so am hoping
that tearing down the gateway after *each* individual test will help with
that. To that end, I have refactored the code to better utilise services, so
that teardown (without just stopping the reactor) is possible.

So in the setUp() of each trial.unittest.TestCase, is a call to
test_utils.startGateway(). This returns the result of
gateway_svc.startService() (starts the parent MultiService of all child
services). Each of these setUp methods uses @inlineCallbacks and yields the
deferred returned from this top-level startService().
@inlineCallbacks
def setUp(self):
   yield test_utils.startGateway()
   {do other set up}

By the same token, the tearDown() in each test class calls
test_utils.tearDown(), which looks like:

class SomeTests(TestCase):
    def tearDown(self):
        self.extra_svc.stopService()
        test_utils.tearDown()

Haha! In writing this I've realised what I believe was the problem (and my
repeat testing just now has failed to show the intermittent failure). Each
test class's tearDown() was *not* *returning* the deferred returned from
stopService(). Changing the final line above seems to solve the issue:

return test_utils.tearDown()

Thanks for the sounding board, Drew.
Cheers
Brad



On 17 March 2011 05:06, Drew Smathers <drew.smathers at gmail.com> wrote:

> On Tue, Mar 15, 2011 at 9:55 PM, Brad Milne
> <brad.milne at devx.runthered.com> wrote:
> > Hi
> > I have a series of MultiService objects, with child Services. Some of
> these
> > services are TCPServers, for example, and others are my own objects
> > (extending from Service).
> > In the instance that I have a Service which controls a LoopingCall, I am
> > getting intermittent 'unclean reactor' errors during tests. I feel I
> might
> > be missing some handling of deferreds, perhaps.
> > (using 8.2.0 - looking to migrate to 10.2.0 soon)
> > Here is an example of the approach being used:
> > class AdapterQueue(service.MultiService):
> >     def startService(self):
> >         service.Service.startService(self)
> >         self._looping_controller = LoopingCall(self._action)
> >         d = self._looping_controller.start(self.delay, False)
> >         d.addErrback(self._errorInScheduler)
> >     def stopService(self):
> >         service.Service.stopService(self)
> >         d = self._looping_controller.deferred
> >         self._looping_controller.stop()
> >         return d
>
>
> I'm not sure if this is part of the issue or not, but it seems odd
> that you may have accidentally inherited from MultiService instead of
> Service.
>
>
> > And an example error (happens about 1/3 of the time):
> > DirtyReactorAggregateError: Reactor was unclean.
> > DelayedCalls: (set twisted.internet.base.DelayedCall.debug = True to
> debug)
> > <DelayedCall 30800112 [0.0189974308014s] called=0 cancelled=0
> > LoopingCall<0.033333333333333333>(AdapterQueue._action, *(), **{})()
> > traceback at creation:
> >   File "C:\Python26\lib\threading.py", line 497, in __bootstrap
> >     self.__bootstrap_inner()
> >       File "C:\Python26\lib\threading.py", line 525, in __bootstrap_inner
> >     self.run()
> >       File "C:\Python26\lib\threading.py", line 477, in run
> >     self.__target(*self.__args, **self.__kwargs)
> >       File "D:\dev\eggs\nose-0.11.3-py2.6.egg\nose\twistedtools.py", line
> > 57, in <lambda>
> >     installSignalHandlers=False))
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\base.py",
> line
> > 1128, in run
> >     self.mainLoop()
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\base.py",
> line
> > 1137, in mainLoop
> >     self.runUntilCurrent()
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\base.py",
> line
> > 757, in runUntilCurrent
> >     call.func(*call.args, **call.kw)
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\task.py",
> line
> > 115, in __call__
> >     d.addCallback(cb)
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\defer.py",
> line
> > 195, in addCallback
> >     callbackKeywords=kw)
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\defer.py",
> line
> > 186, in addCallbacks
> >     self._runCallbacks()
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\defer.py",
> line
> > 328, in _runCallbacks
> >     self.result = callback(self.result, *args, **kw)
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\task.py",
> line
> > 103, in cb
> >     self._reschedule()
> >       File
> > "D:\dev\eggs\twisted-8.2.0-py2.6-win32.egg\twisted\internet\task.py",
> line
> > 139, in _reschedule
> >     self.call = self.clock.callLater(nextTime - currentTime, self)
> >>
> >
>
>
> Can you post code for the test?  This means generally (as you
> suggested) that you haven't waited for all related deferreds to fire
> before ending the test. Return the deferred returned by stopService(),
> for example, and make final assertions in a callback; but I'm guessing
> you already know this.
>
>
> -Drew
>
> _______________________________________________
> Twisted-Python mailing list
> Twisted-Python at twistedmatrix.com
> http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
>



-- 
Brad Milne | Run The Red | *brad.milne at devx.runthered.com*
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://twistedmatrix.com/pipermail/twisted-python/attachments/20110317/89f107e6/attachment.htm 


More information about the Twisted-Python mailing list