##// END OF EJS Templates
don't double display in the console...
Paul Ivanov -
Show More
@@ -1,500 +1,501 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), programmatically (such as in test suites) or out-of-process
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):
144 def ask_exit(self):
145 super(ZMQTerminalInteractiveShell, self).ask_exit()
145 super(ZMQTerminalInteractiveShell, self).ask_exit()
146 if self.exit_now and self.manager:
146 if self.exit_now and self.manager:
147 self.client.shutdown()
147 self.client.shutdown()
148
148
149 def run_cell(self, cell, store_history=True):
149 def run_cell(self, cell, store_history=True):
150 """Run a complete IPython cell.
150 """Run a complete IPython cell.
151
151
152 Parameters
152 Parameters
153 ----------
153 ----------
154 cell : str
154 cell : str
155 The code (including IPython code such as %magic functions) to run.
155 The code (including IPython code such as %magic functions) to run.
156 store_history : bool
156 store_history : bool
157 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
158 history. For user code calling back into IPython's machinery, this
158 history. For user code calling back into IPython's machinery, this
159 should be set to False.
159 should be set to False.
160 """
160 """
161 if (not cell) or cell.isspace():
161 if (not cell) or cell.isspace():
162 return
162 return
163
163
164 if cell.strip() == 'exit':
164 if cell.strip() == 'exit':
165 # explicitly handle 'exit' command
165 # explicitly handle 'exit' command
166 return self.ask_exit()
166 return self.ask_exit()
167
167
168 # 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
169 while self.client.shell_channel.msg_ready():
169 while self.client.shell_channel.msg_ready():
170 self.client.shell_channel.get_msg()
170 self.client.shell_channel.get_msg()
171 # 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
172 msg_id = self.client.shell_channel.execute(cell, not store_history)
172 msg_id = self.client.shell_channel.execute(cell, not store_history)
173
173
174 # first thing is wait for any side effects (output, stdin, etc.)
174 # first thing is wait for any side effects (output, stdin, etc.)
175 self._executing = True
175 self._executing = True
176 self._execution_state = "busy"
176 self._execution_state = "busy"
177 while self._execution_state != 'idle' and self.client.is_alive():
177 while self._execution_state != 'idle' and self.client.is_alive():
178 try:
178 try:
179 self.handle_stdin_request(msg_id, timeout=0.05)
179 self.handle_stdin_request(msg_id, timeout=0.05)
180 except Empty:
180 except Empty:
181 # display intermediate print statements, etc.
181 # display intermediate print statements, etc.
182 self.handle_iopub(msg_id)
182 self.handle_iopub(msg_id)
183 pass
183 pass
184
184
185 # after all of that is done, wait for the execute reply
185 # after all of that is done, wait for the execute reply
186 while self.client.is_alive():
186 while self.client.is_alive():
187 try:
187 try:
188 self.handle_execute_reply(msg_id, timeout=0.05)
188 self.handle_execute_reply(msg_id, timeout=0.05)
189 except Empty:
189 except Empty:
190 pass
190 pass
191 else:
191 else:
192 break
192 break
193 self._executing = False
193 self._executing = False
194
194
195 #-----------------
195 #-----------------
196 # message handlers
196 # message handlers
197 #-----------------
197 #-----------------
198
198
199 def handle_execute_reply(self, msg_id, timeout=None):
199 def handle_execute_reply(self, msg_id, timeout=None):
200 msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
200 msg = self.client.shell_channel.get_msg(block=False, timeout=timeout)
201 if msg["parent_header"].get("msg_id", None) == msg_id:
201 if msg["parent_header"].get("msg_id", None) == msg_id:
202
202
203 self.handle_iopub(msg_id)
203 self.handle_iopub(msg_id)
204
204
205 content = msg["content"]
205 content = msg["content"]
206 status = content['status']
206 status = content['status']
207
207
208 if status == 'aborted':
208 if status == 'aborted':
209 self.write('Aborted\n')
209 self.write('Aborted\n')
210 return
210 return
211 elif status == 'ok':
211 elif status == 'ok':
212 # print execution payloads as well:
212 # print execution payloads as well:
213 for item in content["payload"]:
213 for item in content["payload"]:
214 text = item.get('text', None)
214 text = item.get('text', None)
215 if text:
215 if text:
216 page.page(text)
216 page.page(text)
217
217
218 elif status == 'error':
218 elif status == 'error':
219 for frame in content["traceback"]:
219 for frame in content["traceback"]:
220 print(frame, file=io.stderr)
220 print(frame, file=io.stderr)
221
221
222 self.execution_count = int(content["execution_count"] + 1)
222 self.execution_count = int(content["execution_count"] + 1)
223
223
224
224
225 def handle_iopub(self, msg_id):
225 def handle_iopub(self, msg_id):
226 """ Method to process subscribe channel's messages
226 """ Method to process subscribe channel's messages
227
227
228 This method consumes and processes messages on the IOPub channel,
228 This method consumes and processes messages on the IOPub channel,
229 such as stdout, stderr, pyout and status.
229 such as stdout, stderr, pyout and status.
230
230
231 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
232 """
232 """
233 while self.client.iopub_channel.msg_ready():
233 while self.client.iopub_channel.msg_ready():
234 sub_msg = self.client.iopub_channel.get_msg()
234 sub_msg = self.client.iopub_channel.get_msg()
235 msg_type = sub_msg['header']['msg_type']
235 msg_type = sub_msg['header']['msg_type']
236 parent = sub_msg["parent_header"]
236 parent = sub_msg["parent_header"]
237 if (not parent) or msg_id == parent['msg_id']:
237 if (not parent) or msg_id == parent['msg_id']:
238 if msg_type == 'status':
238 if msg_type == 'status':
239 state = self._execution_state = sub_msg["content"]["execution_state"]
239 state = self._execution_state = sub_msg["content"]["execution_state"]
240 # idle messages mean an individual sequence is complete,
240 # idle messages mean an individual sequence is complete,
241 # 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.
242 if state == 'idle':
242 if state == 'idle':
243 break
243 break
244
244
245 elif msg_type == 'stream':
245 elif msg_type == 'stream':
246 if sub_msg["content"]["name"] == "stdout":
246 if sub_msg["content"]["name"] == "stdout":
247 print(sub_msg["content"]["data"], file=io.stdout, end="")
247 print(sub_msg["content"]["data"], file=io.stdout, end="")
248 io.stdout.flush()
248 io.stdout.flush()
249 elif sub_msg["content"]["name"] == "stderr" :
249 elif sub_msg["content"]["name"] == "stderr" :
250 print(sub_msg["content"]["data"], file=io.stderr, end="")
250 print(sub_msg["content"]["data"], file=io.stderr, end="")
251 io.stderr.flush()
251 io.stderr.flush()
252
252
253 elif msg_type == 'pyout':
253 elif msg_type == 'pyout':
254 self.execution_count = int(sub_msg["content"]["execution_count"])
254 self.execution_count = int(sub_msg["content"]["execution_count"])
255 format_dict = sub_msg["content"]["data"]
255 format_dict = sub_msg["content"]["data"]
256 self.handle_rich_data(format_dict)
256 self.handle_rich_data(format_dict)
257 # taken from DisplayHook.__call__:
257 # taken from DisplayHook.__call__:
258 hook = self.displayhook
258 hook = self.displayhook
259 hook.start_displayhook()
259 hook.start_displayhook()
260 hook.write_output_prompt()
260 hook.write_output_prompt()
261 hook.write_format_data(format_dict)
261 hook.write_format_data(format_dict)
262 hook.log_output(format_dict)
262 hook.log_output(format_dict)
263 hook.finish_displayhook()
263 hook.finish_displayhook()
264
264
265 elif msg_type == 'display_data':
265 elif msg_type == 'display_data':
266 self.handle_rich_data(sub_msg["content"]["data"])
266 data = sub_msg["content"]["data"]
267
267 handled = self.handle_rich_data(data)
268 if not handled:
269 # if it was an image, we handled it by now
270 if 'text/plain' in data:
271 print(data['text/plain'])
268
272
269 _imagemime = {
273 _imagemime = {
270 'image/png': 'png',
274 'image/png': 'png',
271 'image/jpeg': 'jpeg',
275 'image/jpeg': 'jpeg',
272 'image/svg+xml': 'svg',
276 'image/svg+xml': 'svg',
273 }
277 }
274
278
275 def handle_rich_data(self, data):
279 def handle_rich_data(self, data):
276 for mime in self.mime_preference:
280 for mime in self.mime_preference:
277 if mime in data and mime in self._imagemime:
281 if mime in data and mime in self._imagemime:
278 self.handle_image(data, mime)
282 self.handle_image(data, mime)
279 return
283 return True
280 # if it was an image, we handled it by now and returned
281 if 'text/plain' in data:
282 print(data['text/plain'])
283
284
284 def handle_image(self, data, mime):
285 def handle_image(self, data, mime):
285 handler = getattr(
286 handler = getattr(
286 self, 'handle_image_{0}'.format(self.image_handler), None)
287 self, 'handle_image_{0}'.format(self.image_handler), None)
287 if handler:
288 if handler:
288 handler(data, mime)
289 handler(data, mime)
289
290
290 def handle_image_PIL(self, data, mime):
291 def handle_image_PIL(self, data, mime):
291 if mime not in ('image/png', 'image/jpeg'):
292 if mime not in ('image/png', 'image/jpeg'):
292 return
293 return
293 import PIL.Image
294 import PIL.Image
294 raw = base64.decodestring(data[mime].encode('ascii'))
295 raw = base64.decodestring(data[mime].encode('ascii'))
295 img = PIL.Image.open(BytesIO(raw))
296 img = PIL.Image.open(BytesIO(raw))
296 img.show()
297 img.show()
297
298
298 def handle_image_stream(self, data, mime):
299 def handle_image_stream(self, data, mime):
299 raw = base64.decodestring(data[mime].encode('ascii'))
300 raw = base64.decodestring(data[mime].encode('ascii'))
300 imageformat = self._imagemime[mime]
301 imageformat = self._imagemime[mime]
301 fmt = dict(format=imageformat)
302 fmt = dict(format=imageformat)
302 args = [s.format(**fmt) for s in self.stream_image_handler]
303 args = [s.format(**fmt) for s in self.stream_image_handler]
303 with open(os.devnull, 'w') as devnull:
304 with open(os.devnull, 'w') as devnull:
304 proc = subprocess.Popen(
305 proc = subprocess.Popen(
305 args, stdin=subprocess.PIPE,
306 args, stdin=subprocess.PIPE,
306 stdout=devnull, stderr=devnull)
307 stdout=devnull, stderr=devnull)
307 proc.communicate(raw)
308 proc.communicate(raw)
308
309
309 def handle_image_tempfile(self, data, mime):
310 def handle_image_tempfile(self, data, mime):
310 raw = base64.decodestring(data[mime].encode('ascii'))
311 raw = base64.decodestring(data[mime].encode('ascii'))
311 imageformat = self._imagemime[mime]
312 imageformat = self._imagemime[mime]
312 filename = 'tmp.{0}'.format(imageformat)
313 filename = 'tmp.{0}'.format(imageformat)
313 with NamedFileInTemporaryDirectory(filename) as f, \
314 with NamedFileInTemporaryDirectory(filename) as f, \
314 open(os.devnull, 'w') as devnull:
315 open(os.devnull, 'w') as devnull:
315 f.write(raw)
316 f.write(raw)
316 f.flush()
317 f.flush()
317 fmt = dict(file=f.name, format=imageformat)
318 fmt = dict(file=f.name, format=imageformat)
318 args = [s.format(**fmt) for s in self.tempfile_image_handler]
319 args = [s.format(**fmt) for s in self.tempfile_image_handler]
319 subprocess.call(args, stdout=devnull, stderr=devnull)
320 subprocess.call(args, stdout=devnull, stderr=devnull)
320
321
321 def handle_image_callable(self, data, mime):
322 def handle_image_callable(self, data, mime):
322 self.callable_image_handler(data)
323 self.callable_image_handler(data)
323
324
324 def handle_stdin_request(self, msg_id, timeout=0.1):
325 def handle_stdin_request(self, msg_id, timeout=0.1):
325 """ Method to capture raw_input
326 """ Method to capture raw_input
326 """
327 """
327 msg_rep = self.client.stdin_channel.get_msg(timeout=timeout)
328 msg_rep = self.client.stdin_channel.get_msg(timeout=timeout)
328 # in case any iopub came while we were waiting:
329 # in case any iopub came while we were waiting:
329 self.handle_iopub(msg_id)
330 self.handle_iopub(msg_id)
330 if msg_id == msg_rep["parent_header"].get("msg_id"):
331 if msg_id == msg_rep["parent_header"].get("msg_id"):
331 # wrap SIGINT handler
332 # wrap SIGINT handler
332 real_handler = signal.getsignal(signal.SIGINT)
333 real_handler = signal.getsignal(signal.SIGINT)
333 def double_int(sig,frame):
334 def double_int(sig,frame):
334 # call real handler (forwards sigint to kernel),
335 # call real handler (forwards sigint to kernel),
335 # then raise local interrupt, stopping local raw_input
336 # then raise local interrupt, stopping local raw_input
336 real_handler(sig,frame)
337 real_handler(sig,frame)
337 raise KeyboardInterrupt
338 raise KeyboardInterrupt
338 signal.signal(signal.SIGINT, double_int)
339 signal.signal(signal.SIGINT, double_int)
339
340
340 try:
341 try:
341 raw_data = input(msg_rep["content"]["prompt"])
342 raw_data = input(msg_rep["content"]["prompt"])
342 except EOFError:
343 except EOFError:
343 # turn EOFError into EOF character
344 # turn EOFError into EOF character
344 raw_data = '\x04'
345 raw_data = '\x04'
345 except KeyboardInterrupt:
346 except KeyboardInterrupt:
346 sys.stdout.write('\n')
347 sys.stdout.write('\n')
347 return
348 return
348 finally:
349 finally:
349 # restore SIGINT handler
350 # restore SIGINT handler
350 signal.signal(signal.SIGINT, real_handler)
351 signal.signal(signal.SIGINT, real_handler)
351
352
352 # only send stdin reply if there *was not* another request
353 # only send stdin reply if there *was not* another request
353 # or execution finished while we were reading.
354 # or execution finished while we were reading.
354 if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
355 if not (self.client.stdin_channel.msg_ready() or self.client.shell_channel.msg_ready()):
355 self.client.stdin_channel.input(raw_data)
356 self.client.stdin_channel.input(raw_data)
356
357
357 def mainloop(self, display_banner=False):
358 def mainloop(self, display_banner=False):
358 while True:
359 while True:
359 try:
360 try:
360 self.interact(display_banner=display_banner)
361 self.interact(display_banner=display_banner)
361 #self.interact_with_readline()
362 #self.interact_with_readline()
362 # XXX for testing of a readline-decoupled repl loop, call
363 # XXX for testing of a readline-decoupled repl loop, call
363 # interact_with_readline above
364 # interact_with_readline above
364 break
365 break
365 except KeyboardInterrupt:
366 except KeyboardInterrupt:
366 # this should not be necessary, but KeyboardInterrupt
367 # this should not be necessary, but KeyboardInterrupt
367 # handling seems rather unpredictable...
368 # handling seems rather unpredictable...
368 self.write("\nKeyboardInterrupt in interact()\n")
369 self.write("\nKeyboardInterrupt in interact()\n")
369
370
370 def wait_for_kernel(self, timeout=None):
371 def wait_for_kernel(self, timeout=None):
371 """method to wait for a kernel to be ready"""
372 """method to wait for a kernel to be ready"""
372 tic = time.time()
373 tic = time.time()
373 self.client.hb_channel.unpause()
374 self.client.hb_channel.unpause()
374 while True:
375 while True:
375 msg_id = self.client.kernel_info()
376 msg_id = self.client.kernel_info()
376 reply = None
377 reply = None
377 while True:
378 while True:
378 try:
379 try:
379 reply = self.client.get_shell_msg(timeout=1)
380 reply = self.client.get_shell_msg(timeout=1)
380 except Empty:
381 except Empty:
381 break
382 break
382 else:
383 else:
383 if reply['parent_header'].get('msg_id') == msg_id:
384 if reply['parent_header'].get('msg_id') == msg_id:
384 return True
385 return True
385 if timeout is not None \
386 if timeout is not None \
386 and (time.time() - tic) > timeout \
387 and (time.time() - tic) > timeout \
387 and not self.client.hb_channel.is_beating():
388 and not self.client.hb_channel.is_beating():
388 # heart failed
389 # heart failed
389 return False
390 return False
390 return True
391 return True
391
392
392 def interact(self, display_banner=None):
393 def interact(self, display_banner=None):
393 """Closely emulate the interactive Python console."""
394 """Closely emulate the interactive Python console."""
394
395
395 # batch run -> do not interact
396 # batch run -> do not interact
396 if self.exit_now:
397 if self.exit_now:
397 return
398 return
398
399
399 if display_banner is None:
400 if display_banner is None:
400 display_banner = self.display_banner
401 display_banner = self.display_banner
401
402
402 if isinstance(display_banner, string_types):
403 if isinstance(display_banner, string_types):
403 self.show_banner(display_banner)
404 self.show_banner(display_banner)
404 elif display_banner:
405 elif display_banner:
405 self.show_banner()
406 self.show_banner()
406
407
407 more = False
408 more = False
408
409
409 # run a non-empty no-op, so that we don't get a prompt until
410 # run a non-empty no-op, so that we don't get a prompt until
410 # we know the kernel is ready. This keeps the connection
411 # we know the kernel is ready. This keeps the connection
411 # message above the first prompt.
412 # message above the first prompt.
412 if not self.wait_for_kernel(self.kernel_timeout):
413 if not self.wait_for_kernel(self.kernel_timeout):
413 error("Kernel did not respond\n")
414 error("Kernel did not respond\n")
414 return
415 return
415
416
416 if self.has_readline:
417 if self.has_readline:
417 self.readline_startup_hook(self.pre_readline)
418 self.readline_startup_hook(self.pre_readline)
418 hlen_b4_cell = self.readline.get_current_history_length()
419 hlen_b4_cell = self.readline.get_current_history_length()
419 else:
420 else:
420 hlen_b4_cell = 0
421 hlen_b4_cell = 0
421 # exit_now is set by a call to %Exit or %Quit, through the
422 # exit_now is set by a call to %Exit or %Quit, through the
422 # ask_exit callback.
423 # ask_exit callback.
423
424
424 while not self.exit_now:
425 while not self.exit_now:
425 if not self.client.is_alive():
426 if not self.client.is_alive():
426 # kernel died, prompt for action or exit
427 # kernel died, prompt for action or exit
427
428
428 action = "restart" if self.manager else "wait for restart"
429 action = "restart" if self.manager else "wait for restart"
429 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
430 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
430 if ans:
431 if ans:
431 if self.manager:
432 if self.manager:
432 self.manager.restart_kernel(True)
433 self.manager.restart_kernel(True)
433 self.wait_for_kernel(self.kernel_timeout)
434 self.wait_for_kernel(self.kernel_timeout)
434 else:
435 else:
435 self.exit_now = True
436 self.exit_now = True
436 continue
437 continue
437 try:
438 try:
438 # protect prompt block from KeyboardInterrupt
439 # protect prompt block from KeyboardInterrupt
439 # when sitting on ctrl-C
440 # when sitting on ctrl-C
440 self.hooks.pre_prompt_hook()
441 self.hooks.pre_prompt_hook()
441 if more:
442 if more:
442 try:
443 try:
443 prompt = self.prompt_manager.render('in2')
444 prompt = self.prompt_manager.render('in2')
444 except Exception:
445 except Exception:
445 self.showtraceback()
446 self.showtraceback()
446 if self.autoindent:
447 if self.autoindent:
447 self.rl_do_indent = True
448 self.rl_do_indent = True
448
449
449 else:
450 else:
450 try:
451 try:
451 prompt = self.separate_in + self.prompt_manager.render('in')
452 prompt = self.separate_in + self.prompt_manager.render('in')
452 except Exception:
453 except Exception:
453 self.showtraceback()
454 self.showtraceback()
454
455
455 line = self.raw_input(prompt)
456 line = self.raw_input(prompt)
456 if self.exit_now:
457 if self.exit_now:
457 # quick exit on sys.std[in|out] close
458 # quick exit on sys.std[in|out] close
458 break
459 break
459 if self.autoindent:
460 if self.autoindent:
460 self.rl_do_indent = False
461 self.rl_do_indent = False
461
462
462 except KeyboardInterrupt:
463 except KeyboardInterrupt:
463 #double-guard against keyboardinterrupts during kbdint handling
464 #double-guard against keyboardinterrupts during kbdint handling
464 try:
465 try:
465 self.write('\nKeyboardInterrupt\n')
466 self.write('\nKeyboardInterrupt\n')
466 source_raw = self.input_splitter.source_raw_reset()[1]
467 source_raw = self.input_splitter.source_raw_reset()[1]
467 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
468 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
468 more = False
469 more = False
469 except KeyboardInterrupt:
470 except KeyboardInterrupt:
470 pass
471 pass
471 except EOFError:
472 except EOFError:
472 if self.autoindent:
473 if self.autoindent:
473 self.rl_do_indent = False
474 self.rl_do_indent = False
474 if self.has_readline:
475 if self.has_readline:
475 self.readline_startup_hook(None)
476 self.readline_startup_hook(None)
476 self.write('\n')
477 self.write('\n')
477 self.exit()
478 self.exit()
478 except bdb.BdbQuit:
479 except bdb.BdbQuit:
479 warn('The Python debugger has exited with a BdbQuit exception.\n'
480 warn('The Python debugger has exited with a BdbQuit exception.\n'
480 'Because of how pdb handles the stack, it is impossible\n'
481 'Because of how pdb handles the stack, it is impossible\n'
481 'for IPython to properly format this particular exception.\n'
482 'for IPython to properly format this particular exception.\n'
482 'IPython will resume normal operation.')
483 'IPython will resume normal operation.')
483 except:
484 except:
484 # exceptions here are VERY RARE, but they can be triggered
485 # exceptions here are VERY RARE, but they can be triggered
485 # asynchronously by signal handlers, for example.
486 # asynchronously by signal handlers, for example.
486 self.showtraceback()
487 self.showtraceback()
487 else:
488 else:
488 self.input_splitter.push(line)
489 self.input_splitter.push(line)
489 more = self.input_splitter.push_accepts_more()
490 more = self.input_splitter.push_accepts_more()
490 if (self.SyntaxTB.last_syntax_error and
491 if (self.SyntaxTB.last_syntax_error and
491 self.autoedit_syntax):
492 self.autoedit_syntax):
492 self.edit_syntax_error()
493 self.edit_syntax_error()
493 if not more:
494 if not more:
494 source_raw = self.input_splitter.source_raw_reset()[1]
495 source_raw = self.input_splitter.source_raw_reset()[1]
495 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
496 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
496 self.run_cell(source_raw)
497 self.run_cell(source_raw)
497
498
498
499
499 # Turn off the exit flag, so the mainloop can be restarted if desired
500 # Turn off the exit flag, so the mainloop can be restarted if desired
500 self.exit_now = False
501 self.exit_now = False
General Comments 0
You need to be logged in to leave comments. Login now