From b934b3d685b96ba4c96c01e8e4375b1c8caf5978 Mon Sep 17 00:00:00 2001 From: StarHeart Date: Thu, 13 Apr 2023 18:57:00 +0800 Subject: [PATCH] Fix shutdown event on Windows in reloader (#1584) Co-authored-by: Marcelo Trylesinski --- setup.cfg | 1 + tests/supervisors/test_reload.py | 6 +++++- uvicorn/supervisors/basereload.py | 19 ++++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/setup.cfg b/setup.cfg index cfe2b0cb0..203789557 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,6 +47,7 @@ exclude_lines = [coverage:coverage_conditional_plugin] rules = "sys_platform == 'win32'": py-win32 + "sys_platform != 'win32'": py-not-win32 "sys_platform == 'linux'": py-linux "sys_platform == 'darwin'": py-darwin "sys_version_info >= (3, 8)": py-gte-38 diff --git a/tests/supervisors/test_reload.py b/tests/supervisors/test_reload.py index 4f3190117..542b6b04a 100644 --- a/tests/supervisors/test_reload.py +++ b/tests/supervisors/test_reload.py @@ -1,6 +1,7 @@ import logging import signal import socket +import sys from pathlib import Path from time import sleep from typing import Optional, Type @@ -393,7 +394,10 @@ def test_base_reloader_should_exit(tmp_path): assert not reloader.should_exit.is_set() reloader.pause() - reloader.signal_handler(signal.SIGINT, None) + if sys.platform == "win32": + reloader.signal_handler(signal.CTRL_C_EVENT, None) # pragma: py-not-win32 + else: + reloader.signal_handler(signal.SIGINT, None) # pragma: py-win32 assert reloader.should_exit.is_set() with pytest.raises(StopIteration): diff --git a/uvicorn/supervisors/basereload.py b/uvicorn/supervisors/basereload.py index d60d565ee..0c1dc25ad 100644 --- a/uvicorn/supervisors/basereload.py +++ b/uvicorn/supervisors/basereload.py @@ -1,6 +1,7 @@ import logging import os import signal +import sys import threading from pathlib import Path from socket import socket @@ -32,13 +33,17 @@ def __init__( self.sockets = sockets self.should_exit = threading.Event() self.pid = os.getpid() + self.is_restarting = False self.reloader_name: Optional[str] = None def signal_handler(self, sig: int, frame: Optional[FrameType]) -> None: """ A signal handler that is registered with the parent process. """ - self.should_exit.set() + if sys.platform == "win32" and self.is_restarting: + self.is_restarting = False # pragma: py-not-win32 + else: + self.should_exit.set() # pragma: py-win32 def run(self) -> None: self.startup() @@ -80,7 +85,12 @@ def startup(self) -> None: self.process.start() def restart(self) -> None: - self.process.terminate() + if sys.platform == "win32": # pragma: py-not-win32 + self.is_restarting = True + assert self.process.pid is not None + os.kill(self.process.pid, signal.CTRL_C_EVENT) + else: # pragma: py-win32 + self.process.terminate() self.process.join() self.process = get_subprocess( @@ -89,7 +99,10 @@ def restart(self) -> None: self.process.start() def shutdown(self) -> None: - self.process.terminate() + if sys.platform == "win32": + self.should_exit.set() # pragma: py-not-win32 + else: + self.process.terminate() # pragma: py-win32 self.process.join() for sock in self.sockets: