Note: I have read this, this, this, and this question. While there is much useful info, none give an exact answer. My knowledge of the language is limited, so I cannot put together pieces from those answers in a way that fit the needed use case (especially point (4) below).
I am looking for a way to run a process with a given set of arguments in current Python (latest version atm is 3.11), with the following requirements:
bash
or PowerShell
All points except for (1) are covered by subprocess.run(args, capture_output=True)
. So, I need to define a function
def run_and_output_realtime:
# ???
return result
Which would allow to change just the first line of code like
run_tool_result = subproccess.run(args, capture_output=True)
if run_tool_result.returncode != 0:
if "Unauthorized" in run_tool_result.stderr.decode():
print("Please check authorization")
else:
if "Duration" in run_tool_result.stdout.decode():
# parse to get whatever duration was in the output
To run_tool_result = run_and_output_realtime(args)
, and have all the remaining lines unchanged and working.
The following would hopefully solve your (1). Tested it on Python 3.11.
import subprocess
import sys
from typing import List, Tuple, NamedTuple
class ProcessResult(NamedTuple):
stdout: bytes
stderr: bytes
returncode: int
def run_and_output_realtime(args: List[str]) -> ProcessResult:
stdout_lines = []
stderr_lines = []
process = subprocess.Popen(
args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True,
bufsize=1,
)
# Read stdout and stderr line by line
while process.poll() is None:
stdout_line = process.stdout.readline()
stderr_line = process.stderr.readline()
if stdout_line:
sys.stdout.write(stdout_line)
sys.stdout.flush()
stdout_lines.append(stdout_line)
if stderr_line:
sys.stderr.write(stderr_line)
sys.stderr.flush()
stderr_lines.append(stderr_line)
# Capture remaining stdout and stderr lines
stdout_remaining = process.stdout.readlines()
stderr_remaining = process.stderr.readlines()
stdout_lines.extend(stdout_remaining)
stderr_lines.extend(stderr_remaining)
process.stdout.close()
process.stderr.close()
stdout = "".join(stdout_lines).encode()
stderr = "".join(stderr_lines).encode()
return ProcessResult(stdout=stdout, stderr=stderr, returncode=process.returncode)