Reputation: 2265
I have some hooks in place, and I thought I could decorate them with @ndb.tasklet in order to use async apis inside the hooks. e.g.
@classmethod
@ndb.tasklet
def _post_delete_hook(cls, key,future):
yield do_something_async()
This seemed to work, but every now and then I see "suspended generator" error for the code inside those hooks.
Should I be using @ndb.synctasklet instead?
An example of error:
suspended generator _post_put_hook(data_field.py:112) raised TypeError(Expected Future, received <class 'google.appengine.api.apiproxy_stub_map.UserRPC'>: <google.appengine.api.apiproxy_stub_map.UserRPC object at 0x09AA00B0>)
The code causing the error occasionally was:
t, d = yield (queue.add_async(task), queue.delete_tasks_async(taskqueue.Task(name=existing_task_name)))
Now that I've put @ndb.synctasklet it raises an actual exception.
Upvotes: 2
Views: 206
Reputation: 55630
An ndb tasklet returns a future. If calling the tasklet results in an exception, the exception will only be raised if the future's get_result
method is called.
ndb.synctasklet
automatically calls get_result
on the futures yielded by tasklets, causing exceptions to be raised if they occurred, rather than just logged.
For the error that you are seeing, you may be able to fix it by converting the UserRPCs returned by the taskqueue async methods to tasklets.
This untested code is based on ndb.context.urlfetch
(link), which converts the UserRPC
produced by urlfetch.createRPC
into a Future
.
@ndb.tasklet
def add_async(queue, **taskqueue_kwargs):
rpc = queue.add_async(**taskqueue_kwargs)
result = yield rpc
raise ndb.Return(result)
You would need to create a tasklet for each async method that you want to use, or you could extend the taskqueue class and make the async methods tasklets.
Upvotes: 2