From eb0df3a68cb6a6d3859422c357be47de9f5a50c2 Mon Sep 17 00:00:00 2001 From: Samuel Colvin Date: Thu, 24 Aug 2023 13:36:20 +0100 Subject: [PATCH] Catch `SIGTERM` and kill running process before exitting (#244) * catch sigterm properly * remove log --- watchfiles/run.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/watchfiles/run.py b/watchfiles/run.py index 71e9b3a..d1e2494 100644 --- a/watchfiles/run.py +++ b/watchfiles/run.py @@ -129,6 +129,7 @@ def foobar(a, b, c): target_type = detect_target_type(target) logger.debug('running "%s" as %s', target, target_type) + catch_sigterm() process = start_process(target, target_type, args, kwargs) reloads = 0 @@ -207,6 +208,7 @@ async def main(): target_type = detect_target_type(target) logger.debug('running "%s" as %s', target, target_type) + catch_sigterm() process = await anyio.to_thread.run_sync(start_process, target, target_type, args, kwargs) reloads = 0 @@ -421,3 +423,19 @@ def set_tty(tty_path: Optional[str]) -> Generator[None, None, None]: else: # currently on windows tty_path is None and there's nothing we can do here yield + + +def raise_keyboard_interrupt(signum: int, _frame: Any) -> None: # pragma: no cover + logger.warning('received signal %s, raising KeyboardInterrupt', signal.Signals(signum)) + raise KeyboardInterrupt + + +def catch_sigterm() -> None: + """ + Catch SIGTERM and raise KeyboardInterrupt instead. This means watchfiles will stop quickly + on `docker compose stop` and other cases where SIGTERM is sent. + + Without this the watchfiles process will be killed while a running process will continue uninterrupted. + """ + logger.debug('registering handler for SIGTERM on watchfiles process %d', os.getpid()) + signal.signal(signal.SIGTERM, raise_keyboard_interrupt)