##// END OF EJS Templates
Merge pull request #7121 from takluyver/windows-int-parent-handle...
Thomas Kluyver -
r19295:abab15a2 merge
parent child Browse files
Show More
@@ -1,226 +1,226 b''
1 1 """Utilities for launching kernels
2 2 """
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import os
8 8 import sys
9 9 from subprocess import Popen, PIPE
10 10
11 11 from IPython.utils.encoding import getdefaultencoding
12 12 from IPython.utils.py3compat import cast_bytes_py2
13 13
14 14
15 15 def swallow_argv(argv, aliases=None, flags=None):
16 16 """strip frontend-specific aliases and flags from an argument list
17 17
18 18 For use primarily in frontend apps that want to pass a subset of command-line
19 19 arguments through to a subprocess, where frontend-specific flags and aliases
20 20 should be removed from the list.
21 21
22 22 Parameters
23 23 ----------
24 24
25 25 argv : list(str)
26 26 The starting argv, to be filtered
27 27 aliases : container of aliases (dict, list, set, etc.)
28 28 The frontend-specific aliases to be removed
29 29 flags : container of flags (dict, list, set, etc.)
30 30 The frontend-specific flags to be removed
31 31
32 32 Returns
33 33 -------
34 34
35 35 argv : list(str)
36 36 The argv list, excluding flags and aliases that have been stripped
37 37 """
38 38
39 39 if aliases is None:
40 40 aliases = set()
41 41 if flags is None:
42 42 flags = set()
43 43
44 44 stripped = list(argv) # copy
45 45
46 46 swallow_next = False
47 47 was_flag = False
48 48 for a in argv:
49 49 if a == '--':
50 50 break
51 51 if swallow_next:
52 52 swallow_next = False
53 53 # last arg was an alias, remove the next one
54 54 # *unless* the last alias has a no-arg flag version, in which
55 55 # case, don't swallow the next arg if it's also a flag:
56 56 if not (was_flag and a.startswith('-')):
57 57 stripped.remove(a)
58 58 continue
59 59 if a.startswith('-'):
60 60 split = a.lstrip('-').split('=')
61 61 name = split[0]
62 62 # we use startswith because argparse accepts any arg to be specified
63 63 # by any leading section, as long as it is unique,
64 64 # so `--no-br` means `--no-browser` in the notebook, etc.
65 65 if any(alias.startswith(name) for alias in aliases):
66 66 stripped.remove(a)
67 67 if len(split) == 1:
68 68 # alias passed with arg via space
69 69 swallow_next = True
70 70 # could have been a flag that matches an alias, e.g. `existing`
71 71 # in which case, we might not swallow the next arg
72 72 was_flag = name in flags
73 73 elif len(split) == 1 and any(flag.startswith(name) for flag in flags):
74 74 # strip flag, but don't swallow next, as flags don't take args
75 75 stripped.remove(a)
76 76
77 77 # return shortened list
78 78 return stripped
79 79
80 80
81 81 def make_ipkernel_cmd(mod='IPython.kernel', executable=None, extra_arguments=[], **kw):
82 82 """Build Popen command list for launching an IPython kernel.
83 83
84 84 Parameters
85 85 ----------
86 86 mod : str, optional (default 'IPython.kernel')
87 87 A string of an IPython module whose __main__ starts an IPython kernel
88 88
89 89 executable : str, optional (default sys.executable)
90 90 The Python executable to use for the kernel process.
91 91
92 92 extra_arguments : list, optional
93 93 A list of extra arguments to pass when executing the launch code.
94 94
95 95 Returns
96 96 -------
97 97
98 98 A Popen command list
99 99 """
100 100 if executable is None:
101 101 executable = sys.executable
102 102 arguments = [ executable, '-m', mod, '-f', '{connection_file}' ]
103 103 arguments.extend(extra_arguments)
104 104
105 105 return arguments
106 106
107 107
108 108 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,
109 109 independent=False,
110 110 cwd=None,
111 111 **kw
112 112 ):
113 113 """ Launches a localhost kernel, binding to the specified ports.
114 114
115 115 Parameters
116 116 ----------
117 117 cmd : Popen list,
118 118 A string of Python code that imports and executes a kernel entry point.
119 119
120 120 stdin, stdout, stderr : optional (default None)
121 121 Standards streams, as defined in subprocess.Popen.
122 122
123 123 independent : bool, optional (default False)
124 124 If set, the kernel process is guaranteed to survive if this process
125 125 dies. If not set, an effort is made to ensure that the kernel is killed
126 126 when this process dies. Note that in this case it is still good practice
127 127 to kill kernels manually before exiting.
128 128
129 129 cwd : path, optional
130 130 The working dir of the kernel process (default: cwd of this process).
131 131
132 132 Returns
133 133 -------
134 134
135 135 Popen instance for the kernel subprocess
136 136 """
137 137
138 138 # Popen will fail (sometimes with a deadlock) if stdin, stdout, and stderr
139 139 # are invalid. Unfortunately, there is in general no way to detect whether
140 140 # they are valid. The following two blocks redirect them to (temporary)
141 141 # pipes in certain important cases.
142 142
143 143 # If this process has been backgrounded, our stdin is invalid. Since there
144 144 # is no compelling reason for the kernel to inherit our stdin anyway, we'll
145 145 # place this one safe and always redirect.
146 146 redirect_in = True
147 147 _stdin = PIPE if stdin is None else stdin
148 148
149 149 # If this process in running on pythonw, we know that stdin, stdout, and
150 150 # stderr are all invalid.
151 151 redirect_out = sys.executable.endswith('pythonw.exe')
152 152 if redirect_out:
153 153 blackhole = open(os.devnull, 'w')
154 154 _stdout = blackhole if stdout is None else stdout
155 155 _stderr = blackhole if stderr is None else stderr
156 156 else:
157 157 _stdout, _stderr = stdout, stderr
158 158
159 159 env = env if (env is not None) else os.environ.copy()
160 160
161 161 encoding = getdefaultencoding(prefer_stream=False)
162 162 kwargs = dict(
163 163 stdin=_stdin,
164 164 stdout=_stdout,
165 165 stderr=_stderr,
166 166 cwd=cwd,
167 167 env=env,
168 168 )
169 169
170 170 # Spawn a kernel.
171 171 if sys.platform == 'win32':
172 172 # Popen on Python 2 on Windows cannot handle unicode args or cwd
173 173 cmd = [ cast_bytes_py2(c, encoding) for c in cmd ]
174 174 if cwd:
175 175 cwd = cast_bytes_py2(cwd, sys.getfilesystemencoding() or 'ascii')
176 176 kwargs['cwd'] = cwd
177 177
178 178 from IPython.kernel.zmq.parentpoller import ParentPollerWindows
179 179 # Create a Win32 event for interrupting the kernel
180 180 # and store it in an environment variable.
181 181 interrupt_event = ParentPollerWindows.create_interrupt_event()
182 182 env["JPY_INTERRUPT_EVENT"] = str(interrupt_event)
183 183 # deprecated old env name:
184 184 env["IPY_INTERRUPT_EVENT"] = env["JPY_INTERRUPT_EVENT"]
185 185
186 186 try:
187 187 from _winapi import DuplicateHandle, GetCurrentProcess, \
188 188 DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP
189 189 except:
190 190 from _subprocess import DuplicateHandle, GetCurrentProcess, \
191 191 DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP
192 192 # Launch the kernel process
193 193 if independent:
194 194 kwargs['creationflags'] = CREATE_NEW_PROCESS_GROUP
195 195 else:
196 196 pid = GetCurrentProcess()
197 197 handle = DuplicateHandle(pid, pid, pid, 0,
198 198 True, # Inheritable by new processes.
199 199 DUPLICATE_SAME_ACCESS)
200 env['JPY_PARENT_PID'] = str(handle)
200 env['JPY_PARENT_PID'] = str(int(handle))
201 201
202 202 proc = Popen(cmd, **kwargs)
203 203
204 204 # Attach the interrupt event to the Popen objet so it can be used later.
205 205 proc.win32_interrupt_event = interrupt_event
206 206
207 207 else:
208 208 if independent:
209 209 kwargs['preexec_fn'] = lambda: os.setsid()
210 210 else:
211 211 env['JPY_PARENT_PID'] = str(os.getpid())
212 212
213 213 proc = Popen(cmd, **kwargs)
214 214
215 215 # Clean up pipes created to work around Popen bug.
216 216 if redirect_in:
217 217 if stdin is None:
218 218 proc.stdin.close()
219 219
220 220 return proc
221 221
222 222 __all__ = [
223 223 'swallow_argv',
224 224 'make_ipkernel_cmd',
225 225 'launch_kernel',
226 226 ]
General Comments 0
You need to be logged in to leave comments. Login now