Fastapi: [QUESTION] How do I start a thread with FastApi uvicorn

Created on 24 Oct 2019  Â·  5Comments  Â·  Source: tiangolo/fastapi

Description
I tried to integrate the uvicorn with FastApi inside my application having uvicorn run as a thread like I do when I'm was using flask (that could run as a thread while the main app was the main thread).

But I get the error:
ValueError: signal only works in main thread

How can I get rid of the error?
To have the main application run while the web server part run as another thread?

Well to be fair I got a work around and I run the main application as a thread but ... are there alternatives to fix the issue in a proper way and make the sample code below run.,

Additional context
Here is the sample source code:
```
import threading
import uvicorn
from fastapi import FastAPI

app = FastAPI()

def start_web_app():
uvicorn.run(app, port=5000, log_level="info")

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

if __name__ == "__main__":
thread_fastapi = threading.Thread(name='Web App FastApi', daemon=True, target=start_web_app)

print("star thread FastApi")
thread_fastapi.start()
print("ended thread")
...

Here is the full error that I get:
Connected to pydev debugger (build 191.8026.44)
email-validator not installed, email fields will be treated as str.
To install, run: pip install email-validator
star thread FastApi
ended thread
Exception in thread Web App FastApi:
Traceback (most recent call last):
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 455, in install_signal_handlers
loop.add_signal_handler(sig, self.handle_exit, sig, None)
File "C:\Tools\Python37-32\lib\asyncio\events.py", line 540, in add_signal_handler
raise NotImplementedError
NotImplementedError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "C:\Tools\Python37-32\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "C:\Tools\Python37-32\lib\threading.py", line 865, in run
self._target(self._args, *self._kwargs)
File "D:/Git/sms_rates/test_thread_uvicorn.py", line 8, in start_web_app
uvicorn.run(app, port=8449, log_level="info")
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 279, in run
server.run()
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 307, in run
loop.run_until_complete(self.serve(sockets=sockets))
File "C:\Tools\Python37-32\lib\asyncio\base_events.py", line 568, in run_until_complete
return future.result()
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 319, in serve
self.install_signal_handlers()
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 459, in install_signal_handlers
signal.signal(sig, self.handle_exit)
File "C:\Tools\Python37-32\lib\signal.py", line 47, in signal
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread

question

Most helpful comment

Hmm, you shouldn't run it in a thread.

FastAPI will handle it's own thread pool when necessary (depending on if you use async def or def).

And either way, it will handle requests in the async event loop.

Running a WSGI framework (like Flask) in threads is just a trick to increase concurrency, handled by the OS.

With an async framework, concurrency is a top level feature, everything is built around that, and is handled directly in the language, in the event loop. That's why it can have better performance.

Try first following the docs as they are first. That should work well without extra effort. Running Uvicorn in it's own process.

If you need something more "robust" or need to optimize performance, check the Docker image, it has everything pre-fine-tuned.

All 5 comments

You'll have more precise answers on the uvicorn repository, but I'm not
sure what you are looking for is possible.

To my knowledge you cannot handle signals in threads, so unless you have a
way to run uvicorn skipping its install_signal_handlers method, which is
not possible with its current API, then you can't achieve what you want.

Le jeu. 24 oct. 2019 à 5:42 PM, tincumagic notifications@github.com a
écrit :

Description
I tried to integrate the uvicorn with FastApi inside my application having
uvicorn as a thread.
But I get the error:
ValueError: signal only works in main thread

How can I get rid of the error to have the main application run and the
web server part run as another thread.

Additional context
Here is the sample source code:

import threading
import uvicorn
from fastapi import FastAPI

app = FastAPI()

def start_web_app():
uvicorn.run(app, port=8449, log_level="info")

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

if __name__ == "__main__":
thread_fastapi = threading.Thread(name='Web App FastApi', daemon=True, target=start_web_app)

print("star thread FastApi")
thread_fastapi.start()
print("ended thread")
...

I get the error:
Connected to pydev debugger (build 191.8026.44)
email-validator not installed, email fields will be treated as str.
To install, run: pip install email-validator
star thread FastApi
ended thread
Exception in thread Web App FastApi:
Traceback (most recent call last):
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 455, in install_signal_handlers
loop.add_signal_handler(sig, self.handle_exit, sig, None)
File "C:\Tools\Python37-32\lib\asyncio\events.py", line 540, in add_signal_handler
raise NotImplementedError
NotImplementedError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "C:\Tools\Python37-32\lib\threading.py", line 917, in _bootstrap_inner
self.run()
File "C:\Tools\Python37-32\lib\threading.py", line 865, in run
self._target(self._args, *self._kwargs)
File "D:/Git/sms_rates/test_thread_uvicorn.py", line 8, in start_web_app
uvicorn.run(app, port=8449, log_level="info")
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 279, in run
server.run()
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 307, in run
loop.run_until_complete(self.serve(sockets=sockets))
File "C:\Tools\Python37-32\lib\asyncio\base_events.py", line 568, in run_until_complete
return future.result()
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 319, in serve
self.install_signal_handlers()
File "D:\Git\sms_rates\venv\lib\site-packages\uvicorn\main.py", line 459, in install_signal_handlers
signal.signal(sig, self.handle_exit)
File "C:\Tools\Python37-32\lib\signal.py", line 47, in signal
handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
ValueError: signal only works in main thread

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/tiangolo/fastapi/issues/650?email_source=notifications&email_token=AAINSPUJFRNUTIA5Z6B757TQQG65HA5CNFSM4JEV3GVKYY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4HUE53BQ,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/AAINSPWUPKX6G2SILMLLGQTQQG65HANCNFSM4JEV3GVA
.

Hmm, you shouldn't run it in a thread.

FastAPI will handle it's own thread pool when necessary (depending on if you use async def or def).

And either way, it will handle requests in the async event loop.

Running a WSGI framework (like Flask) in threads is just a trick to increase concurrency, handled by the OS.

With an async framework, concurrency is a top level feature, everything is built around that, and is handled directly in the language, in the event loop. That's why it can have better performance.

Try first following the docs as they are first. That should work well without extra effort. Running Uvicorn in it's own process.

If you need something more "robust" or need to optimize performance, check the Docker image, it has everything pre-fine-tuned.

should you want to really do that @tincumagic I'd watch the following PR https://github.com/encode/uvicorn/pull/402 , currently closed but I guess someone needs the same functionnality, that would enable you to run uvicorn overidding its signals setup.

Once the uvicorn API enables you to do that signal handling yourself, you can handle on your main thread and run an event loop per thread if you want, that's possible with something like this for instance.

import asyncio
import threading
from typing import Awaitable, List


def join_t(*threads: threading.Thread) -> List[None]:
    return [t.join() for t in threads]


def start_threads(*threads: threading.Thread) -> List[None]:
    return [t.start() for t in threads]


def event_thread(worker: Awaitable, *args, **kwargs) -> threading.Thread:
    def _worker(*args, **kwargs):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            loop.run_until_complete(worker(*args, **kwargs))
        finally:
            loop.close()
    return threading.Thread(target=_worker, args=args, kwargs=kwargs)


async def mycoro(*args, **kwargs) -> None:
    print(f"using {threading.get_ident()}: ", (args, kwargs))


def main():
    workers = [event_thread(mycoro, "arg1", "arg2", kwarg1=1, kwarg2=2) for i in range(10)]
    start_threads(*workers)
    join_t(*workers)


if __name__ == '__main__':
    main()

Hmm, still, even if Uvicorn was run on a separate thread, FastAPI could run stuff in a threadpool, so I would still advise to have it on its own process.

Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.

Was this page helpful?
0 / 5 - 0 ratings