Fatal Errors and Deferreds in Twisted, stop a deferred
Question: I have a problem, with normal try except blocks in python you can just return
if there is a fatal error, ex...

    
    
    try:
        logon()
    except 404_Error:
        retry_logon(try = 2)
    
    except AuthenticationProblem:
        error_label.SetText( "You password or username was wrong. Make sure that CAPS LOCK key is not on." )
        return#nothing more we can do here
    else:
        #display user information or whatever
    

So how do this with deferreds, if I just return it goes onto execute the
callbacks thinking the error is handled but how do I inform the user something
went wrong and destroy the down river chain.

==== Update ===

mg thanks for the help but it didn't work, even with a fatal error the defer
still returns to the callbacks afterwords

    
    
    from twisted.internet import reactor
    from twisted.internet.defer import Deferred as D
    
    class NonFatalError(Exception):
        'non fatal error'
    class FatalError(Exception):
        'fatal error'
    def c(s):
        print "Callback called"
        print "Data Received: %s" % s
    def e(f):
        print "Errorback Called"
        print "Error Type: %s" % type(f)
        print "Traceback"
        f.printTraceback()
        print "======================================="
        f.trap(NonFatalError)
        return "Error Handled"
    def e_fatal(f, d):
        print "Errorback Called"
        print "Error Type: %s" % type(f)
        print "Traceback"
        f.printTraceback()
        print "======================================="
        print "Fatal Error"
        f.trap(FatalError)
        return "Fatal Error... Crash and die. No more callbacks should be called."
    
    def trigger():
        d.errback(FatalError("This error is fatal to the defer"))
    
    if __name__ == "__main__":
        d = D()
        d.addErrback(e)
        d.addErrback(e_fatal, d)
        d.addCallback(c)
        d.addCallback(c)
        d.addCallback(c)
        d.addCallback(c)
        reactor.callLater(3, trigger)
        reactor.callLater(10, reactor.stop)
        reactor.run()
    
        raw_input("Done.")
    


Answer: Ok, a whole new answer to explain better how
[deferreds](http://twistedmatrix.com/documents/11.0.0/api/twisted.internet.defer.Deferred.html)
work. You should think, at least I do, the flow of the program as a state
machine. A success or a failure is like an input of that machine that,
potentially, change the state. In your case you have two states, logged and
non logged, and three inputs: successfully logged in, wrong authentication and
could not logged for server problems. Only one of this inputs is recoverable,
the case the server could not login the user for same strange problem and in
this case you can recover the problem retrying the login. Here the new code:

    
    
    import sys
    from twisted.internet import reactor, defer
    
    
    class FourOhFourError(Exception):
        pass
    
    
    class AuthenticationError(Exception):
        pass
    
    
    def logon(retry=3, success=2, wrong_auth=0):
        # do stuff
        d = defer.Deferred()
        # not_found is the only error recoverable
        d.addErrback(not_found, retry, success)
        if wrong_auth:
            reactor.callLater(0, d.errback, AuthenticationError("wrong auth"))
        else:
            if success == 0:
                reactor.callLater(0, d.callback, "Mario")
            else:
                reactor.callLater(0, d.errback, FourOhFourError("Not found"))
        return d
    
    
    def not_found(failure, retry, success):
        failure.trap(FourOhFourError) # this is superfluous here
        print failure.getErrorMessage()
        if retry == 0:
            raise AuthenticationError("Max retries")
        # do stuff
        print "retring..."
        d = defer.Deferred()
        d.addCallback(logon, success-1)
        reactor.callLater(1, d.callback, retry-1) # not really clean here
        return d
    
    
    def wrong_auth(failure):
        failure.trap(AuthenticationError) # this is superfluous here
        # do stuff
        print "something goes wrong"
        print failure.getErrorMessage()
    
    
    def loggedIn(user):
        print "hello %s" % user
    
    
    def stop(_):
        reactor.stop()
    
    
    d = logon(*map(int, sys.argv[1:]))
    d.addCallbacks(loggedIn, wrong_auth)
    d.addBoth(stop)
    reactor.run()
    

Invoke the code with three parameters: the maximum number of retries, at which
retry the system should login the user and the third is a boolean indicating
the correctness of the user credentials. Try the following invocations: `0 0
1`, `3 2 0`, `3 4 0`.

I hope this example is more explicative.

