David Y.
—After I add the async
keyword to a FastAPI function, calling the associated route produces the following error:
[TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
Here’s a simplified version of my application code:
from fastapi import FastAPI import asyncio import job # internal module app = FastAPI() @app.get("/run-job") @login_required async def run_job(): result = await job.run() return {"message": result }
I need to run the asynchronous method job.run
in this endpoint’s code, so the function itself has to be asynchronous. What is causing this error and how can I fix it?
The most likely cause of this error is the decorator @login_required
, which we can assume returns a synchronous function. Even though run_job
is asynchronous, the actual function that gets executed after the decorator is applied will be synchronous, leading to an error when await
is called.
To fix this code, we will either need to alter login_required
so that it returns an asynchronous function or create another decoration (e.g. login_required_async
) that does so. For example:
def login_required_async(f): async def wrapper(*args, **kwargs): # ... access control logic goes here ... return await f(*args, **kwargs) return wrapper
Depending on how the rest of our application functions, we may require both synchronous and asynchronous versions of @login_required
. We can create one function for both using a conditional return that checks whether the passed-in function is synchronous or asynchronous:
from inspect import iscoroutinefunction from functools import wraps def login_required(decorator_option=None): def access_control(*args, **kwargs): # ... access control logic goes here ... @wraps(f) def wrapper(*args, **kwargs): access_control(*args, **kwargs) return f(*args, **kwargs) @wraps(f) async def async_wrapper(*args, **kwargs): access_control(*args, **kwargs) return await f(*args, **kwargs) return async_wrapper if iscoroutinefunction(f) else wrapper
iscoroutinefunction
from Python’s built-in inspect
module will return True
if the passed-in function is asynchronous and False
if it is synchronous. This will allow us to use the @login_required
decorator on synchronous and asynchronous functions alike.
Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.
SEE EPISODESConsidered “not bad” by 4 million developers and more than 100,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.
Here’s a quick look at how Sentry handles your personal information (PII).
×We collect PII about people browsing our website, users of the Sentry service, prospective customers, and people who otherwise interact with us.
What if my PII is included in data sent to Sentry by a Sentry customer (e.g., someone using Sentry to monitor their app)? In this case you have to contact the Sentry customer (e.g., the maker of the app). We do not control the data that is sent to us through the Sentry service for the purposes of application monitoring.
Am I included?We may disclose your PII to the following type of recipients:
You may have the following rights related to your PII:
If you have any questions or concerns about your privacy at Sentry, please email us at compliance@sentry.io.
If you are a California resident, see our Supplemental notice.