import logging import os import signal import threading import click from uvicorn.subprocess import get_subprocess HANDLED_SIGNALS = ( signal.SIGINT, # Unix signal 2. Sent by Ctrl+C. signal.SIGTERM, # Unix signal 15. Sent by `kill `. ) logger = logging.getLogger("uvicorn.error") class BaseReload: def __init__(self, config, target, sockets): self.config = config self.target = target self.sockets = sockets self.should_exit = threading.Event() self.pid = os.getpid() self.reloader_name = None def signal_handler(self, sig, frame): """ A signal handler that is registered with the parent process. """ self.should_exit.set() def run(self): self.startup() while not self.should_exit.wait(self.config.reload_delay): if self.should_restart(): self.restart() self.shutdown() def startup(self): message = f"Started reloader process [{self.pid}] using {self.reloader_name}" color_message = "Started reloader process [{}] using {}".format( click.style(str(self.pid), fg="cyan", bold=True), click.style(str(self.reloader_name), fg="cyan", bold=True), ) logger.info(message, extra={"color_message": color_message}) for sig in HANDLED_SIGNALS: signal.signal(sig, self.signal_handler) self.process = get_subprocess( config=self.config, target=self.target, sockets=self.sockets ) self.process.start() def restart(self): self.mtimes = {} self.process.terminate() self.process.join() self.process = get_subprocess( config=self.config, target=self.target, sockets=self.sockets ) self.process.start() def shutdown(self): self.process.join() message = "Stopping reloader process [{}]".format(str(self.pid)) color_message = "Stopping reloader process [{}]".format( click.style(str(self.pid), fg="cyan", bold=True) ) logger.info(message, extra={"color_message": color_message}) def should_restart(self): raise NotImplementedError("Reload strategies should override should_restart()")