##// END OF EJS Templates
Run user $SHELL only for shell commands....
Pavol Juhas -
Show More
@@ -1,217 +1,223 b''
1 1 """Common utilities for the various process_* implementations.
2 2
3 3 This file is only meant to be imported by the platform-specific implementations
4 4 of subprocess utilities, and it contains tools that are common to all of them.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2010-2011 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 import subprocess
18 18 import shlex
19 19 import sys
20 20 import os
21 21
22 22 from IPython.utils import py3compat
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Function definitions
26 26 #-----------------------------------------------------------------------------
27 27
28 28 def read_no_interrupt(p):
29 29 """Read from a pipe ignoring EINTR errors.
30 30
31 31 This is necessary because when reading from pipes with GUI event loops
32 32 running in the background, often interrupts are raised that stop the
33 33 command from completing."""
34 34 import errno
35 35
36 36 try:
37 37 return p.read()
38 38 except IOError as err:
39 39 if err.errno != errno.EINTR:
40 40 raise
41 41
42 42
43 43 def process_handler(cmd, callback, stderr=subprocess.PIPE):
44 44 """Open a command in a shell subprocess and execute a callback.
45 45
46 46 This function provides common scaffolding for creating subprocess.Popen()
47 47 calls. It creates a Popen object and then calls the callback with it.
48 48
49 49 Parameters
50 50 ----------
51 51 cmd : str or list
52 52 A command to be executed by the system, using :class:`subprocess.Popen`.
53 53 If a string is passed, it will be run in the system shell. If a list is
54 54 passed, it will be used directly as arguments.
55 55
56 56 callback : callable
57 57 A one-argument function that will be called with the Popen object.
58 58
59 59 stderr : file descriptor number, optional
60 60 By default this is set to ``subprocess.PIPE``, but you can also pass the
61 61 value ``subprocess.STDOUT`` to force the subprocess' stderr to go into
62 62 the same file descriptor as its stdout. This is useful to read stdout
63 63 and stderr combined in the order they are generated.
64 64
65 65 Returns
66 66 -------
67 67 The return value of the provided callback is returned.
68 68 """
69 69 sys.stdout.flush()
70 70 sys.stderr.flush()
71 71 # On win32, close_fds can't be true when using pipes for stdin/out/err
72 72 close_fds = sys.platform != 'win32'
73 p = subprocess.Popen(cmd, shell=isinstance(cmd, py3compat.string_types),
74 executable=os.environ.get('SHELL'),
73 # Determine if cmd should be run with system shell.
74 shell = isinstance(cmd, py3compat.string_types)
75 # On POSIX systems run shell commands with user-preferred shell.
76 executable = None
77 if shell and os.name == 'posix' and 'SHELL' in os.environ:
78 executable = os.environ['SHELL']
79 p = subprocess.Popen(cmd, shell=shell,
80 executable=executable,
75 81 stdin=subprocess.PIPE,
76 82 stdout=subprocess.PIPE,
77 83 stderr=stderr,
78 84 close_fds=close_fds)
79 85
80 86 try:
81 87 out = callback(p)
82 88 except KeyboardInterrupt:
83 89 print('^C')
84 90 sys.stdout.flush()
85 91 sys.stderr.flush()
86 92 out = None
87 93 finally:
88 94 # Make really sure that we don't leave processes behind, in case the
89 95 # call above raises an exception
90 96 # We start by assuming the subprocess finished (to avoid NameErrors
91 97 # later depending on the path taken)
92 98 if p.returncode is None:
93 99 try:
94 100 p.terminate()
95 101 p.poll()
96 102 except OSError:
97 103 pass
98 104 # One last try on our way out
99 105 if p.returncode is None:
100 106 try:
101 107 p.kill()
102 108 except OSError:
103 109 pass
104 110
105 111 return out
106 112
107 113
108 114 def getoutput(cmd):
109 115 """Run a command and return its stdout/stderr as a string.
110 116
111 117 Parameters
112 118 ----------
113 119 cmd : str or list
114 120 A command to be executed in the system shell.
115 121
116 122 Returns
117 123 -------
118 124 output : str
119 125 A string containing the combination of stdout and stderr from the
120 126 subprocess, in whatever order the subprocess originally wrote to its
121 127 file descriptors (so the order of the information in this string is the
122 128 correct order as would be seen if running the command in a terminal).
123 129 """
124 130 out = process_handler(cmd, lambda p: p.communicate()[0], subprocess.STDOUT)
125 131 if out is None:
126 132 return ''
127 133 return py3compat.bytes_to_str(out)
128 134
129 135
130 136 def getoutputerror(cmd):
131 137 """Return (standard output, standard error) of executing cmd in a shell.
132 138
133 139 Accepts the same arguments as os.system().
134 140
135 141 Parameters
136 142 ----------
137 143 cmd : str or list
138 144 A command to be executed in the system shell.
139 145
140 146 Returns
141 147 -------
142 148 stdout : str
143 149 stderr : str
144 150 """
145 151 return get_output_error_code(cmd)[:2]
146 152
147 153 def get_output_error_code(cmd):
148 154 """Return (standard output, standard error, return code) of executing cmd
149 155 in a shell.
150 156
151 157 Accepts the same arguments as os.system().
152 158
153 159 Parameters
154 160 ----------
155 161 cmd : str or list
156 162 A command to be executed in the system shell.
157 163
158 164 Returns
159 165 -------
160 166 stdout : str
161 167 stderr : str
162 168 returncode: int
163 169 """
164 170
165 171 out_err, p = process_handler(cmd, lambda p: (p.communicate(), p))
166 172 if out_err is None:
167 173 return '', '', p.returncode
168 174 out, err = out_err
169 175 return py3compat.bytes_to_str(out), py3compat.bytes_to_str(err), p.returncode
170 176
171 177 def arg_split(s, posix=False, strict=True):
172 178 """Split a command line's arguments in a shell-like manner.
173 179
174 180 This is a modified version of the standard library's shlex.split()
175 181 function, but with a default of posix=False for splitting, so that quotes
176 182 in inputs are respected.
177 183
178 184 if strict=False, then any errors shlex.split would raise will result in the
179 185 unparsed remainder being the last element of the list, rather than raising.
180 186 This is because we sometimes use arg_split to parse things other than
181 187 command-line args.
182 188 """
183 189
184 190 # Unfortunately, python's shlex module is buggy with unicode input:
185 191 # http://bugs.python.org/issue1170
186 192 # At least encoding the input when it's unicode seems to help, but there
187 193 # may be more problems lurking. Apparently this is fixed in python3.
188 194 is_unicode = False
189 195 if (not py3compat.PY3) and isinstance(s, unicode):
190 196 is_unicode = True
191 197 s = s.encode('utf-8')
192 198 lex = shlex.shlex(s, posix=posix)
193 199 lex.whitespace_split = True
194 200 # Extract tokens, ensuring that things like leaving open quotes
195 201 # does not cause this to raise. This is important, because we
196 202 # sometimes pass Python source through this (e.g. %timeit f(" ")),
197 203 # and it shouldn't raise an exception.
198 204 # It may be a bad idea to parse things that are not command-line args
199 205 # through this function, but we do, so let's be safe about it.
200 206 lex.commenters='' #fix for GH-1269
201 207 tokens = []
202 208 while True:
203 209 try:
204 210 tokens.append(next(lex))
205 211 except StopIteration:
206 212 break
207 213 except ValueError:
208 214 if strict:
209 215 raise
210 216 # couldn't parse, get remaining blob as last token
211 217 tokens.append(lex.token)
212 218 break
213 219
214 220 if is_unicode:
215 221 # Convert the tokens back to unicode.
216 222 tokens = [x.decode('utf-8') for x in tokens]
217 223 return tokens
General Comments 0
You need to be logged in to leave comments. Login now