1

In the following, the coroutinerunIt() is created and provided as a parameter to delegate(...) - which is turned into a Task that is canceled before runIt executes:

import asyncio

async def cancelTaskTest():
    async def runIt():
        print("RunIt ran")

    async def delegate(coro):
        await coro

    task = asyncio.create_task(delegate(runIt()))
    task.cancel()

if __name__=='__main__':
    asyncio.run(cancelTaskTest())

Produces the unwanted warning:

/usr/lib/python3.10/asyncio/base_events.py:1881: RuntimeWarning: coroutine 'cancelTaskTest.<locals>.runIt' was never awaited
  handle = self._ready.popleft()
RuntimeWarning: Enable tracemalloc to get the object allocation traceback

I'm aware that runIt did not run. I don't want a warning about it - what's the best way to avoid this.

user48956
  • 14,850
  • 19
  • 93
  • 154

1 Answers1

2

Simplest method would be to remove () in runIt and call it in runAfterTimeout():

import asyncio


async def cancelTaskTest():
    async def runIt():
        print("RunIt ran")

    async def delegate(asyncFunc):
        coro = asyncFunc() # <-- put () here
        await coro  

    task = asyncio.create_task(delegate(runIt))  # <-- removed () in runIt
    task.cancel()


if __name__ == "__main__":
    asyncio.run(cancelTaskTest())

EDIT: To add parameters to RunIt, just create plain lambda::

import asyncio


async def cancelTaskTest():
    async def runIt(p1, p2):
        print(f"RunIt({p1}, {p2})")

    async def delegate(coro):
        await coro()

    task = asyncio.create_task(delegate(lambda: runIt(1, 2)))
    task.cancel()


if __name__ == "__main__":
    asyncio.run(cancelTaskTest())
user48956
  • 14,850
  • 19
  • 93
  • 154
Andrej Kesely
  • 168,389
  • 15
  • 48
  • 91
  • Yeah -- I've condered it, however, passing around coro is sometimes important. In my actual use case, I catch and log exceptions in a drop-in replacement for create_task. create_task task task a coroutine, and we should except many other method to do so also. – user48956 Aug 04 '22 at 20:50
  • Plus, if your function has paramters, the lack of python's async lambda mean you also have to resort to creating parameterless async function. It doesn't even have an async version of `partial`: https://stackoverflow.com/questions/52422860/partial-asynchronous-functions-are-not-detected-as-asynchronous Also wrapping functions like this hides the function name in stack traces and debugging. Yuk! – user48956 Aug 04 '22 at 20:56
  • 1
    @user48956 I cannot answer the first comment, but for the second one, just create plain `lambda:` (I've updated the answer). – Andrej Kesely Aug 04 '22 at 20:58
  • Pretty good. But mysteriously, those lambdas give false when passed to asyncio.iscouroutinefunction - which is a sad situation for decorators wanting to work for both functions and coroutine functions. https://stackoverflow.com/questions/73243313/how-can-i-know-if-a-lambda-is-an-coroutine-function – user48956 Aug 04 '22 at 23:56