##// END OF EJS Templates
Merge pull request #8483 from Carreau/keep-kernel...
Thomas Kluyver -
r21403:a4c5f92a merge
parent child Browse files
Show More
@@ -1,149 +1,150 b''
1 1 """ A minimal application using the ZMQ-based terminal IPython frontend.
2 2
3 3 This is not a complete console app, as subprocess will not be able to receive
4 4 input, there is no real readline support, among other limitations.
5 5
6 6 Authors:
7 7
8 8 * Min RK
9 9 * Paul Ivanov
10 10
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 import signal
17 17
18 18 from IPython.terminal.ipapp import TerminalIPythonApp, frontend_flags as term_flags
19 19
20 20 from IPython.utils.traitlets import (
21 21 Dict, Any
22 22 )
23 23 from IPython.utils.warn import error
24 24
25 25 from IPython.consoleapp import (
26 26 IPythonConsoleApp, app_aliases, app_flags, aliases, flags
27 27 )
28 28
29 29 from IPython.terminal.console.interactiveshell import ZMQTerminalInteractiveShell
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Globals
33 33 #-----------------------------------------------------------------------------
34 34
35 35 _examples = """
36 36 ipython console # start the ZMQ-based console
37 37 ipython console --existing # connect to an existing ipython session
38 38 """
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Flags and Aliases
42 42 #-----------------------------------------------------------------------------
43 43
44 44 # copy flags from mixin:
45 45 flags = dict(flags)
46 46 # start with mixin frontend flags:
47 47 frontend_flags = dict(app_flags)
48 48 # add TerminalIPApp flags:
49 49 frontend_flags.update(term_flags)
50 50 # disable quick startup, as it won't propagate to the kernel anyway
51 51 frontend_flags.pop('quick')
52 52 # update full dict with frontend flags:
53 53 flags.update(frontend_flags)
54 54
55 55 # copy flags from mixin
56 56 aliases = dict(aliases)
57 57 # start with mixin frontend flags
58 58 frontend_aliases = dict(app_aliases)
59 59 # load updated frontend flags into full dict
60 60 aliases.update(frontend_aliases)
61 61
62 62 # get flags&aliases into sets, and remove a couple that
63 63 # shouldn't be scrubbed from backend flags:
64 64 frontend_aliases = set(frontend_aliases.keys())
65 65 frontend_flags = set(frontend_flags.keys())
66 66
67 67
68 68 #-----------------------------------------------------------------------------
69 69 # Classes
70 70 #-----------------------------------------------------------------------------
71 71
72 72
73 73 class ZMQTerminalIPythonApp(TerminalIPythonApp, IPythonConsoleApp):
74 74 name = "ipython-console"
75 75 """Start a terminal frontend to the IPython zmq kernel."""
76 76
77 77 description = """
78 78 The IPython terminal-based Console.
79 79
80 80 This launches a Console application inside a terminal.
81 81
82 82 The Console supports various extra features beyond the traditional
83 83 single-process Terminal IPython shell, such as connecting to an
84 84 existing ipython session, via:
85 85
86 86 ipython console --existing
87 87
88 88 where the previous session could have been created by another ipython
89 89 console, an ipython qtconsole, or by opening an ipython notebook.
90 90
91 91 """
92 92 examples = _examples
93 93
94 94 classes = [ZMQTerminalInteractiveShell] + IPythonConsoleApp.classes
95 95 flags = Dict(flags)
96 96 aliases = Dict(aliases)
97 97 frontend_aliases = Any(frontend_aliases)
98 98 frontend_flags = Any(frontend_flags)
99 99
100 100 subcommands = Dict()
101 101
102 102 def parse_command_line(self, argv=None):
103 103 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
104 104 self.build_kernel_argv(argv)
105 105
106 106 def init_shell(self):
107 107 IPythonConsoleApp.initialize(self)
108 108 # relay sigint to kernel
109 109 signal.signal(signal.SIGINT, self.handle_sigint)
110 110 self.shell = ZMQTerminalInteractiveShell.instance(parent=self,
111 111 display_banner=False, profile_dir=self.profile_dir,
112 112 ipython_dir=self.ipython_dir,
113 113 manager=self.kernel_manager,
114 114 client=self.kernel_client,
115 115 )
116 self.shell.own_kernel = not self.existing
116 117
117 118 def init_gui_pylab(self):
118 119 # no-op, because we don't want to import matplotlib in the frontend.
119 120 pass
120 121
121 122 def handle_sigint(self, *args):
122 123 if self.shell._executing:
123 124 if self.kernel_manager:
124 125 # interrupt already gets passed to subprocess by signal handler.
125 126 # Only if we prevent that should we need to explicitly call
126 127 # interrupt_kernel, until which time, this would result in a
127 128 # double-interrupt:
128 129 # self.kernel_manager.interrupt_kernel()
129 130 pass
130 131 else:
131 132 self.shell.write_err('\n')
132 133 error("Cannot interrupt kernels we didn't start.\n")
133 134 else:
134 135 # raise the KeyboardInterrupt if we aren't waiting for execution,
135 136 # so that the interact loop advances, and prompt is redrawn, etc.
136 137 raise KeyboardInterrupt
137 138
138 139
139 140 def init_code(self):
140 141 # no-op in the frontend, code gets run in the backend
141 142 pass
142 143
143 144
144 145 launch_new_instance = ZMQTerminalIPythonApp.launch_instance
145 146
146 147
147 148 if __name__ == '__main__':
148 149 launch_new_instance()
149 150
@@ -1,585 +1,595 b''
1 1 # -*- coding: utf-8 -*-
2 2 """terminal client to the IPython kernel"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 from __future__ import print_function
8 8
9 9 import base64
10 10 import bdb
11 11 import errno
12 12 import signal
13 13 import os
14 14 import sys
15 15 import time
16 16 import subprocess
17 17 from getpass import getpass
18 18 from io import BytesIO
19 19
20 20 try:
21 21 from queue import Empty # Py 3
22 22 except ImportError:
23 23 from Queue import Empty # Py 2
24 24
25 25 from zmq import ZMQError
26 26
27 27 from IPython.core import page
28 28 from IPython.core import release
29 29 from IPython.terminal.console.zmqhistory import ZMQHistoryManager
30 30 from IPython.utils.warn import warn, error
31 31 from IPython.utils import io
32 32 from IPython.utils.py3compat import string_types, input
33 33 from IPython.utils.traitlets import List, Enum, Any, Instance, Unicode, Float, Bool
34 34 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
35 35
36 36 from IPython.terminal.interactiveshell import TerminalInteractiveShell
37 37 from IPython.terminal.console.completer import ZMQCompleter
38 38
39 39 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
40 40 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
41 41 _executing = False
42 42 _execution_state = Unicode('')
43 43 _pending_clearoutput = False
44 44 kernel_banner = Unicode('')
45 45 kernel_timeout = Float(60, config=True,
46 46 help="""Timeout for giving up on a kernel (in seconds).
47 47
48 48 On first connect and restart, the console tests whether the
49 49 kernel is running and responsive by sending kernel_info_requests.
50 50 This sets the timeout in seconds for how long the kernel can take
51 51 before being presumed dead.
52 52 """
53 53 )
54 54
55 55 image_handler = Enum(('PIL', 'stream', 'tempfile', 'callable'),
56 56 config=True, help=
57 57 """
58 58 Handler for image type output. This is useful, for example,
59 59 when connecting to the kernel in which pylab inline backend is
60 60 activated. There are four handlers defined. 'PIL': Use
61 61 Python Imaging Library to popup image; 'stream': Use an
62 62 external program to show the image. Image will be fed into
63 63 the STDIN of the program. You will need to configure
64 64 `stream_image_handler`; 'tempfile': Use an external program to
65 65 show the image. Image will be saved in a temporally file and
66 66 the program is called with the temporally file. You will need
67 67 to configure `tempfile_image_handler`; 'callable': You can set
68 68 any Python callable which is called with the image data. You
69 69 will need to configure `callable_image_handler`.
70 70 """
71 71 )
72 72
73 73 stream_image_handler = List(config=True, help=
74 74 """
75 75 Command to invoke an image viewer program when you are using
76 76 'stream' image handler. This option is a list of string where
77 77 the first element is the command itself and reminders are the
78 78 options for the command. Raw image data is given as STDIN to
79 79 the program.
80 80 """
81 81 )
82 82
83 83 tempfile_image_handler = List(config=True, help=
84 84 """
85 85 Command to invoke an image viewer program when you are using
86 86 'tempfile' image handler. This option is a list of string
87 87 where the first element is the command itself and reminders
88 88 are the options for the command. You can use {file} and
89 89 {format} in the string to represent the location of the
90 90 generated image file and image format.
91 91 """
92 92 )
93 93
94 94 callable_image_handler = Any(config=True, help=
95 95 """
96 96 Callable object called via 'callable' image handler with one
97 97 argument, `data`, which is `msg["content"]["data"]` where
98 98 `msg` is the message from iopub channel. For exmaple, you can
99 99 find base64 encoded PNG data as `data['image/png']`.
100 100 """
101 101 )
102 102
103 103 mime_preference = List(
104 104 default_value=['image/png', 'image/jpeg', 'image/svg+xml'],
105 105 config=True, allow_none=False, help=
106 106 """
107 107 Preferred object representation MIME type in order. First
108 108 matched MIME type will be used.
109 109 """
110 110 )
111 111
112 112 manager = Instance('IPython.kernel.KernelManager')
113 113 client = Instance('IPython.kernel.KernelClient')
114 114 def _client_changed(self, name, old, new):
115 115 self.session_id = new.session.session
116 116 session_id = Unicode()
117 117
118 118 def init_completer(self):
119 119 """Initialize the completion machinery.
120 120
121 121 This creates completion machinery that can be used by client code,
122 122 either interactively in-process (typically triggered by the readline
123 123 library), programmatically (such as in test suites) or out-of-process
124 124 (typically over the network by remote frontends).
125 125 """
126 126 from IPython.core.completerlib import (module_completer,
127 127 magic_run_completer, cd_completer)
128 128
129 129 self.Completer = ZMQCompleter(self, self.client, config=self.config)
130 130
131 131
132 132 self.set_hook('complete_command', module_completer, str_key = 'import')
133 133 self.set_hook('complete_command', module_completer, str_key = 'from')
134 134 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
135 135 self.set_hook('complete_command', cd_completer, str_key = '%cd')
136 136
137 137 # Only configure readline if we truly are using readline. IPython can
138 138 # do tab-completion over the network, in GUIs, etc, where readline
139 139 # itself may be absent
140 140 if self.has_readline:
141 141 self.set_readline_completer()
142 142
143 143 def run_cell(self, cell, store_history=True):
144 144 """Run a complete IPython cell.
145 145
146 146 Parameters
147 147 ----------
148 148 cell : str
149 149 The code (including IPython code such as %magic functions) to run.
150 150 store_history : bool
151 151 If True, the raw and translated cell will be stored in IPython's
152 152 history. For user code calling back into IPython's machinery, this
153 153 should be set to False.
154 154 """
155 155 if (not cell) or cell.isspace():
156 156 # pressing enter flushes any pending display
157 157 self.handle_iopub()
158 158 return
159 159
160 160 # flush stale replies, which could have been ignored, due to missed heartbeats
161 161 while self.client.shell_channel.msg_ready():
162 162 self.client.shell_channel.get_msg()
163 163 # execute takes 'hidden', which is the inverse of store_hist
164 164 msg_id = self.client.execute(cell, not store_history)
165 165
166 166 # first thing is wait for any side effects (output, stdin, etc.)
167 167 self._executing = True
168 168 self._execution_state = "busy"
169 169 while self._execution_state != 'idle' and self.client.is_alive():
170 170 try:
171 171 self.handle_input_request(msg_id, timeout=0.05)
172 172 except Empty:
173 173 # display intermediate print statements, etc.
174 174 self.handle_iopub(msg_id)
175 175 except ZMQError as e:
176 176 # Carry on if polling was interrupted by a signal
177 177 if e.errno != errno.EINTR:
178 178 raise
179 179
180 180 # after all of that is done, wait for the execute reply
181 181 while self.client.is_alive():
182 182 try:
183 183 self.handle_execute_reply(msg_id, timeout=0.05)
184 184 except Empty:
185 185 pass
186 186 else:
187 187 break
188 188 self._executing = False
189 189
190 190 #-----------------
191 191 # message handlers
192 192 #-----------------
193 193
194 194 def handle_execute_reply(self, msg_id, timeout=None):
195 195 msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
196 196 if msg["parent_header"].get("msg_id", None) == msg_id:
197 197
198 198 self.handle_iopub(msg_id)
199 199
200 200 content = msg["content"]
201 201 status = content['status']
202 202
203 203 if status == 'aborted':
204 204 self.write('Aborted\n')
205 205 return
206 206 elif status == 'ok':
207 207 # handle payloads
208 208 for item in content["payload"]:
209 209 source = item['source']
210 210 if source == 'page':
211 211 page.page(item['data']['text/plain'])
212 212 elif source == 'set_next_input':
213 213 self.set_next_input(item['text'])
214 214 elif source == 'ask_exit':
215 self.keepkernel=item.get('keepkernel', False)
215 216 self.ask_exit()
216 217
217 218 elif status == 'error':
218 219 for frame in content["traceback"]:
219 220 print(frame, file=io.stderr)
220 221
221 222 self.execution_count = int(content["execution_count"] + 1)
222 223
223 224 include_other_output = Bool(False, config=True,
224 225 help="""Whether to include output from clients
225 226 other than this one sharing the same kernel.
226 227
227 228 Outputs are not displayed until enter is pressed.
228 229 """
229 230 )
230 231 other_output_prefix = Unicode("[remote] ", config=True,
231 232 help="""Prefix to add to outputs coming from clients other than this one.
232 233
233 234 Only relevant if include_other_output is True.
234 235 """
235 236 )
236
237
237 238 def from_here(self, msg):
238 239 """Return whether a message is from this session"""
239 240 return msg['parent_header'].get("session", self.session_id) == self.session_id
240 241
241 242 def include_output(self, msg):
242 243 """Return whether we should include a given output message"""
243 244 from_here = self.from_here(msg)
244 245 if msg['msg_type'] == 'execute_input':
245 246 # only echo inputs not from here
246 247 return self.include_other_output and not from_here
247 248
248 249 if self.include_other_output:
249 250 return True
250 251 else:
251 252 return from_here
252 253
253 254 def handle_iopub(self, msg_id=''):
254 255 """Process messages on the IOPub channel
255 256
256 257 This method consumes and processes messages on the IOPub channel,
257 258 such as stdout, stderr, execute_result and status.
258 259
259 260 It only displays output that is caused by this session.
260 261 """
261 262 while self.client.iopub_channel.msg_ready():
262 263 sub_msg = self.client.iopub_channel.get_msg()
263 264 msg_type = sub_msg['header']['msg_type']
264 265 parent = sub_msg["parent_header"]
265 266
266 267 if self.include_output(sub_msg):
267 268 if msg_type == 'status':
268 269 self._execution_state = sub_msg["content"]["execution_state"]
269 270 elif msg_type == 'stream':
270 271 if sub_msg["content"]["name"] == "stdout":
271 272 if self._pending_clearoutput:
272 273 print("\r", file=io.stdout, end="")
273 274 self._pending_clearoutput = False
274 275 print(sub_msg["content"]["text"], file=io.stdout, end="")
275 276 io.stdout.flush()
276 277 elif sub_msg["content"]["name"] == "stderr":
277 278 if self._pending_clearoutput:
278 279 print("\r", file=io.stderr, end="")
279 280 self._pending_clearoutput = False
280 281 print(sub_msg["content"]["text"], file=io.stderr, end="")
281 282 io.stderr.flush()
282 283
283 284 elif msg_type == 'execute_result':
284 285 if self._pending_clearoutput:
285 286 print("\r", file=io.stdout, end="")
286 287 self._pending_clearoutput = False
287 288 self.execution_count = int(sub_msg["content"]["execution_count"])
288 289 if not self.from_here(sub_msg):
289 290 sys.stdout.write(self.other_output_prefix)
290 291 format_dict = sub_msg["content"]["data"]
291 292 self.handle_rich_data(format_dict)
292 293
293 294 # taken from DisplayHook.__call__:
294 295 hook = self.displayhook
295 296 hook.start_displayhook()
296 297 hook.write_output_prompt()
297 298 hook.write_format_data(format_dict)
298 299 hook.log_output(format_dict)
299 300 hook.finish_displayhook()
300 301
301 302 elif msg_type == 'display_data':
302 303 data = sub_msg["content"]["data"]
303 304 handled = self.handle_rich_data(data)
304 305 if not handled:
305 306 if not self.from_here(sub_msg):
306 307 sys.stdout.write(self.other_output_prefix)
307 308 # if it was an image, we handled it by now
308 309 if 'text/plain' in data:
309 310 print(data['text/plain'])
310 311
311 312 elif msg_type == 'execute_input':
312 313 content = sub_msg['content']
313 314 self.execution_count = content['execution_count']
314 315 if not self.from_here(sub_msg):
315 316 sys.stdout.write(self.other_output_prefix)
316 317 sys.stdout.write(self.prompt_manager.render('in'))
317 318 sys.stdout.write(content['code'])
318 319
319 320 elif msg_type == 'clear_output':
320 321 if sub_msg["content"]["wait"]:
321 322 self._pending_clearoutput = True
322 323 else:
323 324 print("\r", file=io.stdout, end="")
324 325
325 326 _imagemime = {
326 327 'image/png': 'png',
327 328 'image/jpeg': 'jpeg',
328 329 'image/svg+xml': 'svg',
329 330 }
330 331
331 332 def handle_rich_data(self, data):
332 333 for mime in self.mime_preference:
333 334 if mime in data and mime in self._imagemime:
334 335 self.handle_image(data, mime)
335 336 return True
336 337
337 338 def handle_image(self, data, mime):
338 339 handler = getattr(
339 340 self, 'handle_image_{0}'.format(self.image_handler), None)
340 341 if handler:
341 342 handler(data, mime)
342 343
343 344 def handle_image_PIL(self, data, mime):
344 345 if mime not in ('image/png', 'image/jpeg'):
345 346 return
346 347 import PIL.Image
347 348 raw = base64.decodestring(data[mime].encode('ascii'))
348 349 img = PIL.Image.open(BytesIO(raw))
349 350 img.show()
350 351
351 352 def handle_image_stream(self, data, mime):
352 353 raw = base64.decodestring(data[mime].encode('ascii'))
353 354 imageformat = self._imagemime[mime]
354 355 fmt = dict(format=imageformat)
355 356 args = [s.format(**fmt) for s in self.stream_image_handler]
356 357 with open(os.devnull, 'w') as devnull:
357 358 proc = subprocess.Popen(
358 359 args, stdin=subprocess.PIPE,
359 360 stdout=devnull, stderr=devnull)
360 361 proc.communicate(raw)
361 362
362 363 def handle_image_tempfile(self, data, mime):
363 364 raw = base64.decodestring(data[mime].encode('ascii'))
364 365 imageformat = self._imagemime[mime]
365 366 filename = 'tmp.{0}'.format(imageformat)
366 367 with NamedFileInTemporaryDirectory(filename) as f, \
367 368 open(os.devnull, 'w') as devnull:
368 369 f.write(raw)
369 370 f.flush()
370 371 fmt = dict(file=f.name, format=imageformat)
371 372 args = [s.format(**fmt) for s in self.tempfile_image_handler]
372 373 subprocess.call(args, stdout=devnull, stderr=devnull)
373 374
374 375 def handle_image_callable(self, data, mime):
375 376 self.callable_image_handler(data)
376 377
377 378 def handle_input_request(self, msg_id, timeout=0.1):
378 379 """ Method to capture raw_input
379 380 """
380 381 req = self.client.stdin_channel.get_msg(timeout=timeout)
381 382 # in case any iopub came while we were waiting:
382 383 self.handle_iopub(msg_id)
383 384 if msg_id == req["parent_header"].get("msg_id"):
384 385 # wrap SIGINT handler
385 386 real_handler = signal.getsignal(signal.SIGINT)
386 387 def double_int(sig,frame):
387 388 # call real handler (forwards sigint to kernel),
388 389 # then raise local interrupt, stopping local raw_input
389 390 real_handler(sig,frame)
390 391 raise KeyboardInterrupt
391 392 signal.signal(signal.SIGINT, double_int)
392 393 content = req['content']
393 394 read = getpass if content.get('password', False) else input
394 395 try:
395 396 raw_data = read(content["prompt"])
396 397 except EOFError:
397 398 # turn EOFError into EOF character
398 399 raw_data = '\x04'
399 400 except KeyboardInterrupt:
400 401 sys.stdout.write('\n')
401 402 return
402 403 finally:
403 404 # restore SIGINT handler
404 405 signal.signal(signal.SIGINT, real_handler)
405 406
406 407 # only send stdin reply if there *was not* another request
407 408 # or execution finished while we were reading.
408 409 if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
409 410 self.client.input(raw_data)
410 411
411 412 def mainloop(self, display_banner=False):
413 self.keepkernel = False
412 414 while True:
413 415 try:
414 416 self.interact(display_banner=display_banner)
415 417 #self.interact_with_readline()
416 418 # XXX for testing of a readline-decoupled repl loop, call
417 419 # interact_with_readline above
418 420 break
419 421 except KeyboardInterrupt:
420 422 # this should not be necessary, but KeyboardInterrupt
421 423 # handling seems rather unpredictable...
422 424 self.write("\nKeyboardInterrupt in interact()\n")
423 425
424 self.client.shutdown()
426
427 if self.keepkernel and not self.own_kernel:
428 print('keeping kernel alive')
429 elif self.keepkernel and self.own_kernel :
430 print("owning kernel, cannot keep it alive")
431 self.client.shutdown()
432 else :
433 print("Shutting down kernel")
434 self.client.shutdown()
425 435
426 436 def _banner1_default(self):
427 437 return "IPython Console {version}\n".format(version=release.version)
428 438
429 439 def compute_banner(self):
430 440 super(ZMQTerminalInteractiveShell, self).compute_banner()
431 441 if self.client and not self.kernel_banner:
432 442 msg_id = self.client.kernel_info()
433 443 while True:
434 444 try:
435 445 reply = self.client.get_shell_msg(timeout=1)
436 446 except Empty:
437 447 break
438 448 else:
439 449 if reply['parent_header'].get('msg_id') == msg_id:
440 450 self.kernel_banner = reply['content'].get('banner', '')
441 451 break
442 452 self.banner += self.kernel_banner
443 453
444 454 def wait_for_kernel(self, timeout=None):
445 455 """method to wait for a kernel to be ready"""
446 456 tic = time.time()
447 457 self.client.hb_channel.unpause()
448 458 while True:
449 459 msg_id = self.client.kernel_info()
450 460 reply = None
451 461 while True:
452 462 try:
453 463 reply = self.client.get_shell_msg(timeout=1)
454 464 except Empty:
455 465 break
456 466 else:
457 467 if reply['parent_header'].get('msg_id') == msg_id:
458 468 return True
459 469 if timeout is not None \
460 470 and (time.time() - tic) > timeout \
461 471 and not self.client.hb_channel.is_beating():
462 472 # heart failed
463 473 return False
464 474 return True
465 475
466 476 def interact(self, display_banner=None):
467 477 """Closely emulate the interactive Python console."""
468 478
469 479 # batch run -> do not interact
470 480 if self.exit_now:
471 481 return
472 482
473 483 if display_banner is None:
474 484 display_banner = self.display_banner
475 485
476 486 if isinstance(display_banner, string_types):
477 487 self.show_banner(display_banner)
478 488 elif display_banner:
479 489 self.show_banner()
480 490
481 491 more = False
482 492
483 493 # run a non-empty no-op, so that we don't get a prompt until
484 494 # we know the kernel is ready. This keeps the connection
485 495 # message above the first prompt.
486 496 if not self.wait_for_kernel(self.kernel_timeout):
487 497 error("Kernel did not respond\n")
488 498 return
489 499
490 500 if self.has_readline:
491 501 self.readline_startup_hook(self.pre_readline)
492 502 hlen_b4_cell = self.readline.get_current_history_length()
493 503 else:
494 504 hlen_b4_cell = 0
495 505 # exit_now is set by a call to %Exit or %Quit, through the
496 506 # ask_exit callback.
497 507
498 508 while not self.exit_now:
499 509 if not self.client.is_alive():
500 510 # kernel died, prompt for action or exit
501 511
502 512 action = "restart" if self.manager else "wait for restart"
503 513 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
504 514 if ans:
505 515 if self.manager:
506 516 self.manager.restart_kernel(True)
507 517 self.wait_for_kernel(self.kernel_timeout)
508 518 else:
509 519 self.exit_now = True
510 520 continue
511 521 try:
512 522 # protect prompt block from KeyboardInterrupt
513 523 # when sitting on ctrl-C
514 524 self.hooks.pre_prompt_hook()
515 525 if more:
516 526 try:
517 527 prompt = self.prompt_manager.render('in2')
518 528 except Exception:
519 529 self.showtraceback()
520 530 if self.autoindent:
521 531 self.rl_do_indent = True
522 532
523 533 else:
524 534 try:
525 535 prompt = self.separate_in + self.prompt_manager.render('in')
526 536 except Exception:
527 537 self.showtraceback()
528 538
529 539 line = self.raw_input(prompt)
530 540 if self.exit_now:
531 541 # quick exit on sys.std[in|out] close
532 542 break
533 543 if self.autoindent:
534 544 self.rl_do_indent = False
535 545
536 546 except KeyboardInterrupt:
537 547 #double-guard against keyboardinterrupts during kbdint handling
538 548 try:
539 549 self.write('\n' + self.get_exception_only())
540 550 source_raw = self.input_splitter.raw_reset()
541 551 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
542 552 more = False
543 553 except KeyboardInterrupt:
544 554 pass
545 555 except EOFError:
546 556 if self.autoindent:
547 557 self.rl_do_indent = False
548 558 if self.has_readline:
549 559 self.readline_startup_hook(None)
550 560 self.write('\n')
551 561 self.exit()
552 562 except bdb.BdbQuit:
553 563 warn('The Python debugger has exited with a BdbQuit exception.\n'
554 564 'Because of how pdb handles the stack, it is impossible\n'
555 565 'for IPython to properly format this particular exception.\n'
556 566 'IPython will resume normal operation.')
557 567 except:
558 568 # exceptions here are VERY RARE, but they can be triggered
559 569 # asynchronously by signal handlers, for example.
560 570 self.showtraceback()
561 571 else:
562 572 try:
563 573 self.input_splitter.push(line)
564 574 more = self.input_splitter.push_accepts_more()
565 575 except SyntaxError:
566 576 # Run the code directly - run_cell takes care of displaying
567 577 # the exception.
568 578 more = False
569 579 if (self.SyntaxTB.last_syntax_error and
570 580 self.autoedit_syntax):
571 581 self.edit_syntax_error()
572 582 if not more:
573 583 source_raw = self.input_splitter.raw_reset()
574 584 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
575 585 self.run_cell(source_raw)
576 586
577 587
578 588 # Turn off the exit flag, so the mainloop can be restarted if desired
579 589 self.exit_now = False
580 590
581 591 def init_history(self):
582 592 """Sets up the command history. """
583 593 self.history_manager = ZMQHistoryManager(client=self.client)
584 594 self.configurables.append(self.history_manager)
585 595
General Comments 0
You need to be logged in to leave comments. Login now