##// END OF EJS Templates
relax timeouts in Terminal console check for kernel...
MinRK -
Show More
@@ -1,477 +1,484 b''
1 1 # -*- coding: utf-8 -*-
2 2 """terminal client to the IPython kernel
3 3
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (C) 2013 The IPython Development Team
7 7 #
8 8 # Distributed under the terms of the BSD License. The full license is in
9 9 # the file COPYING, distributed as part of this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15 from __future__ import print_function
16 16
17 17 import bdb
18 18 import signal
19 19 import os
20 20 import sys
21 21 import time
22 22 import subprocess
23 23 from io import BytesIO
24 24 import base64
25 25
26 26 from Queue import Empty
27 27
28 28 try:
29 29 from contextlib import nested
30 30 except:
31 31 from IPython.utils.nested_context import nested
32 32
33 33 from IPython.core import page
34 34 from IPython.utils.warn import warn, error
35 35 from IPython.utils import io
36 36 from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode
37 37 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
38 38
39 39 from IPython.terminal.interactiveshell import TerminalInteractiveShell
40 40 from IPython.terminal.console.completer import ZMQCompleter
41 41
42 42
43 43 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
44 44 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
45 45 _executing = False
46 46 _execution_state = Unicode('')
47 47
48 48 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
49 49 config=True, help=
50 50 """
51 51 Handler for image type output. This is useful, for example,
52 52 when connecting to the kernel in which pylab inline backend is
53 53 activated. There are four handlers defined. 'PIL': Use
54 54 Python Imaging Library to popup image; 'stream': Use an
55 55 external program to show the image. Image will be fed into
56 56 the STDIN of the program. You will need to configure
57 57 `stream_image_handler`; 'tempfile': Use an external program to
58 58 show the image. Image will be saved in a temporally file and
59 59 the program is called with the temporally file. You will need
60 60 to configure `tempfile_image_handler`; 'callable': You can set
61 61 any Python callable which is called with the image data. You
62 62 will need to configure `callable_image_handler`.
63 63 """
64 64 )
65 65
66 66 stream_image_handler = List(config=True, help=
67 67 """
68 68 Command to invoke an image viewer program when you are using
69 69 'stream' image handler. This option is a list of string where
70 70 the first element is the command itself and reminders are the
71 71 options for the command. Raw image data is given as STDIN to
72 72 the program.
73 73 """
74 74 )
75 75
76 76 tempfile_image_handler = List(config=True, help=
77 77 """
78 78 Command to invoke an image viewer program when you are using
79 79 'tempfile' image handler. This option is a list of string
80 80 where the first element is the command itself and reminders
81 81 are the options for the command. You can use {file} and
82 82 {format} in the string to represent the location of the
83 83 generated image file and image format.
84 84 """
85 85 )
86 86
87 87 callable_image_handler = Any(config=True, help=
88 88 """
89 89 Callable object called via 'callable' image handler with one
90 90 argument, `data`, which is `msg["content"]["data"]` where
91 91 `msg` is the message from iopub channel. For exmaple, you can
92 92 find base64 encoded PNG data as `data['image/png']`.
93 93 """
94 94 )
95 95
96 96 mime_preference = List(
97 97 default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
98 98 config=True, allow_none=False, help=
99 99 """
100 100 Preferred object representation MIME type in order. First
101 101 matched MIME type will be used.
102 102 """
103 103 )
104 104
105 105 manager = Instance('IPython.kernel.KernelManager')
106 106 client = Instance('IPython.kernel.KernelClient')
107 107 def _client_changed(self, name, old, new):
108 108 self.session_id = new.session.session
109 109 session_id = Unicode()
110 110
111 111 def init_completer(self):
112 112 """Initialize the completion machinery.
113 113
114 114 This creates completion machinery that can be used by client code,
115 115 either interactively in-process (typically triggered by the readline
116 116 library), programatically (such as in test suites) or out-of-prcess
117 117 (typically over the network by remote frontends).
118 118 """
119 119 from IPython.core.completerlib import (module_completer,
120 120 magic_run_completer, cd_completer)
121 121
122 122 self.Completer = ZMQCompleter(self, self.client, config=self.config)
123 123
124 124
125 125 self.set_hook('complete_command', module_completer, str_key = 'import')
126 126 self.set_hook('complete_command', module_completer, str_key = 'from')
127 127 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
128 128 self.set_hook('complete_command', cd_completer, str_key = '%cd')
129 129
130 130 # Only configure readline if we truly are using readline. IPython can
131 131 # do tab-completion over the network, in GUIs, etc, where readline
132 132 # itself may be absent
133 133 if self.has_readline:
134 134 self.set_readline_completer()
135 135
136 136 def run_cell(self, cell, store_history=True):
137 137 """Run a complete IPython cell.
138 138
139 139 Parameters
140 140 ----------
141 141 cell : str
142 142 The code (including IPython code such as %magic functions) to run.
143 143 store_history : bool
144 144 If True, the raw and translated cell will be stored in IPython's
145 145 history. For user code calling back into IPython's machinery, this
146 146 should be set to False.
147 147 """
148 148 if (not cell) or cell.isspace():
149 149 return
150 150
151 151 if cell.strip() == 'exit':
152 152 # explicitly handle 'exit' command
153 153 return self.ask_exit()
154 154
155 155 # flush stale replies, which could have been ignored, due to missed heartbeats
156 156 while self.client.shell_channel.msg_ready():
157 157 self.client.shell_channel.get_msg()
158 158 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
159 159 msg_id = self.client.shell_channel.execute(cell, not store_history)
160 160
161 161 # first thing is wait for any side effects (output, stdin, etc.)
162 162 self._executing = True
163 163 self._execution_state = "busy"
164 164 while self._execution_state != 'idle' and self.client.is_alive():
165 165 try:
166 166 self.handle_stdin_request(msg_id, timeout=0.05)
167 167 except Empty:
168 168 # display intermediate print statements, etc.
169 169 self.handle_iopub(msg_id)
170 170 pass
171 171
172 172 # after all of that is done, wait for the execute reply
173 173 while self.client.is_alive():
174 174 try:
175 175 self.handle_execute_reply(msg_id, timeout=0.05)
176 176 except Empty:
177 177 pass
178 178 else:
179 179 break
180 180 self._executing = False
181 181
182 182 #-----------------
183 183 # message handlers
184 184 #-----------------
185 185
186 186 def handle_execute_reply(self, msg_id, timeout=None):
187 187 msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
188 188 if msg["parent_header"].get("msg_id", None) == msg_id:
189 189
190 190 self.handle_iopub(msg_id)
191 191
192 192 content = msg["content"]
193 193 status = content['status']
194 194
195 195 if status == 'aborted':
196 196 self.write('Aborted\n')
197 197 return
198 198 elif status == 'ok':
199 199 # print execution payloads as well:
200 200 for item in content["payload"]:
201 201 text = item.get('text', None)
202 202 if text:
203 203 page.page(text)
204 204
205 205 elif status == 'error':
206 206 for frame in content["traceback"]:
207 207 print(frame, file=io.stderr)
208 208
209 209 self.execution_count = int(content["execution_count"] + 1)
210 210
211 211
212 212 def handle_iopub(self, msg_id):
213 213 """ Method to process subscribe channel's messages
214 214
215 215 This method consumes and processes messages on the IOPub channel,
216 216 such as stdout, stderr, pyout and status.
217 217
218 218 It only displays output that is caused by the given msg_id
219 219 """
220 220 while self.client.iopub_channel.msg_ready():
221 221 sub_msg = self.client.iopub_channel.get_msg()
222 222 msg_type = sub_msg['header']['msg_type']
223 223 parent = sub_msg["parent_header"]
224 224 if (not parent) or msg_id == parent['msg_id']:
225 225 if msg_type == 'status':
226 226 state = self._execution_state = sub_msg["content"]["execution_state"]
227 227 # idle messages mean an individual sequence is complete,
228 228 # so break out of consumption to allow other things to take over.
229 229 if state == 'idle':
230 230 break
231 231
232 232 elif msg_type == 'stream':
233 233 if sub_msg["content"]["name"] == "stdout":
234 234 print(sub_msg["content"]["data"], file=io.stdout, end="")
235 235 io.stdout.flush()
236 236 elif sub_msg["content"]["name"] == "stderr" :
237 237 print(sub_msg["content"]["data"], file=io.stderr, end="")
238 238 io.stderr.flush()
239 239
240 240 elif msg_type == 'pyout':
241 241 self.execution_count = int(sub_msg["content"]["execution_count"])
242 242 format_dict = sub_msg["content"]["data"]
243 243 self.handle_rich_data(format_dict)
244 244 # taken from DisplayHook.__call__:
245 245 hook = self.displayhook
246 246 hook.start_displayhook()
247 247 hook.write_output_prompt()
248 248 hook.write_format_data(format_dict)
249 249 hook.log_output(format_dict)
250 250 hook.finish_displayhook()
251 251
252 252 elif msg_type == 'display_data':
253 253 self.handle_rich_data(sub_msg["content"]["data"])
254 254
255 255
256 256 _imagemime = {
257 257 'image/png': 'png',
258 258 'image/jpeg': 'jpeg',
259 259 'image/svg+xml': 'svg',
260 260 }
261 261
262 262 def handle_rich_data(self, data):
263 263 for mime in self.mime_preference:
264 264 if mime in data and mime in self._imagemime:
265 265 self.handle_image(data, mime)
266 266 return
267 267
268 268 def handle_image(self, data, mime):
269 269 handler = getattr(
270 270 self, 'handle_image_{0}'.format(self.image_handler), None)
271 271 if handler:
272 272 handler(data, mime)
273 273
274 274 def handle_image_PIL(self, data, mime):
275 275 if mime not in ('image/png', 'image/jpeg'):
276 276 return
277 277 import PIL.Image
278 278 raw = base64.decodestring(data[mime].encode('ascii'))
279 279 img = PIL.Image.open(BytesIO(raw))
280 280 img.show()
281 281
282 282 def handle_image_stream(self, data, mime):
283 283 raw = base64.decodestring(data[mime].encode('ascii'))
284 284 imageformat = self._imagemime[mime]
285 285 fmt = dict(format=imageformat)
286 286 args = [s.format(**fmt) for s in self.stream_image_handler]
287 287 with open(os.devnull, 'w') as devnull:
288 288 proc = subprocess.Popen(
289 289 args, stdin=subprocess.PIPE,
290 290 stdout=devnull, stderr=devnull)
291 291 proc.communicate(raw)
292 292
293 293 def handle_image_tempfile(self, data, mime):
294 294 raw = base64.decodestring(data[mime].encode('ascii'))
295 295 imageformat = self._imagemime[mime]
296 296 filename = 'tmp.{0}'.format(imageformat)
297 297 with nested(NamedFileInTemporaryDirectory(filename),
298 298 open(os.devnull, 'w')) as (f, devnull):
299 299 f.write(raw)
300 300 f.flush()
301 301 fmt = dict(file=f.name, format=imageformat)
302 302 args = [s.format(**fmt) for s in self.tempfile_image_handler]
303 303 subprocess.call(args, stdout=devnull, stderr=devnull)
304 304
305 305 def handle_image_callable(self, data, mime):
306 306 self.callable_image_handler(data)
307 307
308 308 def handle_stdin_request(self, msg_id, timeout=0.1):
309 309 """ Method to capture raw_input
310 310 """
311 311 msg_rep = self.client.stdin_channel.get_msg(timeout=timeout)
312 312 # in case any iopub came while we were waiting:
313 313 self.handle_iopub(msg_id)
314 314 if msg_id == msg_rep["parent_header"].get("msg_id"):
315 315 # wrap SIGINT handler
316 316 real_handler = signal.getsignal(signal.SIGINT)
317 317 def double_int(sig,frame):
318 318 # call real handler (forwards sigint to kernel),
319 319 # then raise local interrupt, stopping local raw_input
320 320 real_handler(sig,frame)
321 321 raise KeyboardInterrupt
322 322 signal.signal(signal.SIGINT, double_int)
323 323
324 324 try:
325 325 raw_data = raw_input(msg_rep["content"]["prompt"])
326 326 except EOFError:
327 327 # turn EOFError into EOF character
328 328 raw_data = '\x04'
329 329 except KeyboardInterrupt:
330 330 sys.stdout.write('\n')
331 331 return
332 332 finally:
333 333 # restore SIGINT handler
334 334 signal.signal(signal.SIGINT, real_handler)
335 335
336 336 # only send stdin reply if there *was not* another request
337 337 # or execution finished while we were reading.
338 338 if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
339 339 self.client.stdin_channel.input(raw_data)
340 340
341 341 def mainloop(self, display_banner=False):
342 342 while True:
343 343 try:
344 344 self.interact(display_banner=display_banner)
345 345 #self.interact_with_readline()
346 346 # XXX for testing of a readline-decoupled repl loop, call
347 347 # interact_with_readline above
348 348 break
349 349 except KeyboardInterrupt:
350 350 # this should not be necessary, but KeyboardInterrupt
351 351 # handling seems rather unpredictable...
352 352 self.write("\nKeyboardInterrupt in interact()\n")
353 353
354 354 def wait_for_kernel(self, timeout=None):
355 355 """method to wait for a kernel to be ready"""
356 356 tic = time.time()
357 357 self.client.hb_channel.unpause()
358 358 while True:
359 self.run_cell('1', False)
360 if self.client.hb_channel.is_beating():
361 # heart failure was not the reason this returned
362 break
363 else:
359 msg_id = self.client.kernel_info()
360 reply = None
361 while True:
362 try:
363 reply = self.client.get_shell_msg(timeout=1)
364 except Empty:
365 break
366 else:
367 if reply['parent_header'].get('msg_id') == msg_id:
368 return True
369 if timeout is not None \
370 and (time.time() - tic) > timeout \
371 and not self.client.hb_channel.is_beating():
364 372 # heart failed
365 if timeout is not None and (time.time() - tic) > timeout:
366 return False
373 return False
367 374 return True
368 375
369 376 def interact(self, display_banner=None):
370 377 """Closely emulate the interactive Python console."""
371 378
372 379 # batch run -> do not interact
373 380 if self.exit_now:
374 381 return
375 382
376 383 if display_banner is None:
377 384 display_banner = self.display_banner
378 385
379 386 if isinstance(display_banner, basestring):
380 387 self.show_banner(display_banner)
381 388 elif display_banner:
382 389 self.show_banner()
383 390
384 391 more = False
385 392
386 393 # run a non-empty no-op, so that we don't get a prompt until
387 394 # we know the kernel is ready. This keeps the connection
388 395 # message above the first prompt.
389 if not self.wait_for_kernel(10):
396 if not self.wait_for_kernel(60):
390 397 error("Kernel did not respond\n")
391 398 return
392 399
393 400 if self.has_readline:
394 401 self.readline_startup_hook(self.pre_readline)
395 402 hlen_b4_cell = self.readline.get_current_history_length()
396 403 else:
397 404 hlen_b4_cell = 0
398 405 # exit_now is set by a call to %Exit or %Quit, through the
399 406 # ask_exit callback.
400 407
401 408 while not self.exit_now:
402 409 if not self.client.is_alive():
403 410 # kernel died, prompt for action or exit
404 411
405 412 action = "restart" if self.manager else "wait for restart"
406 413 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
407 414 if ans:
408 415 if self.manager:
409 416 self.manager.restart_kernel(True)
410 self.wait_for_kernel(10)
417 self.wait_for_kernel(30)
411 418 else:
412 419 self.exit_now = True
413 420 continue
414 421 try:
415 422 # protect prompt block from KeyboardInterrupt
416 423 # when sitting on ctrl-C
417 424 self.hooks.pre_prompt_hook()
418 425 if more:
419 426 try:
420 427 prompt = self.prompt_manager.render('in2')
421 428 except Exception:
422 429 self.showtraceback()
423 430 if self.autoindent:
424 431 self.rl_do_indent = True
425 432
426 433 else:
427 434 try:
428 435 prompt = self.separate_in + self.prompt_manager.render('in')
429 436 except Exception:
430 437 self.showtraceback()
431 438
432 439 line = self.raw_input(prompt)
433 440 if self.exit_now:
434 441 # quick exit on sys.std[in|out] close
435 442 break
436 443 if self.autoindent:
437 444 self.rl_do_indent = False
438 445
439 446 except KeyboardInterrupt:
440 447 #double-guard against keyboardinterrupts during kbdint handling
441 448 try:
442 449 self.write('\nKeyboardInterrupt\n')
443 450 source_raw = self.input_splitter.source_raw_reset()[1]
444 451 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
445 452 more = False
446 453 except KeyboardInterrupt:
447 454 pass
448 455 except EOFError:
449 456 if self.autoindent:
450 457 self.rl_do_indent = False
451 458 if self.has_readline:
452 459 self.readline_startup_hook(None)
453 460 self.write('\n')
454 461 self.exit()
455 462 except bdb.BdbQuit:
456 463 warn('The Python debugger has exited with a BdbQuit exception.\n'
457 464 'Because of how pdb handles the stack, it is impossible\n'
458 465 'for IPython to properly format this particular exception.\n'
459 466 'IPython will resume normal operation.')
460 467 except:
461 468 # exceptions here are VERY RARE, but they can be triggered
462 469 # asynchronously by signal handlers, for example.
463 470 self.showtraceback()
464 471 else:
465 472 self.input_splitter.push(line)
466 473 more = self.input_splitter.push_accepts_more()
467 474 if (self.SyntaxTB.last_syntax_error and
468 475 self.autoedit_syntax):
469 476 self.edit_syntax_error()
470 477 if not more:
471 478 source_raw = self.input_splitter.source_raw_reset()[1]
472 479 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
473 480 self.run_cell(source_raw)
474 481
475 482
476 483 # Turn off the exit flag, so the mainloop can be restarted if desired
477 484 self.exit_now = False
General Comments 0
You need to be logged in to leave comments. Login now