Search code examples
pythontwisted

Does a generator decorator exist?


I just got through tracking down a random bug while using Twisted:

Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/twisted/spread/pb.py", line 826, in proto_message
    self._recvMessage(self.localObjectForID, requestID, objectID, message, answerRequired, netArgs, netKw)
  File "/usr/lib/python2.7/dist-packages/twisted/spread/pb.py", line 840, in _recvMessage
    netResult = object.remoteMessageReceived(self, message, netArgs, netKw)
  File "/usr/lib/python2.7/dist-packages/twisted/spread/flavors.py", line 114, in remoteMessageReceived
    state = method(*args, **kw)
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 1141, in unwindGenerator
    return _inlineCallbacks(None, f(*args, **kwargs), Deferred())
--- <exception caught here> ---
  File "/usr/lib/python2.7/dist-packages/twisted/internet/defer.py", line 1020, in _inlineCallbacks
    result = g.send(result)
exceptions.AttributeError: 'NoneType' object has no attribute 'send'

This was caused by:

@defer.inlineCallbacks
def myfunc():
    # Function implementation with no yield statement.

And when the myfunc was called, I would get the previous traceback printed but with everything within the function working properly. This is because it returned None instead a generator when called, which defer.inlineCallbacks expects to be returned. Is there way to declare a function a generator without placing a yield statement within the function body? Such as a generator decorator?


Solution

  • As others have noted, this likely makes no sense. But for completeness and to answer the question:

    No, you have to use a yield to make it a generator, except of course if you create a decorator that's a generator (by including the yield keyword) itself but doesn't actually yield anything and just calls the decorated function. Such a yield can be unreachable and pointless (if False: yield), but it has to be there. As this isn't a common need, there is nothing like the latter pre-built, at least nothing I'm aware of. The by far simplest way is to just add this to your function, writing your own decorator isn't worth it for a few cases and should you need it frequently, there's probably something wrong without your design and you should fix that instead.