Source code for pyfstat.utils.cli

import logging
import os
import subprocess
from typing import Union

import numpy as np

logger = logging.getLogger(__name__)


[docs]def run_commandline( cl: str, raise_error: bool = True, return_output: bool = False ) -> Union[subprocess.CompletedProcess, None]: """Run a string command as a subprocess. Parameters ---------- cl: Command to run raise_error: If True, raise an error if the subprocess fails. If False, just log the error, continue, and return ``None``. return_output: If True, return the ``subprocess.CompletedProcess`` object. If False, return ``None``. Returns ---------- out: The ```subprocess.CompletedProcess`` of the subprocess if ``return_output=True``. ``None`` if ``return_output=False`` or on failed execution if ``raise_error=False``. """ logger.info("Now executing: " + cl) if "|" in cl: logger.warning( "Pipe ('|') found in commandline, errors may not be properly caught!" ) try: completed_process = subprocess.run( cl, check=True, shell=True, capture_output=True, text=True, ) if msg := completed_process.stdout: [logger.info(line) for line in msg.splitlines()] if msg := completed_process.stderr: [logger.error(line) for line in msg.splitlines()] if return_output: return completed_process except subprocess.CalledProcessError as e: if msg := getattr(e, "output", None): [logger.info(line) for line in msg.splitlines()] logger.error(f"Execution failed: {e}") if msg := getattr(e, "stderr", None): [logger.error(line) for line in msg.splitlines()] if raise_error: raise return None
[docs]def match_commandlines(cl1, cl2, be_strict_about_full_executable_path=False): """Check if two commandline strings match element-by-element, regardless of order. Parameters ---------- cl1, cl2: str Commandline strings of `executable --key1=val1 --key2=val2` etc format. be_strict_about_full_executable_path: bool If False (default), only checks the basename of the executable. If True, requires its full path to match. Returns ------- match: bool Whether the executable and all `key=val` pairs of the two strings matched. """ cl1s = cl1.split(" ") cl2s = cl2.split(" ") # first item will be the executable name # by default be generous here and do not worry about full paths if not be_strict_about_full_executable_path: cl1s[0] = os.path.basename(cl1s[0]) cl2s[0] = os.path.basename(cl2s[0]) unmatched = np.setxor1d(cl1s, cl2s) return len(unmatched) == 0