# # Module providing various facilities to other parts of the package # # billiard/util.py # # Copyright (c) 2006-2008, R Oudkerk --- see COPYING.txt # Licensed to PSF under a Contributor Agreement. # import sys import errno import functools import atexit try: import cffi except ImportError: import ctypes try: from subprocess import _args_from_interpreter_flags # noqa except ImportError: # pragma: no cover def _args_from_interpreter_flags(): # noqa """Return a list of command-line arguments reproducing the current settings in sys.flags and sys.warnoptions.""" flag_opt_map = { 'debug': 'd', 'optimize': 'O', 'dont_write_bytecode': 'B', 'no_user_site': 's', 'no_site': 'S', 'ignore_environment': 'E', 'verbose': 'v', 'bytes_warning': 'b', 'hash_randomization': 'R', 'py3k_warning': '3', } args = [] for flag, opt in flag_opt_map.items(): v = getattr(sys.flags, flag) if v > 0: args.append('-' + opt * v) for opt in sys.warnoptions: args.append('-W' + opt) return args from multiprocessing.util import ( # noqa _afterfork_registry, _afterfork_counter, _exit_function, _finalizer_registry, _finalizer_counter, Finalize, ForkAwareLocal, ForkAwareThreadLock, get_temp_dir, is_exiting, register_after_fork, _run_after_forkers, _run_finalizers, ) from .compat import get_errno __all__ = [ 'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger', 'log_to_stderr', 'get_temp_dir', 'register_after_fork', 'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal', 'SUBDEBUG', 'SUBWARNING', ] # Constants from prctl.h PR_GET_PDEATHSIG = 2 PR_SET_PDEATHSIG = 1 # # Logging # NOTSET = 0 SUBDEBUG = 5 DEBUG = 10 INFO = 20 SUBWARNING = 25 WARNING = 30 ERROR = 40 LOGGER_NAME = 'multiprocessing' DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s' _logger = None _log_to_stderr = False def sub_debug(msg, *args, **kwargs): if _logger: _logger.log(SUBDEBUG, msg, *args, **kwargs) def debug(msg, *args, **kwargs): if _logger: _logger.log(DEBUG, msg, *args, **kwargs) def info(msg, *args, **kwargs): if _logger: _logger.log(INFO, msg, *args, **kwargs) def sub_warning(msg, *args, **kwargs): if _logger: _logger.log(SUBWARNING, msg, *args, **kwargs) def warning(msg, *args, **kwargs): if _logger: _logger.log(WARNING, msg, *args, **kwargs) def error(msg, *args, **kwargs): if _logger: _logger.log(ERROR, msg, *args, **kwargs) def get_logger(): ''' Returns logger used by multiprocessing ''' global _logger import logging logging._acquireLock() try: if not _logger: _logger = logging.getLogger(LOGGER_NAME) _logger.propagate = 0 logging.addLevelName(SUBDEBUG, 'SUBDEBUG') logging.addLevelName(SUBWARNING, 'SUBWARNING') # XXX multiprocessing should cleanup before logging if hasattr(atexit, 'unregister'): atexit.unregister(_exit_function) atexit.register(_exit_function) else: atexit._exithandlers.remove((_exit_function, (), {})) atexit._exithandlers.append((_exit_function, (), {})) finally: logging._releaseLock() return _logger def log_to_stderr(level=None): ''' Turn on logging and add a handler which prints to stderr ''' global _log_to_stderr import logging logger = get_logger() formatter = logging.Formatter(DEFAULT_LOGGING_FORMAT) handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) if level: logger.setLevel(level) _log_to_stderr = True return _logger def get_pdeathsig(): """ Return the current value of the parent process death signal """ if not sys.platform.startswith('linux'): # currently we support only linux platform. raise OSError() try: if 'cffi' in sys.modules: ffi = cffi.FFI() ffi.cdef("int prctl (int __option, ...);") arg = ffi.new("int *") C = ffi.dlopen(None) C.prctl(PR_GET_PDEATHSIG, arg) return arg[0] else: sig = ctypes.c_int() libc = ctypes.cdll.LoadLibrary("libc.so.6") libc.prctl(PR_GET_PDEATHSIG, ctypes.byref(sig)) return sig.value except Exception: raise OSError() def set_pdeathsig(sig): """ Set the parent process death signal of the calling process to sig (either a signal value in the range 1..maxsig, or 0 to clear). This is the signal that the calling process will get when its parent dies. This value is cleared for the child of a fork(2) and (since Linux 2.4.36 / 2.6.23) when executing a set-user-ID or set-group-ID binary. """ if not sys.platform.startswith('linux'): # currently we support only linux platform. raise OSError("pdeathsig is only supported on linux") try: if 'cffi' in sys.modules: ffi = cffi.FFI() ffi.cdef("int prctl (int __option, ...);") C = ffi.dlopen(None) C.prctl(PR_SET_PDEATHSIG, ffi.cast("int", sig)) else: libc = ctypes.cdll.LoadLibrary("libc.so.6") libc.prctl(PR_SET_PDEATHSIG, ctypes.c_int(sig)) except Exception as e: raise OSError("An error occured while setting pdeathsig") from e def _eintr_retry(func): ''' Automatic retry after EINTR. ''' @functools.wraps(func) def wrapped(*args, **kwargs): while 1: try: return func(*args, **kwargs) except OSError as exc: if get_errno(exc) != errno.EINTR: raise return wrapped