Source code for htpolynet.external.command

"""Thin wrapper around subprocess for running external commands.

Author: Cameron F. Abrams <cfa22@drexel.edu>
"""
import logging
import subprocess
import time

from .. import profiling

logger = logging.getLogger(__name__)

_LINELEN = 55


[docs] def opts(**kwargs): """Format keyword arguments as '-key value' flag pairs. Useful for building command strings from option dicts, e.g.:: opts(f='in.gro', o='out.gro') -> '-f in.gro -o out.gro' Args: **kwargs: option name/value pairs Returns: str: space-separated '-key value' string """ return ' '.join(f'-{k} {v}' for k, v in kwargs.items())
[docs] def run(command, ignore_codes=(), override=None, quiet=True): """Run a shell command and return its output. Args: command (str): shell command to run ignore_codes (tuple | list): return codes to treat as success, defaults to () override (tuple[str, str] | None): ``(needle, msg)`` — if needle appears in stdout or stderr, raise even when returncode == 0, defaults to None quiet (bool): if False, log the command before running, defaults to True Returns: tuple[str, str]: (stdout, stderr) Raises: subprocess.SubprocessError: on non-zero return code (unless in ignore_codes), or when the override needle is found in output """ if not quiet: logger.debug(command) _t0 = time.monotonic() process = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, ) out, err = process.communicate() rc = process.returncode profiling.record_subprocess(profiling.classify_command(command), time.monotonic() - _t0) def _log_output(): if out: logger.error('stdout:\n' + '*' * _LINELEN + '\n' + out + '*' * _LINELEN) if err: logger.error('stderr:\n' + '*' * _LINELEN + '\n' + err + '*' * _LINELEN) if rc != 0 and rc not in ignore_codes: logger.error(f'Returncode: {rc}') _log_output() raise subprocess.SubprocessError(f'Command "{command}" failed with returncode {rc}') if override is not None: needle, msg = override if needle in out or needle in err: logger.error(f'Returncode: {rc}, but error detected: {msg}') _log_output() raise subprocess.SubprocessError(f'Command "{command}" triggered override: {msg}') return out, err