pythonasynchronouspycharmpython-asynciocoroutine

Python Warning: Coroutine 'create_task' is not awaited


I am using Python and PyCharm, I am trying to offload database writing using asyncio but am having trouble with IDE warnings

In my main.py I start up tasks in this manner

db_logging_monitoring_task = asyncio.create_task(start_logging_jobs_sniffer())
tasks.append(db_logging_monitoring_task)
await gather(*tasks)

where start_logging_jobs_sniffer is a while loop inside of an async function

# Waits on jobs from a jobs_queue
async def start_logging_jobs_sniffer(self):
    while True:
        try:
            job = await self.logging_jobs_queue.get()


# Adds jobs to a jobs_queue
async def make_logging_job(self, params):
    job = self.LoggingJob(params)
    await self.logging_jobs_queue.put(job)

similarly in main.py I have another background monitoring task that does polling on a 5 second interval and then logs the polling results

async def poll(self, interval: int):
    if self.is_config_applied:
        while True:
            # ...

            asyncio.create_task(make_logging_job(
                params={
                    **self.polling_results
                },
            ))

but pycharm throws a warning at me

Coroutine 'create_task' is not awaited 

I specifically want make_logging_job tasks to work in parallel with other tasks which is why I am not using await, and this seems to work as expected.

If I run things with the warning present the make_logging_job seems to be in parallel and is not blocking anything

Pretty much I don't understand why PyCharm is throwing a warning that the create_task is not awaited, is there some other better way of making the tasks?

Saving the task to a variable suppresses the error and seems to function by running things in the background

            test = asyncio.create_task(make_logging_job(
                params={
                    **self.polling_results
                },
            ))

but i have no idea why this helps, saving the task to a variable doesn't wait on the coroutine i assume


Solution

  • When you create an asyncio task, you can´t just "fire and forget" it - you have to keep a reference to it somewhere - and eventually check if it is done, so you can discard your references.

    As you found out in the comments, attributing the task to a local variable is needed - but unlike your IDE reports, not enough - as in just attributing a variable and discarding it, you'd have no reference of the task.

    Instead, use a container such as a set() to store the tasks, and spawn another task which checks if they are complete, maybe using an asyncio.wait call:

    import asyncio
    
    all_jobs = set()
    
    async def poll(self, interval: int):
        if self.is_config_applied:
            while True:
                # ...
                
                all_jobs.add(asyncio.create_task(make_logging_job(
                    params={
                        **self.polling_results
                    },
                )))
    
    async def task_waiter():
        while True:
            pending, done = await asyncio.wait(all_jobs, timeout=10) # collect once each 10 seconds - change at your will
            all_jobs.clear()
            all_jobs.update(pending)
    
    
    # and wherever in your code, where you call `poll`, instead of
    # just `await poll` do:
    
    async def main(...):
        ...
        # await poll(...)  #old code, commented out
        poll_task = asyncio.create_task(poll(...))
        waiter_task = asyncio.create_task(task_waiter())
        await asyncio.gather(poll_task, waiter_task)