3

I am using FastAPI to create a websocket endpoint, but I have lots of synchronous application code that I would like to use without creating async wrappers. But since all websocket methods seem to be async, I am trying to wrap them in a sync utility function.

@app.websocket("/listen")
def listen_endpoint(websocket: WebSocket):
    run_sync(websocket.accept())
    run_sync(websocket.receive_json())
    run_sync(websocket.send_text('ACK'))
    run_sync(websocket.close())

The problem is with the implementation of run_sync. I tried the following as suggested here: https://websockets.readthedocs.io/en/stable/faq.html

Can I use websockets synchronously, without async / await? You can convert every asynchronous call to a synchronous call by wrapping it in asyncio.get_event_loop().run_until_complete(...).

def run_sync(aw: Awaitable):
    return asyncio.get_event_loop().run_until_complete(aw)

But:

  File "app/main.py", line 56, in listen_endpoint
    run_sync(websocket.accept())
  File "app/lib/util.py", line 77, in run_sync
    return asyncio.get_event_loop().run_until_complete(aw)
  File "/home/mangesh/ws/python-versions/3.9.0/lib/python3.9/asyncio/base_events.py", line 618, in run_until_complete
    self._check_running()
  File "/home/mangesh/ws/python-versions/3.9.0/lib/python3.9/asyncio/base_events.py", line 578, in _check_running
    raise RuntimeError('This event loop is already running')
RuntimeError: This event loop is already running

Also tried this:

def run_sync(aw: Awaitable):
    return asyncio.run(aw)

But:

  File "app/main.py", line 56, in listen_endpoint
    run_sync(websocket.accept())
  File "app/lib/util.py", line 77, in run_sync
    return asyncio.run(aw)
  File "/home/mangesh/ws/python-versions/3.9.0/lib/python3.9/asyncio/runners.py", line 33, in run
    raise RuntimeError(
RuntimeError: asyncio.run() cannot be called from a running event loop

Found another suggestion at https://www.aeracode.org/2018/02/19/python-async-simplified/, but I don't think that will work in FastAPI.

Is it possible to do what I'm trying to do? Thanks in advance.

xelsa
  • 31
  • 2
  • It seems to me that the most painless way is to run your synchronous `websocket` server in a separate thread (or process) ([run_in_executor](https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor)) and already organize its communication with the rest of the parts, through a database, queue, [run_coroutine_threadsafe](https://docs.python.org/3/library/asyncio-task.html#asyncio.run_coroutine_threadsafe) etc. – alex_noname Jan 21 '21 at 12:46
  • @alex_noname So I was mainly looking at two ways to go about this. First was to create a sync endpoint and call websocket's async functions in a sync manner (this question). The other way was to convert my sync code to async (a bit tricky because it is a generator), but I was able to do it with the queue from the "janus" module. I posted this question because it was too much code for my liking, and I just wanted to see if I could completely avoid all async code. – xelsa Jan 21 '21 at 13:51
  • I would probably go with library that was posted in the article you shared. Mixing async/sync code is hard enough and looks like they did a pretty clean job of handling it with ```python def get_chat_id(name): return Chat.objects.get(name=name).id async def main(): result = await sync_to_async(get_chat_id)("django") ``` It also seems to play nicely with fastapis internal eventloop. – NoPlaceLike127.0.0.1 Jan 22 '21 at 18:32
  • Does this answer your question? [FastAPI runs api-calls in serial instead of parallel fashion](https://stackoverflow.com/questions/71516140/fastapi-runs-api-calls-in-serial-instead-of-parallel-fashion) – Chris Apr 25 '23 at 05:30
  • You might find [this answer](https://stackoverflow.com/a/76148361/17865804) helpful as well – Chris May 17 '23 at 11:54

0 Answers0