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