Common Gotchas

Deferreds can occasionally throw up some surprises by exhibiting behaviours that are not bugs, but are also likely to be different than the intended outcome the user was hoping to achieve. Because a lot of these mistakes are caught by unit tests, the examples provided in this section will not occur within a unit test, but instead within a typical reactor loop.

yield not returning anything

Inside of an @inlineCallback wrapper, the yield keyword can be used to tell the program to wait for a Deferred or anything that returns a Deferred, to resolve before continuing. If you forget to add the wrapper, however, a generator object will be returned instead.

example

In our example below, we assign the variable username to the return of yieldWithoutInlineCallbacks():, which does not return the name we were hoping for, but instead returns a generator object:

from twisted.internet import reactor, defer


def yieldWithoutInlineCallbacks():

    # We create a Deferred
    d = defer.Deferred()

    # Pretend to enter a name
    d.callback("Jimmy")

    # Accidentally call yield without @inlineCallbacks
    yield d
    return d


if __name__ == "__main__":

    # This function will return a generator object instead of a yielded Deferred, and we never noticed.
    username = yieldWithoutInlineCallbacks()

    # Some time later...
    print("Hello,", username)
    reactor.run()

When run, the above code block outputs something like:

Hello, <generator object yieldWithoutInlineCallbacks at 0x10977e1b0>

Probably not what we had in mind…

Not waiting for a function that returns a Deferred

Because Deferreds are not generally intended to be waited for, instead using callbacks to continue whenever they are ready, at some points a user may accidentally forget to wait for a Deferred when the program is not intended to continue until the Deferred has been called. This can result in strange behavior.

example

Here we create a Deferred that is intended to get a username. We add getUsername to it as a callback, but we forget to wait for it to come back (in this example we don’t call it at all). Because of this, it is assigned to the variable username, which is then printed, leading odd output:

from twisted.internet import reactor, defer


def returnUncalledDeferred() -> defer.Deferred:

    def getUsername():

        # The callback that is never called
        return "Jimmy"

    # We create a Deferred
    d = defer.Deferred()

    # Add a callback that will get a name
    d.addCallback(getUsername)

    # Accidentally return the Deferred before called its callback
    return d


if __name__ == "__main__":

    # This function will return a Deferred object instead of a username
    username = returnUncalledDeferred()
    print("Hello,", username)
    reactor.run()

Which results in an eventual output of:

Hello, <Deferred at 0x10671c0f0>

Because the Deferred did not raise an exception, and does not list the place that the Deferred originated like in the yield example, this can be a more difficult kind of mistake to track down.