Files
Time-Series-Analysis/.venv/lib/python3.12/site-packages/cmdstanpy/utils/command.py
2025-07-30 18:53:50 +03:00

95 lines
3.2 KiB
Python

"""
Run commands and handle returncodes
"""
import os
import subprocess
import sys
from typing import Callable, List, Optional, TextIO
from .filesystem import pushd
from .logging import get_logger
def do_command(
cmd: List[str],
cwd: Optional[str] = None,
*,
fd_out: Optional[TextIO] = sys.stdout,
pbar: Optional[Callable[[str], None]] = None,
) -> None:
"""
Run command as subprocess, polls process output pipes and
either streams outputs to supplied output stream or sends
each line (stripped) to the supplied progress bar callback hook.
Raises ``RuntimeError`` on non-zero return code or execption ``OSError``.
:param cmd: command and args.
:param cwd: directory in which to run command, if unspecified,
run command in the current working directory.
:param fd_out: when supplied, streams to this output stream,
else writes to sys.stdout.
:param pbar: optional callback hook to tqdm, which takes
single ``str`` arguent, see:
https://github.com/tqdm/tqdm#hooks-and-callbacks.
"""
get_logger().debug('cmd: %s\ncwd: %s', ' '.join(cmd), cwd)
try:
# NB: Using this rather than cwd arg to Popen due to windows behavior
with pushd(cwd if cwd is not None else '.'):
# TODO: replace with subprocess.run in later Python versions?
proc = subprocess.Popen(
cmd,
bufsize=1,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # avoid buffer overflow
env=os.environ,
universal_newlines=True,
)
while proc.poll() is None:
if proc.stdout is not None:
line = proc.stdout.readline()
if fd_out is not None:
fd_out.write(line)
if pbar is not None:
pbar(line.strip())
stdout, _ = proc.communicate()
if stdout:
if len(stdout) > 0:
if fd_out is not None:
fd_out.write(stdout)
if pbar is not None:
pbar(stdout.strip())
if proc.returncode != 0: # throw RuntimeError + msg
serror = ''
try:
serror = os.strerror(proc.returncode)
except (ArithmeticError, ValueError):
pass
msg = 'Command {}\n\t{} {}'.format(
cmd, returncode_msg(proc.returncode), serror
)
raise RuntimeError(msg)
except OSError as e:
msg = 'Command: {}\nfailed with error {}\n'.format(cmd, str(e))
raise RuntimeError(msg) from e
def returncode_msg(retcode: int) -> str:
"""interpret retcode"""
if retcode < 0:
sig = -1 * retcode
return f'terminated by signal {sig}'
if retcode <= 125:
return 'error during processing'
if retcode == 126: # shouldn't happen
return ''
if retcode == 127:
return 'program not found'
sig = retcode - 128
return f'terminated by signal {sig}'