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