I am trying to create a simple monitoring system that periodically checks things and logs them. Here is a cutdown example of the logic I am attempting to use but I keep getting a RuntimeWarning: coroutine 'foo' was never awaited error. 
How should I reschedule an async method from itself?
Code in test.py:
import asyncio
from datetime import datetime
async def collect_data():
    await asyncio.sleep(1)
    return {"some_data": 1,}
async def foo(loop):
    results = await collect_data()
    # Log the results
    print("{}: {}".format(datetime.now(), results))
    # schedule to run again in X seconds
    loop.call_later(5, foo, loop)
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.create_task(foo(loop))
    loop.run_forever()
    loop.close()
Error:
pi@raspberrypi [0] $ python test.py 
2018-01-03 01:59:22.924871: {'some_data': 1}
/usr/lib/python3.5/asyncio/events.py:126: RuntimeWarning: coroutine 'foo' was never awaited
  self._callback(*self._args)
                call_later accepts a plain sync callback (a function defined with def). A coroutine function (async def) should be awaited to be executed.
The cool thing about asyncio is that it imitates imperative plain synchronous code in many ways. How would you solve this task for a plain function? I guess just sleep some time and recursively call function again. Do the same (almost - we should use synchronous sleep) with asyncio also:
import asyncio
from datetime import datetime
async def collect_data():
    await asyncio.sleep(1)
    return {"some_data": 1,}
async def foo(loop):
    results = await collect_data()
    # Log the results
    print("{}: {}".format(datetime.now(), results))
    # Schedule to run again in X seconds
    await asyncio.sleep(5)
    return (await foo(loop))
if __name__ ==  '__main__':
    loop = asyncio.get_event_loop()
    try:
        loop.run_until_complete(foo(loop))
    finally:
        loop.run_until_complete(loop.shutdown_asyncgens())  # Python 3.6 only
        loop.close()
If you sometime would need to run foo in the background alongside with other coroutines you can create a task. There is also shown a way to cancel task execution.
Update:
As Andrew pointed out, a plain loop is even better:
async def foo(loop):
    while True:
        results = await collect_data()
        # Log the results
        print("{}: {}".format(datetime.now(), results))
        # Wait before next iteration:
        await asyncio.sleep(5)
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With