Fastapi: cythonize fastapi code to *.so file encountered ValueError: [TypeError("'coroutine' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]

Created on 19 Aug 2020  路  6Comments  路  Source: tiangolo/fastapi

Description

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')]

Server Code

Here is a simple server code

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_root():
    return {"Hello": "World"}

Setup Code

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

Environment

  • OS: macOS
  • Python Version: 3.7.8
  • FastAPI Version: 0.61.0
  • Cython Version: 0.29.21
question

Most helpful comment

Same here. (Python 3.6, CentOS7)

All 6 comments

@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)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

updatatoday picture updatatoday  路  3Comments

laith43d picture laith43d  路  3Comments

scheung38 picture scheung38  路  3Comments

iwoloschin picture iwoloschin  路  3Comments

kkinder picture kkinder  路  3Comments