"""Only used when there is a job script or CWLTOOL_FORCE_SHELL_POPEN=1.""" import json import os import subprocess # nosec import sys from typing import BinaryIO, Dict, List, Optional, TextIO, Union def handle_software_environment(cwl_env: Dict[str, str], script: str) -> Dict[str, str]: """Update the provided environment dict by running the script.""" exec_env = cwl_env.copy() exec_env["_CWLTOOL"] = "1" res = subprocess.run(["bash", script], shell=False, env=exec_env) # nosec if res.returncode != 0: sys.stderr.write("Error while using SoftwareRequirements to modify environment\n") return cwl_env env = cwl_env.copy() with open("output_environment.dat") as _: data = _.read().strip("\0") for line in data.split("\0"): key, val = line.split("=", 1) if key in ("_", "PWD", "SHLVL", "TMPDIR", "HOME", "_CWLTOOL"): # Skip some variables that are meaningful to the shell or set # specifically by the CWL runtime environment. continue env[key] = val return env def main(argv: List[str]) -> int: """ Read in the configuration JSON and execute the commands. The first argument is the path to the JSON dictionary file containing keys: "commands": an array of strings that represents the command line to run "cwd": A string specifying which directory to run in "env": a dictionary of strings containing the environment variables to set "stdin_path": a string (or a null) giving the path that should be piped to STDIN "stdout_path": a string (or a null) giving the path that should receive the STDOUT "stderr_path": a string (or a null) giving the path that should receive the STDERR The second argument is optional, it specifies a shell script to execute prior, and the environment variables it sets will be combined with the environment variables from the "env" key in the JSON dictionary from the first argument. """ with open(argv[1]) as f: popen_description = json.load(f) commands = popen_description["commands"] cwd = popen_description["cwd"] env = popen_description["env"] env["PATH"] = os.environ.get("PATH") stdin_path = popen_description["stdin_path"] stdout_path = popen_description["stdout_path"] stderr_path = popen_description["stderr_path"] if stdin_path is not None: stdin: Union[BinaryIO, int] = open(stdin_path, "rb") else: stdin = subprocess.PIPE if stdout_path is not None: stdout: Union[BinaryIO, TextIO] = open(stdout_path, "wb") else: stdout = sys.stderr if stderr_path is not None: stderr: Union[BinaryIO, TextIO] = open(stderr_path, "wb") else: stderr = sys.stderr try: env_script: Optional[str] = argv[2] except IndexError: env_script = None if env_script is not None: env = handle_software_environment(env, env_script) sp = subprocess.Popen( # nosec commands, shell=False, close_fds=True, stdin=stdin, stdout=stdout, stderr=stderr, env=env, cwd=cwd, ) if sp.stdin: sp.stdin.close() rcode = sp.wait() if not isinstance(stdin, int): stdin.close() if stdout is not sys.stderr: stdout.close() if stderr is not sys.stderr: stderr.close() return rcode if __name__ == "__main__": sys.exit(main(sys.argv))