Trying to use Cython to compile the server code server.py to server.so, and run the server with the server.so file. But if async is used, the server raise following error when requested:
Traceback (most recent call last):
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/uvicorn/protocols/http/httptools_impl.py", line 385, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
return await self.app(scope, receive, send)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/fastapi/applications.py", line 179, in __call__
await super().__call__(scope, receive, send)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/applications.py", line 111, in __call__
await self.middleware_stack(scope, receive, send)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/middleware/errors.py", line 181, in __call__
raise exc from None
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__
await self.app(scope, receive, _send)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/exceptions.py", line 82, in __call__
raise exc from None
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__
await self.app(scope, receive, sender)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/routing.py", line 566, in __call__
await route.handle(scope, receive, send)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/routing.py", line 227, in handle
await self.app(scope, receive, send)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/starlette/routing.py", line 41, in app
response = await func(request)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/fastapi/routing.py", line 199, in app
is_coroutine=is_coroutine,
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/fastapi/routing.py", line 122, in serialize_response
return jsonable_encoder(response_content)
File "/Users/chuito/anaconda3/envs/fastapi/lib/python3.7/site-packages/fastapi/encoders.py", line 128, in jsonable_encoder
raise ValueError(errors)
ValueError: [TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
Here is a simple server code
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"Hello": "World"}
Here is the script to compile the server code to a *.so file
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize(module_list=["server.py"],
compiler_directives=dict(c_string_encoding="utf-8",
language_level=3,
)
)
)
Run python build.py build_ext
@ChuitoChan Did you manage to fix the error ?
Same here. (Python 3.6, CentOS7)
Yeah, I'd say this needs to be reopened, because I'm encountering it too.
My route looks like this, where I make an API request to validate a user based on their basic credentials:
@analytics_extension.get('/authenticate')
async def simple_authentication_endpoint(
credentials: HTTPBasicCredentials = Depends(security),
) -> JSONResponse:
await authenticate_user(URL, credentials)
return JSONResponse(content=config)
but the resulting error is the same as above due to the cythonization we use in production.
A workaround I found is to modify the get_request_hanlder function in routing.py of fastapi
def get_request_handler(
. . . . . .
) -> Callable:
assert dependant.call is not None, "dependant.call must be a function"
is_coroutine = asyncio.iscoroutinefunction(dependant.call)
is_body_form = body_field and isinstance(body_field.field_info, params.Form)
async def app(request: Request) -> Response:
. . . . . .
if errors:
raise RequestValidationError(errors, body=body)
else:
raw_response = await run_endpoint_function(
dependant=dependant, values=values, is_coroutine=is_coroutine
)
####### Insert here #######
if asyncio.iscoroutine(raw_response):
raw_response = await raw_response
###########################
if isinstance(raw_response, Response):
if raw_response.background is None:
raw_response.background = background_tasks
return raw_response
. . . . . .
return response
return app
and modify the startup function in routing.py of starlette if async startup is used
async def startup(self) -> None:
"""
Run any `.on_startup` event handlers.
"""
for handler in self.on_startup:
####### Insert here #######
if handler.__class__.__name__ == "cython_function_or_method":
handler = asyncio.coroutine(handler)
###########################
if asyncio.iscoroutinefunction(handler):
await handler()
else:
handler()
^^ @ChuitoChan that's a nice fix! Sadly, I'm in a production environment, and I can't get that done as easily. Seems like a simple enough addition to the package, though.
I think this will be fixed in a newer version of Cython: https://github.com/cython/cython/blob/master/CHANGES.rst#300-alpha-7-2020-0- (https://github.com/cython/cython/pull/3427)
Most helpful comment
Same here. (Python 3.6, CentOS7)