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