##// END OF EJS Templates
Merge pull request #7099 from minrk/parent-env...
Thomas Kluyver -
r19268:9c06d407 merge
parent child Browse files
Show More
@@ -101,25 +101,13 b" def make_ipkernel_cmd(mod='IPython.kernel', executable=None, extra_arguments=[],"
101 executable = sys.executable
101 executable = sys.executable
102 arguments = [ executable, '-m', mod, '-f', '{connection_file}' ]
102 arguments = [ executable, '-m', mod, '-f', '{connection_file}' ]
103 arguments.extend(extra_arguments)
103 arguments.extend(extra_arguments)
104
104
105 if sys.platform == 'win32':
106
107 # If the kernel is running on pythonw and stdout/stderr are not been
108 # re-directed, it will crash when more than 4KB of data is written to
109 # stdout or stderr. This is a bug that has been with Python for a very
110 # long time; see http://bugs.python.org/issue706263.
111 # A cleaner solution to this problem would be to pass os.devnull to
112 # Popen directly. Unfortunately, that does not work.
113 if executable.endswith('pythonw.exe'):
114 arguments.append('--no-stdout')
115 arguments.append('--no-stderr')
116
117 return arguments
105 return arguments
118
106
119
107
120 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,
108 def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,
121 independent=False,
109 independent=False,
122 cwd=None, ipython_kernel=True,
110 cwd=None,
123 **kw
111 **kw
124 ):
112 ):
125 """ Launches a localhost kernel, binding to the specified ports.
113 """ Launches a localhost kernel, binding to the specified ports.
@@ -141,10 +129,6 b' def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,'
141 cwd : path, optional
129 cwd : path, optional
142 The working dir of the kernel process (default: cwd of this process).
130 The working dir of the kernel process (default: cwd of this process).
143
131
144 ipython_kernel : bool, optional
145 Whether the kernel is an official IPython one,
146 and should get a bit of special treatment.
147
148 Returns
132 Returns
149 -------
133 -------
150
134
@@ -166,14 +150,22 b' def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,'
166 # stderr are all invalid.
150 # stderr are all invalid.
167 redirect_out = sys.executable.endswith('pythonw.exe')
151 redirect_out = sys.executable.endswith('pythonw.exe')
168 if redirect_out:
152 if redirect_out:
169 _stdout = PIPE if stdout is None else stdout
153 blackhole = open(os.devnull, 'w')
170 _stderr = PIPE if stderr is None else stderr
154 _stdout = blackhole if stdout is None else stdout
155 _stderr = blackhole if stderr is None else stderr
171 else:
156 else:
172 _stdout, _stderr = stdout, stderr
157 _stdout, _stderr = stdout, stderr
173
158
174 env = env if (env is not None) else os.environ.copy()
159 env = env if (env is not None) else os.environ.copy()
175
160
176 encoding = getdefaultencoding(prefer_stream=False)
161 encoding = getdefaultencoding(prefer_stream=False)
162 kwargs = dict(
163 stdin=_stdin,
164 stdout=_stdout,
165 stderr=_stderr,
166 cwd=cwd,
167 env=env,
168 )
177
169
178 # Spawn a kernel.
170 # Spawn a kernel.
179 if sys.platform == 'win32':
171 if sys.platform == 'win32':
@@ -181,73 +173,49 b' def launch_kernel(cmd, stdin=None, stdout=None, stderr=None, env=None,'
181 cmd = [ cast_bytes_py2(c, encoding) for c in cmd ]
173 cmd = [ cast_bytes_py2(c, encoding) for c in cmd ]
182 if cwd:
174 if cwd:
183 cwd = cast_bytes_py2(cwd, sys.getfilesystemencoding() or 'ascii')
175 cwd = cast_bytes_py2(cwd, sys.getfilesystemencoding() or 'ascii')
176 kwargs['cwd'] = cwd
184
177
185 from IPython.kernel.zmq.parentpoller import ParentPollerWindows
178 from IPython.kernel.zmq.parentpoller import ParentPollerWindows
186 # Create a Win32 event for interrupting the kernel.
179 # Create a Win32 event for interrupting the kernel
180 # and store it in an environment variable.
187 interrupt_event = ParentPollerWindows.create_interrupt_event()
181 interrupt_event = ParentPollerWindows.create_interrupt_event()
188 # Store this in an environment variable for third party kernels, but at
182 env["JPY_INTERRUPT_EVENT"] = str(interrupt_event)
189 # present, our own kernel expects this as a command line argument.
183 # deprecated old env name:
190 env["IPY_INTERRUPT_EVENT"] = str(interrupt_event)
184 env["IPY_INTERRUPT_EVENT"] = env["JPY_INTERRUPT_EVENT"]
191 if ipython_kernel:
185
192 cmd += [ '--interrupt=%i' % interrupt_event ]
186 try:
193
187 from _winapi import DuplicateHandle, GetCurrentProcess, \
194 # If the kernel is running on pythonw and stdout/stderr are not been
188 DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP
195 # re-directed, it will crash when more than 4KB of data is written to
189 except:
196 # stdout or stderr. This is a bug that has been with Python for a very
190 from _subprocess import DuplicateHandle, GetCurrentProcess, \
197 # long time; see http://bugs.python.org/issue706263.
191 DUPLICATE_SAME_ACCESS, CREATE_NEW_PROCESS_GROUP
198 # A cleaner solution to this problem would be to pass os.devnull to
192 # Launch the kernel process
199 # Popen directly. Unfortunately, that does not work.
200 if cmd[0].endswith('pythonw.exe'):
201 if stdout is None:
202 cmd.append('--no-stdout')
203 if stderr is None:
204 cmd.append('--no-stderr')
205
206 # Launch the kernel process.
207 if independent:
193 if independent:
208 proc = Popen(cmd,
194 kwargs['creationflags'] = CREATE_NEW_PROCESS_GROUP
209 creationflags=512, # CREATE_NEW_PROCESS_GROUP
210 stdin=_stdin, stdout=_stdout, stderr=_stderr, env=env)
211 else:
195 else:
212 if ipython_kernel:
196 pid = GetCurrentProcess()
213 try:
197 handle = DuplicateHandle(pid, pid, pid, 0,
214 from _winapi import DuplicateHandle, GetCurrentProcess, \
198 True, # Inheritable by new processes.
215 DUPLICATE_SAME_ACCESS
199 DUPLICATE_SAME_ACCESS)
216 except:
200 env['JPY_PARENT_PID'] = str(handle)
217 from _subprocess import DuplicateHandle, GetCurrentProcess, \
201
218 DUPLICATE_SAME_ACCESS
202 proc = Popen(cmd, **kwargs)
219 pid = GetCurrentProcess()
220 handle = DuplicateHandle(pid, pid, pid, 0,
221 True, # Inheritable by new processes.
222 DUPLICATE_SAME_ACCESS)
223 cmd +=[ '--parent=%i' % handle ]
224
225
226 proc = Popen(cmd,
227 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env)
228
203
229 # Attach the interrupt event to the Popen objet so it can be used later.
204 # Attach the interrupt event to the Popen objet so it can be used later.
230 proc.win32_interrupt_event = interrupt_event
205 proc.win32_interrupt_event = interrupt_event
231
206
232 else:
207 else:
233 if independent:
208 if independent:
234 proc = Popen(cmd, preexec_fn=lambda: os.setsid(),
209 kwargs['preexec_fn'] = lambda: os.setsid()
235 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env)
236 else:
210 else:
237 if ipython_kernel:
211 env['JPY_PARENT_PID'] = str(os.getpid())
238 cmd += ['--parent=1']
212
239 proc = Popen(cmd,
213 proc = Popen(cmd, **kwargs)
240 stdin=_stdin, stdout=_stdout, stderr=_stderr, cwd=cwd, env=env)
241
214
242 # Clean up pipes created to work around Popen bug.
215 # Clean up pipes created to work around Popen bug.
243 if redirect_in:
216 if redirect_in:
244 if stdin is None:
217 if stdin is None:
245 proc.stdin.close()
218 proc.stdin.close()
246 if redirect_out:
247 if stdout is None:
248 proc.stdout.close()
249 if stderr is None:
250 proc.stderr.close()
251
219
252 return proc
220 return proc
253
221
@@ -237,7 +237,6 b' class KernelManager(ConnectionFileMixin):'
237 env.update(self.kernel_spec.env or {})
237 env.update(self.kernel_spec.env or {})
238 # launch the kernel subprocess
238 # launch the kernel subprocess
239 self.kernel = self._launch_kernel(kernel_cmd, env=env,
239 self.kernel = self._launch_kernel(kernel_cmd, env=env,
240 ipython_kernel=self.ipython_kernel,
241 **kw)
240 **kw)
242 self.start_restarter()
241 self.start_restarter()
243 self._connect_control_socket()
242 self._connect_control_socket()
@@ -53,11 +53,8 b' kernel_aliases.update({'
53 'stdin' : 'IPKernelApp.stdin_port',
53 'stdin' : 'IPKernelApp.stdin_port',
54 'control' : 'IPKernelApp.control_port',
54 'control' : 'IPKernelApp.control_port',
55 'f' : 'IPKernelApp.connection_file',
55 'f' : 'IPKernelApp.connection_file',
56 'parent': 'IPKernelApp.parent_handle',
57 'transport': 'IPKernelApp.transport',
56 'transport': 'IPKernelApp.transport',
58 })
57 })
59 if sys.platform.startswith('win'):
60 kernel_aliases['interrupt'] = 'IPKernelApp.interrupt'
61
58
62 kernel_flags = dict(base_flags)
59 kernel_flags = dict(base_flags)
63 kernel_flags.update({
60 kernel_flags.update({
@@ -133,11 +130,11 b' class IPKernelApp(BaseIPythonApplication, InteractiveShellApp,'
133 config=True, help="The importstring for the DisplayHook factory")
130 config=True, help="The importstring for the DisplayHook factory")
134
131
135 # polling
132 # polling
136 parent_handle = Integer(0, config=True,
133 parent_handle = Integer(int(os.environ.get('JPY_PARENT_PID') or 0), config=True,
137 help="""kill this process if its parent dies. On Windows, the argument
134 help="""kill this process if its parent dies. On Windows, the argument
138 specifies the HANDLE of the parent process, otherwise it is simply boolean.
135 specifies the HANDLE of the parent process, otherwise it is simply boolean.
139 """)
136 """)
140 interrupt = Integer(0, config=True,
137 interrupt = Integer(int(os.environ.get('JPY_INTERRUPT_EVENT') or 0), config=True,
141 help="""ONLY USED ON WINDOWS
138 help="""ONLY USED ON WINDOWS
142 Interrupt this process when the parent is signaled.
139 Interrupt this process when the parent is signaled.
143 """)
140 """)
General Comments 0
You need to be logged in to leave comments. Login now