I am writing a fastapi app that want to schedule in minutes and hourly, but when I am running this it says:
\base_events.py", line 436, in create_task
task = tasks.Task(coro, loop=self, name=name, context=context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: a coroutine was expected, got None
My code:
import requests
from fastapi_utilities import repeat_at
from fastapi_offline import FastAPIOffline
import asyncio
app = FastAPIOffline()
task = None
@repeat_at(cron="*/1 * * * *") # every 1 minute
async def call_api():
global task
url = "http://sssssss/both/execute_query/4"
try:
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(None, lambda: requests.get(url))
response_json = response.json()
print(response_json)
except requests.exceptions.RequestException as e:
print(f"Error fetching data: {e}")
if task is None:
while True:
#print("Running task...")
await asyncio.sleep(0) # This is a checkpoint where the task can be cancelled
break
return response_json
@app.get("/trigger-call-api")
async def trigger_call_api():
global task
if task is None:
print('*************', call_api(), call_api)
task = asyncio.create_task(call_api())
return {"message": "call_api triggered"}
else:
return {"message": "call_api is already running"}
@app.get("/stop-call-api")
async def stop_call_api():
global task
if task is not None:
#print('me@',dir(task))
task.cancel()
task = None
#print('meXC',task, type(task), task.cancel(), task.cancelled(), task.cancelling())
else:
return {"message": "call_api is not running"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="localhost", port=8001)
I'm figuring out that call_api
is None
and trying to fix it, but no effort becomes.
According to the source code of the @repeat_at
decorator, it does not support that an async def
function is called directly after it has been decorated, because it returns a normal def
function (which will start the original function as a background task and return None
, as you have identified).
You can work around this by adding a wrapper around call_api
which you decorate with @repeat_at
, so that you can keep using the undecorated call_api
normally.
@repeat_at(cron="*/1 * * * *") # every 1 minute
async def call_api_repeat():
await call_api()
async def call_api():
global task
url = "http://sssssss/both/execute_query/4"
try:
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(None, lambda: requests.get(url))
response_json = response.json()
print(response_json)
except requests.exceptions.RequestException as e:
print(f"Error fetching data: {e}")
if task is None:
while True:
#print("Running task...")
await asyncio.sleep(0) # This is a checkpoint where the task can be cancelled
break
return response_json