"""
Module containing a wrapper for launching shell commands
"""
import logging
import subprocess
import os
from typing import Optional
# from grader.utils.logger import VERBOSE
logger = logging.getLogger("grader")
[docs]
def run(
command: list[str], current_directory: Optional[str] = None, env_vars: Optional[dict[str, str]] = None
) -> subprocess.CompletedProcess[str]:
"""
Execute a command in the terminal.
Wraps the subprocess.run function, with the check=False, capture_output=True and text=True flags.
If the command passes, log the stdout.
If the command fails, log the returncode, stdout and stderr.
:param command: The command to execute
:param current_directory: The directory to execute the command in
:param env_vars: A dictionary of environment variables to set for the subprocess
:return: The output of the command (returncode, stdout, stderr)
"""
logger.debug(
"Running command: %s, from directory: %s, with environment variables: %s",
" ".join(command),
current_directory,
env_vars,
)
# Prepare the environment variables
if env_vars is not None:
for key, value in os.environ.items():
if key not in env_vars:
env_vars[key] = value
output = subprocess.run(command, check=False, capture_output=True, text=True, cwd=current_directory, env=env_vars)
if output.returncode != 0:
logger.debug("Command failed: %d %s %s", output.returncode, output.stdout, output.stderr)
else:
logger.debug("Command succeeded: %s", output.stdout)
return output
[docs]
def extend_env_variable(variable: str, value: str) -> dict[str, str]:
"""
Extend an environment variable with a new value.
:param variable: The name of the environment variable to extend
:param value: The value to append to the environment variable
:return: A dictionary with the updated environment variable
"""
env = {}
try:
old_value = os.environ[variable]
env[variable] = f"{value}:{old_value}"
except KeyError:
env[variable] = value
return env