# -*- coding: utf-8 -*- # Copyright (C) 2012 Anaconda, Inc # SPDX-License-Identifier: BSD-3-Clause from __future__ import absolute_import, division, print_function, unicode_literals from contextlib import contextmanager from logging import getLogger import signal import threading from .compat import iteritems log = getLogger(__name__) INTERRUPT_SIGNALS = ( 'SIGABRT', 'SIGINT', 'SIGTERM', 'SIGQUIT', 'SIGBREAK', ) def get_signal_name(signum): """ Examples: >>> from signal import SIGINT >>> get_signal_name(SIGINT) 'SIGINT' """ return next((k for k, v in iteritems(signal.__dict__) if v == signum and k.startswith('SIG') and not k.startswith('SIG_')), None) @contextmanager def signal_handler(handler): # TODO: test and fix windows # https://danielkaes.wordpress.com/2009/06/04/how-to-catch-kill-events-with-python/ _thread_local = threading.local() _thread_local.previous_handlers = [] for signame in INTERRUPT_SIGNALS: sig = getattr(signal, signame, None) if sig: log.debug("registering handler for %s", signame) try: prev_handler = signal.signal(sig, handler) _thread_local.previous_handlers.append((sig, prev_handler)) except ValueError as e: # pragma: no cover # ValueError: signal only works in main thread log.debug('%r', e) try: yield finally: standard_handlers = signal.SIG_IGN, signal.SIG_DFL for sig, previous_handler in _thread_local.previous_handlers: if callable(previous_handler) or previous_handler in standard_handlers: log.debug("de-registering handler for %s", sig) signal.signal(sig, previous_handler)