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