Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Make event-handling optional #526

Closed
sm-Fifteen opened this issue Dec 13, 2019 · 6 comments
Closed

[FEATURE] Make event-handling optional #526

sm-Fifteen opened this issue Dec 13, 2019 · 6 comments

Comments

@sm-Fifteen
Copy link

sm-Fifteen commented Dec 13, 2019

I'm trying to get Uvicorn to run as a Windows service so event logging and automatic start/restart can be handled more cleanly than by just running uvicorn as a scheduled task. I've run into a number of roadblocks, though most of these I've been able to work my way around with some configuration. The main roadbloack I still have is event-loop compatibility. Due to some odd behavior of Python threads while running as a windows service (see mhammond/pywin32#1452), it's not currently possible for Python code to register signal handlers. That makes the default ProactorEventLoop unusable, but that can be solved by falling back to a SelectorEventLoop, which used to be the default event loop for Windows before Python 3.8. Not being able to handle the signal for Ctrl+C is not too much of a problem for my use case either, since I can just set the service to trip the Server#should_exit flag manually when the service is asked to stop.

So with all this said and done, if you ignore the service boilerplate wrapping my code, I'm essentially running Uvicorn like this:

import asyncio
import logging
from logging.handlers import NTEventLogHandler

from uvicorn import Server, Config
from app import app

asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

windows_log_handler = NTEventLogHandler('myASGIApp')
logging.getLogger().addHandler(windows_log_handler)

server_config = Config(
    app, host="0.0.0.0", port=8000,
    # Can't log to stdout as a service
    log_config={"version": 1},
    # Don't let uvicorn select what loop to use
    loop="asyncio",
)
self.app_server = Server(server_config)
self.app_server.run()

The main problem I still have with this is this line, which will try to setup event handlers even though doing so will immediately fail given my event loop configuration:

self.install_signal_handlers()

uvicorn/uvicorn/main.py

Lines 518 to 527 in 996aa2d

def install_signal_handlers(self):
loop = asyncio.get_event_loop()
try:
for sig in HANDLED_SIGNALS:
loop.add_signal_handler(sig, self.handle_exit, sig, None)
except NotImplementedError as exc:
# Windows
for sig in HANDLED_SIGNALS:
signal.signal(sig, self.handle_exit)

I currently have to go and comment out that line in the uvicorn module code to get my application to start at all, which is less than ideal. Would it be possible to make signal handling optional if uvicorn is running from inside a Python script, maybe?

EDIT: I would have tried import uvicorn; uvicorn.main.HANDLED_SIGNALS = (), but it turns out that uvicorn.main:main is imported by uvicorn in a way that shadows the real uvicorn.main package, so even this awful hack is not an option.

@euri10
Copy link
Member

euri10 commented Dec 13, 2019

Is that #510 what you'd want?

@sm-Fifteen
Copy link
Author

Yeah, pretty much. Subclassing uvicorn.Server also seems like a much better workaround than what I had. (Why didn't I think of that?)

@vashek
Copy link

vashek commented Sep 12, 2020

I'm making a Windows service as well.
I did have the issue with the signal handlers, which I solved by doing server_instance.install_signal_handlers = lambda: None before calling server_instance.run(). Having done that, my program seems to work fine.

However, I didn't do anything about the event loop policy as you describe (and yet it seems to work), so I'm wondering whether I am missing some non-obvious problem? What issue did you have that made you go for the workaround with asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())?

I am running Python 3.8.2 (tags/v3.8.2:7b3ab59, Feb 25 2020, 23:03:10) [MSC v.1916 64 bit (AMD64)] on win32

@sm-Fifteen
Copy link
Author

@vashek: As mentioned in the OP, it was because of mhammond/pywin32#1452, but disabling install_signal_handlers as euri10 suggested circumvents the issue and makes switching event loop types no longer needed.

@euri10
Copy link
Member

euri10 commented Dec 28, 2020

This should be fixed by #871 without the need to subclass

@thunderamur
Copy link

I need option to disable installation of signal handlers.

I found some workarounds:

  • using separate thread to run uvicorn
  • subclassing
  • method substitution

As for me the last method is the best of the worst. But why did you refuse a direct solution with option for Config?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants