Reawaiting an instance of a coroutine for a periodic Task
mbrunnen opened this issue · 3 comments
My idea was to create a periodic task, inherited from the asyncio.Task, which calls given coroutine periodically. A minimal working example follows here:
#!/usr/bin/env python
import asyncio
class CyclicTask(asyncio.Task):
def __init__(self, wrapped_coro, loop):
super(CyclicTask, self).__init__(wrapped_coro, loop=loop)
@staticmethod
async def run(loop, coro):
while True:
loop.set_task_factory(None)
await loop.create_task(coro)
@staticmethod
def create(loop, func):
return CyclicTask(CyclicTask.run(loop, func), loop)
async def main(loop):
async def my_func():
await asyncio.sleep(1)
print('Hello!')
loop.set_task_factory(CyclicTask.create)
cyclic = loop.create_task(my_func())
await cyclic
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
But then I get this error, because I'm reawaiting the same instance of a coroutine as described here:
RuntimeError: cannot reuse already awaited coroutine
Probably there is an elegant way to avoid this problem, I am pretty new to this...
Hi,
I'm not sure you can do what you want this way.
When you invoke a coroutine function, it returns an object which is the coroutine, which can be seen as a "reference" to a function execution context which is paused when "await" is reached. By design, a coroutine is not meant to be rewound and replayed, rather, you want to get a new instance of the coroutine function.
You use a task to control how the coroutine instance is executed on the loop, a bit like a process executes an instance of a program, with its own attributes.
Also, changing the task_factory of the loop will have side effects you don't expect. Until CyclicTask.run() is executed by the loop (and this will not happen in create() but later, asynchronously), any created task will be a CyclicTask. This is likely to break the internals of asyncio, as it may schedule tasks which are not meant to be executed again.
You probably don't need to extend Task, but maybe a coroutine function helper will be closer to what you want to do:
async def repeat_forever(coroutine, *args, **kwargs):
while True:
await coroutine(*args, **kwargs)
task = loop.create_task(repeat_forever(my_func)))
loop.run_until_complete(task)
or even:
loop.run_until_complete(repeat_forever(my_func))
If you want more help, this bug tracker may not be the best place, the mailing list (https://groups.google.com/forum/?fromgroups#!forum/python-tulip) seems more appropriate.
I think the issue can be closed.
Let's close it then.