1# Copyright (c) 2023 Nordic Semiconductor ASA 2# 3# SPDX-License-Identifier: Apache-2.0 4 5from __future__ import annotations 6 7import contextlib 8import logging 9import os 10import platform 11import shlex 12import signal 13import subprocess 14 15import psutil 16 17_WINDOWS = platform.system() == 'Windows' 18 19 20def log_command(logger: logging.Logger, msg: str, args: list, level: int = logging.DEBUG) -> None: 21 """ 22 Platform-independent helper for logging subprocess invocations. 23 24 Will log a command string that can be copy/pasted into a POSIX 25 shell on POSIX platforms. This is not available on Windows, so 26 the entire args array is logged instead. 27 28 :param logger: logging.Logger to use 29 :param msg: message to associate with the command 30 :param args: argument list as passed to subprocess module 31 :param level: log level 32 """ 33 msg = f'{msg}: %s' 34 if _WINDOWS: 35 logger.log(level, msg, str(args)) 36 else: 37 logger.log(level, msg, shlex.join(args)) 38 39 40def terminate_process(proc: subprocess.Popen, timeout: float = 0.5) -> None: 41 """ 42 Try to terminate provided process and all its subprocesses recursively. 43 """ 44 with contextlib.suppress(ProcessLookupError, psutil.NoSuchProcess): 45 for child in psutil.Process(proc.pid).children(recursive=True): 46 with contextlib.suppress(ProcessLookupError, psutil.NoSuchProcess): 47 os.kill(child.pid, signal.SIGTERM) 48 proc.terminate() 49 try: 50 proc.wait(timeout=timeout) 51 except subprocess.TimeoutExpired: 52 proc.kill() 53