Mercurial > repos > guerler > springsuite
diff planemo/lib/python3.7/site-packages/galaxy/util/commands.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/planemo/lib/python3.7/site-packages/galaxy/util/commands.py Fri Jul 31 00:32:28 2020 -0400 @@ -0,0 +1,175 @@ +"""Generic I/O and shell processing code used by Galaxy tool dependencies.""" +import logging +import os +import subprocess +import sys as _sys + +import six +from six.moves import shlex_quote + +from galaxy.util import ( + unicodify, + which +) + +log = logging.getLogger(__name__) + +STDOUT_INDICATOR = "-" + + +def redirecting_io(sys=_sys): + """Predicate to determine if we are redicting stdout in process.""" + assert sys is not None + try: + # Need to explicitly call fileno() because sys.stdout could be a + # io.StringIO object, which has a fileno() method but only raises an + # io.UnsupportedOperation exception + sys.stdout.fileno() + except Exception: + return True + else: + return False + + +def redirect_aware_commmunicate(p, sys=_sys): + """Variant of process.communicate that works with in process I/O redirection.""" + assert sys is not None + out, err = p.communicate() + if redirecting_io(sys=sys): + if out: + # We don't unicodify in Python2 because sys.stdout may be a + # cStringIO.StringIO object, which does not accept Unicode strings + if not six.PY2: + out = unicodify(out) + sys.stdout.write(out) + out = None + if err: + if not six.PY2: + err = unicodify(err) + sys.stderr.write(err) + err = None + return out, err + + +def shell(cmds, env=None, **kwds): + """Run shell commands with `shell_process` and wait.""" + sys = kwds.get("sys", _sys) + assert sys is not None + p = shell_process(cmds, env, **kwds) + if redirecting_io(sys=sys): + redirect_aware_commmunicate(p, sys=sys) + exit = p.returncode + return exit + else: + return p.wait() + + +def shell_process(cmds, env=None, **kwds): + """A high-level method wrapping subprocess.Popen. + + Handles details such as environment extension and in process I/O + redirection. + """ + sys = kwds.get("sys", _sys) + popen_kwds = dict() + if isinstance(cmds, six.string_types): + log.warning("Passing program arguments as a string may be a security hazard if combined with untrusted input") + popen_kwds['shell'] = True + if kwds.get("stdout", None) is None and redirecting_io(sys=sys): + popen_kwds["stdout"] = subprocess.PIPE + if kwds.get("stderr", None) is None and redirecting_io(sys=sys): + popen_kwds["stderr"] = subprocess.PIPE + + popen_kwds.update(**kwds) + if env: + new_env = os.environ.copy() + new_env.update(env) + popen_kwds["env"] = new_env + p = subprocess.Popen(cmds, **popen_kwds) + return p + + +def execute(cmds, input=None): + """Execute commands and throw an exception on a non-zero exit. + if input is not None then the string is sent to the process' stdin. + + Return the standard output if the commands are successful + """ + return _wait(cmds, input=input, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + +def argv_to_str(command_argv, quote=True): + """Convert an argv command list to a string for shell subprocess. + + If None appears in the command list it is simply excluded. + + Arguments are quoted with shlex_quote. That said, this method is not meant to be + used in security critical paths of code and should not be used to sanitize + code. + """ + map_func = shlex_quote if quote else lambda x: x + return " ".join(map_func(c) for c in command_argv if c is not None) + + +def _wait(cmds, input=None, **popen_kwds): + p = subprocess.Popen(cmds, **popen_kwds) + stdout, stderr = p.communicate(input) + stdout, stderr = unicodify(stdout), unicodify(stderr) + if p.returncode != 0: + raise CommandLineException(argv_to_str(cmds), stdout, stderr, p.returncode) + return stdout + + +def download_command(url, to=STDOUT_INDICATOR): + """Build a command line to download a URL. + + By default the URL will be downloaded to standard output but a specific + file can be specified with the `to` argument. + """ + if which("wget"): + download_cmd = ["wget", "-q"] + if to == STDOUT_INDICATOR: + download_cmd.extend(["-O", STDOUT_INDICATOR, url]) + else: + download_cmd.extend(["--recursive", "-O", to, url]) + else: + download_cmd = ["curl", "-L", url] + if to != STDOUT_INDICATOR: + download_cmd.extend(["-o", to]) + return download_cmd + + +class CommandLineException(Exception): + """An exception indicating a non-zero command-line exit.""" + + def __init__(self, command, stdout, stderr, returncode): + """Construct a CommandLineException from command and standard I/O.""" + self.command = command + self.stdout = stdout + self.stderr = stderr + self.returncode = returncode + self.message = ("Failed to execute command-line %s, stderr was:\n" + "-------->>begin stderr<<--------\n" + "%s\n" + "-------->>end stderr<<--------\n" + "-------->>begin stdout<<--------\n" + "%s\n" + "-------->>end stdout<<--------\n" + ) % (command, stderr, stdout) + + def __str__(self): + """Return a verbose error message indicating the command problem.""" + return self.message + + +__all__ = ( + 'argv_to_str', + 'CommandLineException', + 'download_command', + 'execute', + 'redirect_aware_commmunicate', + 'redirecting_io', + 'shell', + 'shell_process', + 'which', +)