| |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import logging |
| import os |
| import signal |
| import sys |
|
|
|
|
| class ProcessSignalHandler: |
| """Utility class to attach graceful shutdown signal handlers. |
| |
| The class exposes a shutdown_event attribute that is set when a shutdown |
| signal is received. A counter tracks how many shutdown signals have been |
| caught. On the second signal the process exits with status 1. |
| """ |
|
|
| _SUPPORTED_SIGNALS = ("SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT") |
|
|
| def __init__(self, use_threads: bool, display_pid: bool = False): |
| |
| |
| |
| if use_threads: |
| from threading import Event |
| else: |
| from multiprocessing import Event |
|
|
| self.shutdown_event = Event() |
| self._counter: int = 0 |
| self._display_pid = display_pid |
|
|
| self._register_handlers() |
|
|
| @property |
| def counter(self) -> int: |
| """Number of shutdown signals that have been intercepted.""" |
| return self._counter |
|
|
| def _register_handlers(self): |
| """Attach the internal _signal_handler to a subset of POSIX signals.""" |
|
|
| def _signal_handler(signum, frame): |
| pid_str = "" |
| if self._display_pid: |
| pid_str = f"[PID: {os.getpid()}]" |
| logging.info(f"{pid_str} Shutdown signal {signum} received. Cleaning up…") |
| self.shutdown_event.set() |
| self._counter += 1 |
|
|
| |
| |
| |
| |
| if self._counter > 1: |
| logging.info("Force shutdown") |
| sys.exit(1) |
|
|
| for sig_name in self._SUPPORTED_SIGNALS: |
| sig = getattr(signal, sig_name, None) |
| if sig is None: |
| |
| |
| continue |
| try: |
| signal.signal(sig, _signal_handler) |
| except (ValueError, OSError): |
| |
| continue |
|
|