Reusing pending objects in Twisted

In Twisted, it seems that a deferred object can only be used once after its callback, unlike other "promising" libraries that I worked with:

from twisted.internet import defer


class Foo(object):

    def __init__(self):
        self.dfd = defer.Deferred()

    def async(self):
        return self.dfd

    @defer.inlineCallbacks
    def func(self):
        print 'Started!'

        result = yield self.async()

        print 'Stopped with result: {0}'.format(result)

if __name__ == '__main__':
    foo = Foo()
    foo.func()

    import time
    time.sleep(3)

    foo.dfd.callback('3 seconds passed!')

    foo.func()

In the standard version:

$ Started!
$ Stopped with result: 3 seconds passed!
$ Started!
$ Stopped with result: None

In my situation, I expect to funcbe called again and again in the stream reactor. Is there a way to ensure that the call yieldwill always return the “allowed” value of the pending object without introducing an extra state, and if so, what is the most elegant way to do this?

UPDATE

Based on the advice below, I implemented the solution as a decorator:

import functools


def recycles_deferred(deferred_getter):
    """Given a callable deferred_getter that returns a deferred object, create
    another function that returns a 'reusable' version of that deferred object."""
    @functools.wraps(deferred_getter)
    def _recycler(*args, **kwargs):
        old_dfd = deferred_getter(*args, **kwargs)
        new_dfd = defer.Deferred()

        def _recycle(result):
            new_dfd.callback(result)
            return result

        old_dfd.addCallback(_recycle)

        return new_dfd

    return _recycler



if __name__ == '__main__':
    """Demonstration of how this @recycles_deferred should be used."""
    import time

    from twisted.internet import defer

    class O(object):

        def __init__(self):
            """In practice this could representation a network request."""
            self.dfd = defer.Deferred()

        def do_something_with_result(self, result):
            print 'Got result: {0}'.format(result)
            return result

        @recycles_deferred
        def deferred_getter(self):
            """Return the deferred."""
            return self.dfd

        @defer.inlineCallbacks
        def do_something_with_deferred(self):
            result = yield self.deferred_getter()

            print 'Got inline result: {0}'.format(result)


    o = O()

    o.dfd.addCallback(o.do_something_with_result) # Got result: foo
    o.do_something_with_deferred()                # Got inline result: foo
    o.dfd.addCallback(o.do_something_with_result) # Got result: foo

    # sleep 3 seconds, then resolve the deferred
    time.sleep(3)
    o.dfd.callback('foo')

    o.do_something_with_deferred()                # Got inline result: foo
    o.dfd.addCallback(o.do_something_with_result) # Got result: foo

    # the inline call to yield never returns None
    o.do_something_with_deferred() # Got inline result: foo
    o.do_something_with_deferred() # Got inline result: foo
    o.do_something_with_deferred() # Got inline result: foo
+1
1

, Deferred - , , , . , , , Deferred .

, yield ing a Deferred inlineCallbacks "" a Deferred - - , ( Deferred , ), , yield, None. , - " inlineCallbacks", , 20/20: -).

, Deferred , API, Deferred, Deferred . , , , . : API, Deferred, , , JSON, .addCallback(json.loads), , JSON- , .

, async , , , :

from __future__ import print_function, unicode_literals

from twisted.internet import defer

class Foo(object):

    def __init__(self):
        self.dfd = defer.Deferred()

    def async(self):
        justForThisCall = defer.Deferred()
        def callbackForDFD(result):
            justForThisCall.callback(result)
            return result
        self.dfd.addCallback(callbackForDFD)
        return justForThisCall

    @defer.inlineCallbacks
    def func(self):
        print('Started!')
        result = yield self.async()
        print('Stopped with result: {0}'.format(result))

if __name__ == '__main__':
    foo = Foo()
    print("calling func")
    foo.func()
    print("firing dfd")
    foo.dfd.callback('no need to wait!')
    print("calling func again")
    foo.func()
    print("done")

:

calling func
Started!
firing dfd
Stopped with result: no need to wait!
calling func again
Started!
Stopped with result: no need to wait!
done
+2

All Articles