Files
computer-playbook/cli/deploy/development/proc.py
T

73 lines
1.5 KiB
Python

from __future__ import annotations
import sys
import subprocess
import threading
from typing import TextIO
def _drain_stream(
stream: TextIO,
*,
sink: TextIO,
) -> None:
"""
Read a text stream line-by-line and write to sink.
"""
try:
for line in iter(stream.readline, ""):
sink.write(line)
sink.flush()
finally:
try:
stream.close()
except Exception as exc:
sys.stderr.write(f"Warning: failed to close stream {stream!r}: {exc}\n")
sys.stderr.flush()
def run_streaming(
cmd: list[str],
*,
cwd,
env: dict[str, str],
text: bool = True,
) -> subprocess.CompletedProcess:
"""
Run a subprocess and stream stdout/stderr live to the terminal.
"""
p = subprocess.Popen(
cmd,
cwd=cwd,
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=text,
bufsize=1, # line buffered (best effort)
)
assert p.stdout is not None
assert p.stderr is not None
t_out = threading.Thread(
target=_drain_stream,
kwargs={"stream": p.stdout, "sink": sys.stdout},
)
t_err = threading.Thread(
target=_drain_stream,
kwargs={"stream": p.stderr, "sink": sys.stderr},
)
t_out.daemon = True
t_err.daemon = True
t_out.start()
t_err.start()
rc = p.wait()
# Process is finished; drain threads should finish quickly too.
t_out.join()
t_err.join()
return subprocess.CompletedProcess(cmd, rc)