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