##// END OF EJS Templates
add missing env arg...
MinRK -
Show More
@@ -1,277 +1,278 b''
1 1 """Utilities for launching kernels
2 2
3 3 Authors:
4 4
5 5 * Min Ragan-Kelley
6 6
7 7 """
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Copyright (C) 2013 The IPython Development Team
11 11 #
12 12 # Distributed under the terms of the BSD License. The full license is in
13 13 # the file COPYING, distributed as part of this software.
14 14 #-----------------------------------------------------------------------------
15 15
16 16 #-----------------------------------------------------------------------------
17 17 # Imports
18 18 #-----------------------------------------------------------------------------
19 19
20 20 import os
21 21 import sys
22 22 from subprocess import Popen, PIPE
23 23
24 24 from IPython.utils.encoding import getdefaultencoding
25 25 from IPython.utils.py3compat import cast_bytes_py2
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Launching Kernels
29 29 #-----------------------------------------------------------------------------
30 30
31 31 def swallow_argv(argv, aliases=None, flags=None):
32 32 """strip frontend-specific aliases and flags from an argument list
33 33
34 34 For use primarily in frontend apps that want to pass a subset of command-line
35 35 arguments through to a subprocess, where frontend-specific flags and aliases
36 36 should be removed from the list.
37 37
38 38 Parameters
39 39 ----------
40 40
41 41 argv : list(str)
42 42 The starting argv, to be filtered
43 43 aliases : container of aliases (dict, list, set, etc.)
44 44 The frontend-specific aliases to be removed
45 45 flags : container of flags (dict, list, set, etc.)
46 46 The frontend-specific flags to be removed
47 47
48 48 Returns
49 49 -------
50 50
51 51 argv : list(str)
52 52 The argv list, excluding flags and aliases that have been stripped
53 53 """
54 54
55 55 if aliases is None:
56 56 aliases = set()
57 57 if flags is None:
58 58 flags = set()
59 59
60 60 stripped = list(argv) # copy
61 61
62 62 swallow_next = False
63 63 was_flag = False
64 64 for a in argv:
65 65 if a == '--':
66 66 break
67 67 if swallow_next:
68 68 swallow_next = False
69 69 # last arg was an alias, remove the next one
70 70 # *unless* the last alias has a no-arg flag version, in which
71 71 # case, don't swallow the next arg if it's also a flag:
72 72 if not (was_flag and a.startswith('-')):
73 73 stripped.remove(a)
74 74 continue
75 75 if a.startswith('-'):
76 76 split = a.lstrip('-').split('=')
77 77 name = split[0]
78 78 # we use startswith because argparse accepts any arg to be specified
79 79 # by any leading section, as long as it is unique,
80 80 # so `--no-br` means `--no-browser` in the notebook, etc.
81 81 if any(alias.startswith(name) for alias in aliases):
82 82 stripped.remove(a)
83 83 if len(split) == 1:
84 84 # alias passed with arg via space
85 85 swallow_next = True
86 86 # could have been a flag that matches an alias, e.g. `existing`
87 87 # in which case, we might not swallow the next arg
88 88 was_flag = name in flags
89 89 elif len(split) == 1 and any(flag.startswith(name) for flag in flags):
90 90 # strip flag, but don't swallow next, as flags don't take args
91 91 stripped.remove(a)
92 92
93 93 # return shortened list
94 94 return stripped
95 95
96 96
97 97 def make_ipkernel_cmd(code, executable=None, extra_arguments=[], **kw):
98 98 """Build Popen command list for launching an IPython kernel.
99 99
100 100 Parameters
101 101 ----------
102 102 code : str,
103 103 A string of Python code that imports and executes a kernel entry point.
104 104
105 105 executable : str, optional (default sys.executable)
106 106 The Python executable to use for the kernel process.
107 107
108 108 extra_arguments : list, optional
109 109 A list of extra arguments to pass when executing the launch code.
110 110
111 111 Returns
112 112 -------
113 113
114 114 A Popen command list
115 115 """
116 116
117 117 # Build the kernel launch command.
118 118 if executable is None:
119 119 executable = sys.executable
120 120 arguments = [ executable, '-c', code, '-f', '{connection_file}' ]
121 121 arguments.extend(extra_arguments)
122 122
123 123 # Spawn a kernel.
124 124 if sys.platform == 'win32':
125 125
126 126 # If the kernel is running on pythonw and stdout/stderr are not been
127 127 # re-directed, it will crash when more than 4KB of data is written to
128 128 # stdout or stderr. This is a bug that has been with Python for a very
129 129 # long time; see http://bugs.python.org/issue706263.
130 130 # A cleaner solution to this problem would be to pass os.devnull to
131 131 # Popen directly. Unfortunately, that does not work.
132 132 if executable.endswith('pythonw.exe'):
133 133 arguments.append('--no-stdout')
134 134 arguments.append('--no-stderr')
135 135
136 136 return arguments
137 137
138 138
139 139 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None,
140 140 independent=False,
141 141 cwd=None, ipython_kernel=True,
142 env=None,
142 143 **kw
143 144 ):
144 145 """ Launches a localhost kernel, binding to the specified ports.
145 146
146 147 Parameters
147 148 ----------
148 149 cmd : Popen list,
149 150 A string of Python code that imports and executes a kernel entry point.
150 151
151 152 stdin, stdout, stderr : optional (default None)
152 153 Standards streams, as defined in subprocess.Popen.
153 154
154 155 independent : bool, optional (default False)
155 156 If set, the kernel process is guaranteed to survive if this process
156 157 dies. If not set, an effort is made to ensure that the kernel is killed
157 158 when this process dies. Note that in this case it is still good practice
158 159 to kill kernels manually before exiting.
159 160
160 161 cwd : path, optional
161 162 The working dir of the kernel process (default: cwd of this process).
162 163
163 164 ipython_kernel : bool, optional
164 165 Whether the kernel is an official IPython one,
165 166 and should get a bit of special treatment.
166 167
167 168 Returns
168 169 -------
169 170
170 171 Popen instance for the kernel subprocess
171 172 """
172 173
173 174 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
174 175 # are invalid. Unfortunately, there is in general no way to detect whether
175 176 # they are valid. The following two blocks redirect them to (temporary)
176 177 # pipes in certain important cases.
177 178
178 179 # If this process has been backgrounded, our stdin is invalid. Since there
179 180 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
180 181 # place this one safe and always redirect.
181 182 redirect_in = True
182 183 _stdin = PIPE if stdin is None else stdin
183 184
184 185 # If this process in running on pythonw, we know that stdin, stdout, and
185 186 # stderr are all invalid.
186 187 redirect_out = sys.executable.endswith('pythonw.exe')
187 188 if redirect_out:
188 189 _stdout = PIPE if stdout is None else stdout
189 190 _stderr = PIPE if stderr is None else stderr
190 191 else:
191 192 _stdout, _stderr = stdout, stderr
192 193
193 194 env = env if (env is not None) else os.environ.copy()
194 195
195 196 encoding = getdefaultencoding(prefer_stream=False)
196 197
197 198 # Spawn a kernel.
198 199 if sys.platform == 'win32':
199 200 # Popen on Python 2 on Windows cannot handle unicode args or cwd
200 201 cmd = [ cast_bytes_py2(c, encoding) for c in cmd ]
201 202 if cwd:
202 203 cwd = cast_bytes_py2(cwd, sys.getfilesystemencoding() or 'ascii')
203 204
204 205 from IPython.kernel.zmq.parentpoller import ParentPollerWindows
205 206 # Create a Win32 event for interrupting the kernel.
206 207 interrupt_event = ParentPollerWindows.create_interrupt_event()
207 208 # Store this in an environment variable for third party kernels, but at
208 209 # present, our own kernel expects this as a command line argument.
209 210 env["IPY_INTERRUPT_EVENT"] = str(interrupt_event)
210 211 if ipython_kernel:
211 212 cmd += [ '--interrupt=%i' % interrupt_event ]
212 213
213 214 # If the kernel is running on pythonw and stdout/stderr are not been
214 215 # re-directed, it will crash when more than 4KB of data is written to
215 216 # stdout or stderr. This is a bug that has been with Python for a very
216 217 # long time; see http://bugs.python.org/issue706263.
217 218 # A cleaner solution to this problem would be to pass os.devnull to
218 219 # Popen directly. Unfortunately, that does not work.
219 220 if cmd[0].endswith('pythonw.exe'):
220 221 if stdout is None:
221 222 cmd.append('--no-stdout')
222 223 if stderr is None:
223 224 cmd.append('--no-stderr')
224 225
225 226 # Launch the kernel process.
226 227 if independent:
227 228 proc = Popen(cmd,
228 229 creationflags=512, # CREATE_NEW_PROCESS_GROUP
229 230 stdin=_stdin, stdout=_stdout, stderr=_stderr, env=os.environ)
230 231 else:
231 232 if ipython_kernel:
232 233 try:
233 234 from _winapi import DuplicateHandle, GetCurrentProcess, \
234 235 DUPLICATE_SAME_ACCESS
235 236 except:
236 237 from _subprocess import DuplicateHandle, GetCurrentProcess, \
237 238 DUPLICATE_SAME_ACCESS
238 239 pid = GetCurrentProcess()
239 240 handle = DuplicateHandle(pid, pid, pid, 0,
240 241 True, # Inheritable by new processes.
241 242 DUPLICATE_SAME_ACCESS)
242 243 cmd +=[ '--parent=%i' % handle ]
243 244
244 245
245 246 proc = Popen(cmd,
246 247 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=os.environ)
247 248
248 249 # Attach the interrupt event to the Popen objet so it can be used later.
249 250 proc.win32_interrupt_event = interrupt_event
250 251
251 252 else:
252 253 if independent:
253 254 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
254 255 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=os.environ)
255 256 else:
256 257 if ipython_kernel:
257 258 cmd += ['--parent=1']
258 259 proc = Popen(cmd,
259 260 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=os.environ)
260 261
261 262 # Clean up pipes created to work around Popen bug.
262 263 if redirect_in:
263 264 if stdin is None:
264 265 proc.stdin.close()
265 266 if redirect_out:
266 267 if stdout is None:
267 268 proc.stdout.close()
268 269 if stderr is None:
269 270 proc.stderr.close()
270 271
271 272 return proc
272 273
273 274 __all__ = [
274 275 'swallow_argv',
275 276 'make_ipkernel_cmd',
276 277 'launch_kernel',
277 278 ]
General Comments 0
You need to be logged in to leave comments. Login now