Welcome, guest | Sign In | My Account | Store | Cart

This small piece of code helps separating the "public" and "private" interface parts of deferred objects in Twisted.

Although it might not be really useful in production code, it is great for newcomers who are still learning the framework.

Python, 46 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
'''
@author: Alan Franzoni <public@MYSURNAME.eu>
'''
from twisted.internet.defer import Deferred

class ShouldntBecalledError(Exception):
    pass
     
class PublicDeferred(Deferred):
    """
    PublicDeferred decorator.

    Prevents calling methods which should be just be called by the "real"
    deferred object owner. If such calls occur, an error is immediately raised.
    """
    
    def __init__(self, original_d):
        self.__dict__ = original_d.__dict__ 
        
    def callback(self, result):
        raise ShouldntBecalledError, "callback() should only be called by the owner!"
    
    def errback(self, failuire):
        raise ShouldntBecalledError, "errback() should only be called by the owner!"
        
if __name__ == '__main__':
    """
    example usage:
    if you don't want somebody to mess up with your internals, do what you need
    with your deferred and wrap it into a PublicDeferred before returning it.
    """
    from twisted.internet import reactor
    from twisted.web.client import getPage
    
    def publicGetPage(*args, **kwargs):
        d = getPage(*args, **kwargs)
        return PublicDeferred(d)
    
    def printResult(result):
        print result
    
    d = publicGetPage("http://www.google.com")
    d.addCallback(printResult)
    reactor.callLater(0, d.callback, "hello, there!")
    
    reactor.run()
    
    
    
    
    

Sometimes it's not easy to explain deferreds to twisted newcomers. One of the difficulties I found is that Deferred instances somewhat mix their public and private interface; twisted docs say that you didn't create a deferred, do not under any circumstances call its callback or errback.

http://twistedmatrix.com/documents/current/core/howto/deferredindepth.html

By the way, callback() and errback() are just two publicly accessible methods of deferred objects; it may not be obvious at first sight why and when you should or shouldn't call them.

Also, if someone wrongly calls a callback() method with a proper value, but the deferred object takes quite a lot of time to be called by the 'real' owner, the problem may become hard to spot.

A better way to solve a problem would be to split the deferred idea in two parts: when creating a deferred you should get a sort of "handler", which lets callback() and errback() to be invoked, and a public object, which offers the usual addCallback, addCallbacks and addErrback interface; the former is retained by the object who as created the deferred, the latter is returned to the caller.

But that's not easy to do in Twisted right now, since there're some isinstance(something, Deferred) checks throught the code.

A quick solution is here. Just wrap the deferred into a PublicDeferred object before returning to the caller, but retain the reference to the original deferred for callbacking/errbacking purposes. If anyone tries calling errback() or callback() on a PublicDeferred, an error will be raised immediately.

this has not been tested extensively . I'll publish some unit tests in a separate package. Let me know if you encounter any issue.