Search code examples
pythontwistedtwisted.internet

Stopping task.LoopingCall if exception occurs


I'm new to Twisted and after finally figuring out how the deferreds work I'm struggling with the tasks. What I want to achieve is to have a script that sends a REST request in a loop, however if at some point it fails I want to stop the loop. Since I'm using callbacks I can't easily catch exceptions and because I don't know how to stop the looping from an errback I'm stuck.

This is the simplified version of my code:

def send_request():
    agent = Agent(reactor)
    req_result = agent.request('GET', some_rest_link)
    req_result.addCallbacks(cp_process_request, cb_process_error)

if __name__ == "__main__":
    list_call = task.LoopingCall(send_request)
    list_call.start(2)
    reactor.run()

Solution

  • To end a task.LoopingCall all you need to do is call the stop on the return object (list_call in your case).

    Somehow you need to make that var available to your errback (cb_process_error) either by pushing it into a class that cb_process_error is in, via some other class used as a pseudo-global or by literally using a global, then you simply call list_call.stop() inside the errback.

    BTW you said:

    Since I'm using callbacks I can't easily catch exceptions

    Thats not really true. The point of an errback to to deal with exceptions, thats one of the things that literally causes it to be called! Check out my previous deferred answer and see if it makes errbacks any clearer.

    The following is a runnable example (... I'm not saying this is the best way to do it, just that it is a way...)

    #!/usr/bin/python
    
    from twisted.internet import task
    from twisted.internet import reactor
    from twisted.internet.defer import Deferred
    from twisted.web.client import Agent
    from pprint import pprint
    
    class LoopingStuff (object):
    
        def cp_process_request(self, return_obj):
            print "In callback"
            pprint (return_obj)
    
        def cb_process_error(self, return_obj):
            print "In Errorback"
            pprint(return_obj)
            self.loopstopper()
    
        def send_request(self):
            agent = Agent(reactor)
            req_result = agent.request('GET', 'http://google.com')
            req_result.addCallbacks(self.cp_process_request, self.cb_process_error)
    
    def main():
        looping_stuff_holder = LoopingStuff()
        list_call = task.LoopingCall(looping_stuff_holder.send_request)
        looping_stuff_holder.loopstopper = list_call.stop
        list_call.start(2)
        reactor.callLater(10, reactor.stop)
        reactor.run()
    
    if __name__ == '__main__':
      main()
    

    Assuming you can get to google.com this will fetch pages for 10 seconds, if you change the second arg of the agent.request to something like http://127.0.0.1:12999 (assuming that port 12999 will give a connection refused) then you'll see 1 errback printout (which will have also shutdown the loopingcall) and have a 10 second wait until the reactor shuts down.