Initial code commit
This commit is contained in:
parent
cf72c0fd43
commit
75d003a3be
12 changed files with 1203 additions and 0 deletions
93
setup-scripts/CommandRunner.py
Normal file
93
setup-scripts/CommandRunner.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import os, sys, subprocess, select
|
||||
from typing import List, Tuple
|
||||
|
||||
class CommandRunner:
|
||||
@staticmethod
|
||||
def run_command(command: str | List[str], check=True) -> Tuple[int, str, str]:
|
||||
"""Run a command on the system and return the output
|
||||
|
||||
Args:
|
||||
command (str | List[str]): The command to run
|
||||
check (bool, optional): If the command should raise an exception if it fails (`check=` for `subprocess.run`). Defaults to True.
|
||||
|
||||
Returns:
|
||||
Tuple[int, str, str]: The return code of the command, the output of the command (on standard out), and the error output of the command (on standard error)
|
||||
"""
|
||||
|
||||
# Copy of the environment variables
|
||||
new_env = os.environ.copy()
|
||||
|
||||
if not check:
|
||||
# If the check isn't set, leave it out of the `subprocess.run`` call
|
||||
# This means no exception will be raised if the command fails
|
||||
result = subprocess.run(command, env=new_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
|
||||
return result.returncode, result.stdout.decode('utf-8').strip(), result.stderr.decode('utf-8')
|
||||
else:
|
||||
# Run the command and raise an exception if it fails
|
||||
try:
|
||||
result = subprocess.run(command, env=new_env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, check=True)
|
||||
return result.returncode, result.stdout.decode('utf-8').strip(), result.stderr.decode('utf-8').strip()
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Log the error and re-raise or handle as appropriate
|
||||
raise RuntimeError(f"Command '{command}' failed with error: {e.stderr}") from e
|
||||
|
||||
@staticmethod
|
||||
def run_command_in_real_time(command: str | List[str]) -> Tuple[int, str, str]:
|
||||
"""Similar to `run_command` but prints the output of the command in real-time
|
||||
|
||||
Args:
|
||||
command (str | List[str]): The command to run
|
||||
Returns:
|
||||
tuple[int, str, str]: The return code of the command, the output of the command (on standard out), and the error output of the command (on standard error)
|
||||
"""
|
||||
|
||||
# Variables to store the output of the command
|
||||
stdout_lines: list[str] = []
|
||||
stderr_lines: list[str] = []
|
||||
|
||||
# Copy of the environment variables
|
||||
my_env = os.environ.copy()
|
||||
|
||||
# Run the command
|
||||
# We use a `with` statement to ensure the process is closed properly
|
||||
with subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=my_env, shell=True) as proc:
|
||||
# Call `poll()` initially because we want to emulate a do-while loop
|
||||
return_code = proc.poll()
|
||||
|
||||
# Loop until the process is finished
|
||||
# In theory this will loop about every second (timeout on the select call)
|
||||
# Or slightly faster if the process outputs something
|
||||
while(return_code is None):
|
||||
# Because `readline` block until it gets a line,
|
||||
# But the executed command ISN'T guaranteed to output a line every time
|
||||
# We use `select` to check if there's something to read
|
||||
# It Waits for 1 second or for the process to output something
|
||||
rlist, _, _ = select.select([proc.stdout.fileno(), proc.stderr.fileno()], [], [], 1)
|
||||
|
||||
# There was something to read from the process's stdout
|
||||
if proc.stdout.fileno() in rlist:
|
||||
# Read the line from the process
|
||||
stdout_line = proc.stdout.readline()
|
||||
|
||||
# Add the line to the cumulative output
|
||||
stdout_lines.append(stdout_line.decode('utf-8').strip())
|
||||
|
||||
# Print the output in real-time
|
||||
print(stdout_line.decode('utf-8').strip())
|
||||
|
||||
# There was something to read from the process's stderr
|
||||
if proc.stderr.fileno() in rlist:
|
||||
# Read the line from the process
|
||||
stderr_line = proc.stderr.readline()
|
||||
|
||||
# Add the line to the cumulative output
|
||||
stderr_lines.append(stderr_line.decode('utf-8').strip())
|
||||
|
||||
# Print the error output of the command in real-time to stderr
|
||||
print(stderr_line.decode('utf-8').strip(), file=sys.stderr)
|
||||
|
||||
# Update the return code (to see if the process is finished)
|
||||
return_code = proc.poll()
|
||||
|
||||
# Return the error code AND the full output of the command as a string
|
||||
return return_code, '\n'.join(stdout_lines), '\n'.join(stderr_lines)
|
||||
Loading…
Add table
Add a link
Reference in a new issue