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 ) |