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