Mercurial > repos > guerler > springsuite
comparison 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 |
comparison
equal
deleted
inserted
replaced
| 0:d30785e31577 | 1:56ad4e20f292 |
|---|---|
| 1 """Generic I/O and shell processing code used by Galaxy tool dependencies.""" | |
| 2 import logging | |
| 3 import os | |
| 4 import subprocess | |
| 5 import sys as _sys | |
| 6 | |
| 7 import six | |
| 8 from six.moves import shlex_quote | |
| 9 | |
| 10 from galaxy.util import ( | |
| 11 unicodify, | |
| 12 which | |
| 13 ) | |
| 14 | |
| 15 log = logging.getLogger(__name__) | |
| 16 | |
| 17 STDOUT_INDICATOR = "-" | |
| 18 | |
| 19 | |
| 20 def redirecting_io(sys=_sys): | |
| 21 """Predicate to determine if we are redicting stdout in process.""" | |
| 22 assert sys is not None | |
| 23 try: | |
| 24 # Need to explicitly call fileno() because sys.stdout could be a | |
| 25 # io.StringIO object, which has a fileno() method but only raises an | |
| 26 # io.UnsupportedOperation exception | |
| 27 sys.stdout.fileno() | |
| 28 except Exception: | |
| 29 return True | |
| 30 else: | |
| 31 return False | |
| 32 | |
| 33 | |
| 34 def redirect_aware_commmunicate(p, sys=_sys): | |
| 35 """Variant of process.communicate that works with in process I/O redirection.""" | |
| 36 assert sys is not None | |
| 37 out, err = p.communicate() | |
| 38 if redirecting_io(sys=sys): | |
| 39 if out: | |
| 40 # We don't unicodify in Python2 because sys.stdout may be a | |
| 41 # cStringIO.StringIO object, which does not accept Unicode strings | |
| 42 if not six.PY2: | |
| 43 out = unicodify(out) | |
| 44 sys.stdout.write(out) | |
| 45 out = None | |
| 46 if err: | |
| 47 if not six.PY2: | |
| 48 err = unicodify(err) | |
| 49 sys.stderr.write(err) | |
| 50 err = None | |
| 51 return out, err | |
| 52 | |
| 53 | |
| 54 def shell(cmds, env=None, **kwds): | |
| 55 """Run shell commands with `shell_process` and wait.""" | |
| 56 sys = kwds.get("sys", _sys) | |
| 57 assert sys is not None | |
| 58 p = shell_process(cmds, env, **kwds) | |
| 59 if redirecting_io(sys=sys): | |
| 60 redirect_aware_commmunicate(p, sys=sys) | |
| 61 exit = p.returncode | |
| 62 return exit | |
| 63 else: | |
| 64 return p.wait() | |
| 65 | |
| 66 | |
| 67 def shell_process(cmds, env=None, **kwds): | |
| 68 """A high-level method wrapping subprocess.Popen. | |
| 69 | |
| 70 Handles details such as environment extension and in process I/O | |
| 71 redirection. | |
| 72 """ | |
| 73 sys = kwds.get("sys", _sys) | |
| 74 popen_kwds = dict() | |
| 75 if isinstance(cmds, six.string_types): | |
| 76 log.warning("Passing program arguments as a string may be a security hazard if combined with untrusted input") | |
| 77 popen_kwds['shell'] = True | |
| 78 if kwds.get("stdout", None) is None and redirecting_io(sys=sys): | |
| 79 popen_kwds["stdout"] = subprocess.PIPE | |
| 80 if kwds.get("stderr", None) is None and redirecting_io(sys=sys): | |
| 81 popen_kwds["stderr"] = subprocess.PIPE | |
| 82 | |
| 83 popen_kwds.update(**kwds) | |
| 84 if env: | |
| 85 new_env = os.environ.copy() | |
| 86 new_env.update(env) | |
| 87 popen_kwds["env"] = new_env | |
| 88 p = subprocess.Popen(cmds, **popen_kwds) | |
| 89 return p | |
| 90 | |
| 91 | |
| 92 def execute(cmds, input=None): | |
| 93 """Execute commands and throw an exception on a non-zero exit. | |
| 94 if input is not None then the string is sent to the process' stdin. | |
| 95 | |
| 96 Return the standard output if the commands are successful | |
| 97 """ | |
| 98 return _wait(cmds, input=input, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| 99 | |
| 100 | |
| 101 def argv_to_str(command_argv, quote=True): | |
| 102 """Convert an argv command list to a string for shell subprocess. | |
| 103 | |
| 104 If None appears in the command list it is simply excluded. | |
| 105 | |
| 106 Arguments are quoted with shlex_quote. That said, this method is not meant to be | |
| 107 used in security critical paths of code and should not be used to sanitize | |
| 108 code. | |
| 109 """ | |
| 110 map_func = shlex_quote if quote else lambda x: x | |
| 111 return " ".join(map_func(c) for c in command_argv if c is not None) | |
| 112 | |
| 113 | |
| 114 def _wait(cmds, input=None, **popen_kwds): | |
| 115 p = subprocess.Popen(cmds, **popen_kwds) | |
| 116 stdout, stderr = p.communicate(input) | |
| 117 stdout, stderr = unicodify(stdout), unicodify(stderr) | |
| 118 if p.returncode != 0: | |
| 119 raise CommandLineException(argv_to_str(cmds), stdout, stderr, p.returncode) | |
| 120 return stdout | |
| 121 | |
| 122 | |
| 123 def download_command(url, to=STDOUT_INDICATOR): | |
| 124 """Build a command line to download a URL. | |
| 125 | |
| 126 By default the URL will be downloaded to standard output but a specific | |
| 127 file can be specified with the `to` argument. | |
| 128 """ | |
| 129 if which("wget"): | |
| 130 download_cmd = ["wget", "-q"] | |
| 131 if to == STDOUT_INDICATOR: | |
| 132 download_cmd.extend(["-O", STDOUT_INDICATOR, url]) | |
| 133 else: | |
| 134 download_cmd.extend(["--recursive", "-O", to, url]) | |
| 135 else: | |
| 136 download_cmd = ["curl", "-L", url] | |
| 137 if to != STDOUT_INDICATOR: | |
| 138 download_cmd.extend(["-o", to]) | |
| 139 return download_cmd | |
| 140 | |
| 141 | |
| 142 class CommandLineException(Exception): | |
| 143 """An exception indicating a non-zero command-line exit.""" | |
| 144 | |
| 145 def __init__(self, command, stdout, stderr, returncode): | |
| 146 """Construct a CommandLineException from command and standard I/O.""" | |
| 147 self.command = command | |
| 148 self.stdout = stdout | |
| 149 self.stderr = stderr | |
| 150 self.returncode = returncode | |
| 151 self.message = ("Failed to execute command-line %s, stderr was:\n" | |
| 152 "-------->>begin stderr<<--------\n" | |
| 153 "%s\n" | |
| 154 "-------->>end stderr<<--------\n" | |
| 155 "-------->>begin stdout<<--------\n" | |
| 156 "%s\n" | |
| 157 "-------->>end stdout<<--------\n" | |
| 158 ) % (command, stderr, stdout) | |
| 159 | |
| 160 def __str__(self): | |
| 161 """Return a verbose error message indicating the command problem.""" | |
| 162 return self.message | |
| 163 | |
| 164 | |
| 165 __all__ = ( | |
| 166 'argv_to_str', | |
| 167 'CommandLineException', | |
| 168 'download_command', | |
| 169 'execute', | |
| 170 'redirect_aware_commmunicate', | |
| 171 'redirecting_io', | |
| 172 'shell', | |
| 173 'shell_process', | |
| 174 'which', | |
| 175 ) |
