##// END OF EJS Templates
Merge pull request #420 from epatters/kernel-stream-redirect...
Evan Patterson -
r3845:8879791f merge
parent child Browse files
Show More
@@ -23,6 +23,7 b' from iostream import OutStream'
23 from parentpoller import ParentPollerUnix, ParentPollerWindows
23 from parentpoller import ParentPollerUnix, ParentPollerWindows
24 from session import Session
24 from session import Session
25
25
26
26 def bind_port(socket, ip, port):
27 def bind_port(socket, ip, port):
27 """ Binds the specified ZMQ socket. If the port is zero, a random port is
28 """ Binds the specified ZMQ socket. If the port is zero, a random port is
28 chosen. Returns the port that was bound.
29 chosen. Returns the port that was bound.
@@ -51,6 +52,10 b' def make_argument_parser():'
51 help='set the REQ channel port [default: random]')
52 help='set the REQ channel port [default: random]')
52 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
53 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
53 help='set the heartbeat port [default: random]')
54 help='set the heartbeat port [default: random]')
55 parser.add_argument('--no-stdout', action='store_true',
56 help='redirect stdout to the null device')
57 parser.add_argument('--no-stderr', action='store_true',
58 help='redirect stderr to the null device')
54
59
55 if sys.platform == 'win32':
60 if sys.platform == 'win32':
56 parser.add_argument('--interrupt', type=int, metavar='HANDLE',
61 parser.add_argument('--interrupt', type=int, metavar='HANDLE',
@@ -71,13 +76,13 b' def make_kernel(namespace, kernel_factory,'
71 """ Creates a kernel, redirects stdout/stderr, and installs a display hook
76 """ Creates a kernel, redirects stdout/stderr, and installs a display hook
72 and exception handler.
77 and exception handler.
73 """
78 """
74 # If running under pythonw.exe, the interpreter will crash if more than 4KB
79 # Re-direct stdout/stderr, if necessary.
75 # of data is written to stdout or stderr. This is a bug that has been with
80 if namespace.no_stdout or namespace.no_stderr:
76 # Python for a very long time; see http://bugs.python.org/issue706263.
77 if sys.executable.endswith('pythonw.exe'):
78 blackhole = file(os.devnull, 'w')
81 blackhole = file(os.devnull, 'w')
79 sys.stdout = sys.stderr = blackhole
82 if namespace.no_stdout:
80 sys.__stdout__ = sys.__stderr__ = blackhole
83 sys.stdout = sys.__stdout__ = blackhole
84 if namespace.no_stderr:
85 sys.stderr = sys.__stderr__ = blackhole
81
86
82 # Install minimal exception handling
87 # Install minimal exception handling
83 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
88 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
@@ -155,6 +160,7 b' def make_default_main(kernel_factory):'
155
160
156
161
157 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
162 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
163 stdin=None, stdout=None, stderr=None,
158 executable=None, independent=False, extra_arguments=[]):
164 executable=None, independent=False, extra_arguments=[]):
159 """ Launches a localhost kernel, binding to the specified ports.
165 """ Launches a localhost kernel, binding to the specified ports.
160
166
@@ -175,6 +181,9 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
175 hb_port : int, optional
181 hb_port : int, optional
176 The port to use for the hearbeat REP channel.
182 The port to use for the hearbeat REP channel.
177
183
184 stdin, stdout, stderr : optional (default None)
185 Standards streams, as defined in subprocess.Popen.
186
178 executable : str, optional (default sys.executable)
187 executable : str, optional (default sys.executable)
179 The Python executable to use for the kernel process.
188 The Python executable to use for the kernel process.
180
189
@@ -228,15 +237,35 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
228 interrupt_event = ParentPollerWindows.create_interrupt_event()
237 interrupt_event = ParentPollerWindows.create_interrupt_event()
229 arguments += [ '--interrupt', str(int(interrupt_event)) ]
238 arguments += [ '--interrupt', str(int(interrupt_event)) ]
230
239
231 # If using pythonw, stdin, stdout, and stderr are invalid. Popen will
240 # If this process in running on pythonw, stdin, stdout, and stderr are
232 # fail unless they are suitably redirected. We don't read from the
241 # invalid. Popen will fail unless they are suitably redirected. We don't
233 # pipes, but they must exist.
242 # read from the pipes, but they must exist.
234 redirect = PIPE if executable.endswith('pythonw.exe') else None
243 if sys.executable.endswith('pythonw.exe'):
235
244 redirect = True
245 _stdin = PIPE if stdin is None else stdin
246 _stdout = PIPE if stdout is None else stdout
247 _stderr = PIPE if stderr is None else stderr
248 else:
249 redirect = False
250 _stdin, _stdout, _stderr = stdin, stdout, stderr
251
252 # If the kernel is running on pythonw and stdout/stderr are not been
253 # re-directed, it will crash when more than 4KB of data is written to
254 # stdout or stderr. This is a bug that has been with Python for a very
255 # long time; see http://bugs.python.org/issue706263.
256 # A cleaner solution to this problem would be to pass os.devnull to
257 # Popen directly. Unfortunately, that does not work.
258 if executable.endswith('pythonw.exe'):
259 if stdout is None:
260 arguments.append('--no-stdout')
261 if stderr is None:
262 arguments.append('--no-stderr')
263
264 # Launch the kernel process.
236 if independent:
265 if independent:
237 proc = Popen(arguments,
266 proc = Popen(arguments,
238 creationflags=512, # CREATE_NEW_PROCESS_GROUP
267 creationflags=512, # CREATE_NEW_PROCESS_GROUP
239 stdout=redirect, stderr=redirect, stdin=redirect)
268 stdin=_stdin, stdout=_stdout, stderr=_stderr)
240 else:
269 else:
241 from _subprocess import DuplicateHandle, GetCurrentProcess, \
270 from _subprocess import DuplicateHandle, GetCurrentProcess, \
242 DUPLICATE_SAME_ACCESS
271 DUPLICATE_SAME_ACCESS
@@ -245,21 +274,26 b' def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
245 True, # Inheritable by new processes.
274 True, # Inheritable by new processes.
246 DUPLICATE_SAME_ACCESS)
275 DUPLICATE_SAME_ACCESS)
247 proc = Popen(arguments + ['--parent', str(int(handle))],
276 proc = Popen(arguments + ['--parent', str(int(handle))],
248 stdout=redirect, stderr=redirect, stdin=redirect)
277 stdin=_stdin, stdout=_stdout, stderr=_stderr)
249
278
250 # Attach the interrupt event to the Popen objet so it can be used later.
279 # Attach the interrupt event to the Popen objet so it can be used later.
251 proc.win32_interrupt_event = interrupt_event
280 proc.win32_interrupt_event = interrupt_event
252
281
253 # Clean up pipes created to work around Popen bug.
282 # Clean up pipes created to work around Popen bug.
254 if redirect is not None:
283 if redirect:
255 proc.stdout.close()
284 if stdin is None:
256 proc.stderr.close()
285 proc.stdin.close()
257 proc.stdin.close()
286 if stdout is None:
287 proc.stdout.close()
288 if stderr is None:
289 proc.stderr.close()
258
290
259 else:
291 else:
260 if independent:
292 if independent:
261 proc = Popen(arguments, preexec_fn=lambda: os.setsid())
293 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
294 stdin=stdin, stdout=stdout, stderr=stderr)
262 else:
295 else:
263 proc = Popen(arguments + ['--parent'])
296 proc = Popen(arguments + ['--parent'],
297 stdin=stdin, stdout=stdout, stderr=stderr)
264
298
265 return proc, xrep_port, pub_port, req_port, hb_port
299 return proc, xrep_port, pub_port, req_port, hb_port
@@ -568,6 +568,7 b' class GTKKernel(Kernel):'
568 #-----------------------------------------------------------------------------
568 #-----------------------------------------------------------------------------
569
569
570 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
570 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
571 stdin=None, stdout=None, stderr=None,
571 executable=None, independent=False, pylab=False, colors=None):
572 executable=None, independent=False, pylab=False, colors=None):
572 """Launches a localhost kernel, binding to the specified ports.
573 """Launches a localhost kernel, binding to the specified ports.
573
574
@@ -588,6 +589,9 b' def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
588 hb_port : int, optional
589 hb_port : int, optional
589 The port to use for the hearbeat REP channel.
590 The port to use for the hearbeat REP channel.
590
591
592 stdin, stdout, stderr : optional (default None)
593 Standards streams, as defined in subprocess.Popen.
594
591 executable : str, optional (default sys.executable)
595 executable : str, optional (default sys.executable)
592 The Python executable to use for the kernel process.
596 The Python executable to use for the kernel process.
593
597
@@ -625,6 +629,7 b' def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
625 extra_arguments.append(colors)
629 extra_arguments.append(colors)
626 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
630 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
627 xrep_port, pub_port, req_port, hb_port,
631 xrep_port, pub_port, req_port, hb_port,
632 stdin, stdout, stderr,
628 executable, independent, extra_arguments)
633 executable, independent, extra_arguments)
629
634
630
635
@@ -590,7 +590,8 b' class HBSocketChannel(ZmqSocketChannel):'
590 # list, poll is working correctly even if it
590 # list, poll is working correctly even if it
591 # returns quickly. Note: poll timeout is in
591 # returns quickly. Note: poll timeout is in
592 # milliseconds.
592 # milliseconds.
593 self.poller.poll(1000*until_dead)
593 if until_dead > 0.0:
594 self.poller.poll(1000 * until_dead)
594
595
595 since_last_heartbeat = time.time()-request_time
596 since_last_heartbeat = time.time()-request_time
596 if since_last_heartbeat > self.time_to_dead:
597 if since_last_heartbeat > self.time_to_dead:
@@ -852,8 +853,15 b' class KernelManager(HasTraits):'
852 except OSError, e:
853 except OSError, e:
853 # In Windows, we will get an Access Denied error if the process
854 # In Windows, we will get an Access Denied error if the process
854 # has already terminated. Ignore it.
855 # has already terminated. Ignore it.
855 if not (sys.platform == 'win32' and e.winerror == 5):
856 if sys.platform == 'win32':
856 raise
857 if e.winerror != 5:
858 raise
859 # On Unix, we may get an ESRCH error if the process has already
860 # terminated. Ignore it.
861 else:
862 from errno import ESRCH
863 if e.errno != ESRCH:
864 raise
857 self.kernel = None
865 self.kernel = None
858 else:
866 else:
859 raise RuntimeError("Cannot kill kernel. No kernel is running!")
867 raise RuntimeError("Cannot kill kernel. No kernel is running!")
@@ -248,6 +248,7 b' class Kernel(HasTraits):'
248 #-----------------------------------------------------------------------------
248 #-----------------------------------------------------------------------------
249
249
250 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
250 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
251 stdin=None, stdout=None, stderr=None,
251 executable=None, independent=False):
252 executable=None, independent=False):
252 """ Launches a localhost kernel, binding to the specified ports.
253 """ Launches a localhost kernel, binding to the specified ports.
253
254
@@ -268,6 +269,9 b' def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
268 hb_port : int, optional
269 hb_port : int, optional
269 The port to use for the hearbeat REP channel.
270 The port to use for the hearbeat REP channel.
270
271
272 stdin, stdout, stderr : optional (default None)
273 Standards streams, as defined in subprocess.Popen.
274
271 executable : str, optional (default sys.executable)
275 executable : str, optional (default sys.executable)
272 The Python executable to use for the kernel process.
276 The Python executable to use for the kernel process.
273
277
@@ -291,8 +295,8 b' def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,'
291
295
292 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
296 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
293 xrep_port, pub_port, req_port, hb_port,
297 xrep_port, pub_port, req_port, hb_port,
294 executable, independent,
298 stdin, stdout, stderr,
295 extra_arguments=extra_arguments)
299 executable, independent, extra_arguments)
296
300
297 main = make_default_main(Kernel)
301 main = make_default_main(Kernel)
298
302
General Comments 0
You need to be logged in to leave comments. Login now