##// END OF EJS Templates
only shutdown kernel when it is owned by the console
MinRK -
Show More
@@ -1,497 +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):
144 def ask_exit(self):
145 super(ZMQTerminalInteractiveShell, self).ask_exit()
145 super(ZMQTerminalInteractiveShell, self).ask_exit()
146 if self.exit_now:
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 self.handle_rich_data(sub_msg["content"]["data"])
267
267
268
268
269 _imagemime = {
269 _imagemime = {
270 'image/png': 'png',
270 'image/png': 'png',
271 'image/jpeg': 'jpeg',
271 'image/jpeg': 'jpeg',
272 'image/svg+xml': 'svg',
272 'image/svg+xml': 'svg',
273 }
273 }
274
274
275 def handle_rich_data(self, data):
275 def handle_rich_data(self, data):
276 for mime in self.mime_preference:
276 for mime in self.mime_preference:
277 if mime in data and mime in self._imagemime:
277 if mime in data and mime in self._imagemime:
278 self.handle_image(data, mime)
278 self.handle_image(data, mime)
279 return
279 return
280
280
281 def handle_image(self, data, mime):
281 def handle_image(self, data, mime):
282 handler = getattr(
282 handler = getattr(
283 self, 'handle_image_{0}'.format(self.image_handler), None)
283 self, 'handle_image_{0}'.format(self.image_handler), None)
284 if handler:
284 if handler:
285 handler(data, mime)
285 handler(data, mime)
286
286
287 def handle_image_PIL(self, data, mime):
287 def handle_image_PIL(self, data, mime):
288 if mime not in ('image/png', 'image/jpeg'):
288 if mime not in ('image/png', 'image/jpeg'):
289 return
289 return
290 import PIL.Image
290 import PIL.Image
291 raw = base64.decodestring(data[mime].encode('ascii'))
291 raw = base64.decodestring(data[mime].encode('ascii'))
292 img = PIL.Image.open(BytesIO(raw))
292 img = PIL.Image.open(BytesIO(raw))
293 img.show()
293 img.show()
294
294
295 def handle_image_stream(self, data, mime):
295 def handle_image_stream(self, data, mime):
296 raw = base64.decodestring(data[mime].encode('ascii'))
296 raw = base64.decodestring(data[mime].encode('ascii'))
297 imageformat = self._imagemime[mime]
297 imageformat = self._imagemime[mime]
298 fmt = dict(format=imageformat)
298 fmt = dict(format=imageformat)
299 args = [s.format(**fmt) for s in self.stream_image_handler]
299 args = [s.format(**fmt) for s in self.stream_image_handler]
300 with open(os.devnull, 'w') as devnull:
300 with open(os.devnull, 'w') as devnull:
301 proc = subprocess.Popen(
301 proc = subprocess.Popen(
302 args, stdin=subprocess.PIPE,
302 args, stdin=subprocess.PIPE,
303 stdout=devnull, stderr=devnull)
303 stdout=devnull, stderr=devnull)
304 proc.communicate(raw)
304 proc.communicate(raw)
305
305
306 def handle_image_tempfile(self, data, mime):
306 def handle_image_tempfile(self, data, mime):
307 raw = base64.decodestring(data[mime].encode('ascii'))
307 raw = base64.decodestring(data[mime].encode('ascii'))
308 imageformat = self._imagemime[mime]
308 imageformat = self._imagemime[mime]
309 filename = 'tmp.{0}'.format(imageformat)
309 filename = 'tmp.{0}'.format(imageformat)
310 with NamedFileInTemporaryDirectory(filename) as f, \
310 with NamedFileInTemporaryDirectory(filename) as f, \
311 open(os.devnull, 'w') as devnull:
311 open(os.devnull, 'w') as devnull:
312 f.write(raw)
312 f.write(raw)
313 f.flush()
313 f.flush()
314 fmt = dict(file=f.name, format=imageformat)
314 fmt = dict(file=f.name, format=imageformat)
315 args = [s.format(**fmt) for s in self.tempfile_image_handler]
315 args = [s.format(**fmt) for s in self.tempfile_image_handler]
316 subprocess.call(args, stdout=devnull, stderr=devnull)
316 subprocess.call(args, stdout=devnull, stderr=devnull)
317
317
318 def handle_image_callable(self, data, mime):
318 def handle_image_callable(self, data, mime):
319 self.callable_image_handler(data)
319 self.callable_image_handler(data)
320
320
321 def handle_stdin_request(self, msg_id, timeout=0.1):
321 def handle_stdin_request(self, msg_id, timeout=0.1):
322 """ Method to capture raw_input
322 """ Method to capture raw_input
323 """
323 """
324 msg_rep = self.client.stdin_channel.get_msg(timeout=timeout)
324 msg_rep = self.client.stdin_channel.get_msg(timeout=timeout)
325 # in case any iopub came while we were waiting:
325 # in case any iopub came while we were waiting:
326 self.handle_iopub(msg_id)
326 self.handle_iopub(msg_id)
327 if msg_id == msg_rep["parent_header"].get("msg_id"):
327 if msg_id == msg_rep["parent_header"].get("msg_id"):
328 # wrap SIGINT handler
328 # wrap SIGINT handler
329 real_handler = signal.getsignal(signal.SIGINT)
329 real_handler = signal.getsignal(signal.SIGINT)
330 def double_int(sig,frame):
330 def double_int(sig,frame):
331 # call real handler (forwards sigint to kernel),
331 # call real handler (forwards sigint to kernel),
332 # then raise local interrupt, stopping local raw_input
332 # then raise local interrupt, stopping local raw_input
333 real_handler(sig,frame)
333 real_handler(sig,frame)
334 raise KeyboardInterrupt
334 raise KeyboardInterrupt
335 signal.signal(signal.SIGINT, double_int)
335 signal.signal(signal.SIGINT, double_int)
336
336
337 try:
337 try:
338 raw_data = input(msg_rep["content"]["prompt"])
338 raw_data = input(msg_rep["content"]["prompt"])
339 except EOFError:
339 except EOFError:
340 # turn EOFError into EOF character
340 # turn EOFError into EOF character
341 raw_data = '\x04'
341 raw_data = '\x04'
342 except KeyboardInterrupt:
342 except KeyboardInterrupt:
343 sys.stdout.write('\n')
343 sys.stdout.write('\n')
344 return
344 return
345 finally:
345 finally:
346 # restore SIGINT handler
346 # restore SIGINT handler
347 signal.signal(signal.SIGINT, real_handler)
347 signal.signal(signal.SIGINT, real_handler)
348
348
349 # only send stdin reply if there *was not* another request
349 # only send stdin reply if there *was not* another request
350 # or execution finished while we were reading.
350 # or execution finished while we were reading.
351 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()):
352 self.client.stdin_channel.input(raw_data)
352 self.client.stdin_channel.input(raw_data)
353
353
354 def mainloop(self, display_banner=False):
354 def mainloop(self, display_banner=False):
355 while True:
355 while True:
356 try:
356 try:
357 self.interact(display_banner=display_banner)
357 self.interact(display_banner=display_banner)
358 #self.interact_with_readline()
358 #self.interact_with_readline()
359 # XXX for testing of a readline-decoupled repl loop, call
359 # XXX for testing of a readline-decoupled repl loop, call
360 # interact_with_readline above
360 # interact_with_readline above
361 break
361 break
362 except KeyboardInterrupt:
362 except KeyboardInterrupt:
363 # this should not be necessary, but KeyboardInterrupt
363 # this should not be necessary, but KeyboardInterrupt
364 # handling seems rather unpredictable...
364 # handling seems rather unpredictable...
365 self.write("\nKeyboardInterrupt in interact()\n")
365 self.write("\nKeyboardInterrupt in interact()\n")
366
366
367 def wait_for_kernel(self, timeout=None):
367 def wait_for_kernel(self, timeout=None):
368 """method to wait for a kernel to be ready"""
368 """method to wait for a kernel to be ready"""
369 tic = time.time()
369 tic = time.time()
370 self.client.hb_channel.unpause()
370 self.client.hb_channel.unpause()
371 while True:
371 while True:
372 msg_id = self.client.kernel_info()
372 msg_id = self.client.kernel_info()
373 reply = None
373 reply = None
374 while True:
374 while True:
375 try:
375 try:
376 reply = self.client.get_shell_msg(timeout=1)
376 reply = self.client.get_shell_msg(timeout=1)
377 except Empty:
377 except Empty:
378 break
378 break
379 else:
379 else:
380 if reply['parent_header'].get('msg_id') == msg_id:
380 if reply['parent_header'].get('msg_id') == msg_id:
381 return True
381 return True
382 if timeout is not None \
382 if timeout is not None \
383 and (time.time() - tic) > timeout \
383 and (time.time() - tic) > timeout \
384 and not self.client.hb_channel.is_beating():
384 and not self.client.hb_channel.is_beating():
385 # heart failed
385 # heart failed
386 return False
386 return False
387 return True
387 return True
388
388
389 def interact(self, display_banner=None):
389 def interact(self, display_banner=None):
390 """Closely emulate the interactive Python console."""
390 """Closely emulate the interactive Python console."""
391
391
392 # batch run -> do not interact
392 # batch run -> do not interact
393 if self.exit_now:
393 if self.exit_now:
394 return
394 return
395
395
396 if display_banner is None:
396 if display_banner is None:
397 display_banner = self.display_banner
397 display_banner = self.display_banner
398
398
399 if isinstance(display_banner, string_types):
399 if isinstance(display_banner, string_types):
400 self.show_banner(display_banner)
400 self.show_banner(display_banner)
401 elif display_banner:
401 elif display_banner:
402 self.show_banner()
402 self.show_banner()
403
403
404 more = False
404 more = False
405
405
406 # 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
407 # we know the kernel is ready. This keeps the connection
407 # we know the kernel is ready. This keeps the connection
408 # message above the first prompt.
408 # message above the first prompt.
409 if not self.wait_for_kernel(self.kernel_timeout):
409 if not self.wait_for_kernel(self.kernel_timeout):
410 error("Kernel did not respond\n")
410 error("Kernel did not respond\n")
411 return
411 return
412
412
413 if self.has_readline:
413 if self.has_readline:
414 self.readline_startup_hook(self.pre_readline)
414 self.readline_startup_hook(self.pre_readline)
415 hlen_b4_cell = self.readline.get_current_history_length()
415 hlen_b4_cell = self.readline.get_current_history_length()
416 else:
416 else:
417 hlen_b4_cell = 0
417 hlen_b4_cell = 0
418 # 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
419 # ask_exit callback.
419 # ask_exit callback.
420
420
421 while not self.exit_now:
421 while not self.exit_now:
422 if not self.client.is_alive():
422 if not self.client.is_alive():
423 # kernel died, prompt for action or exit
423 # kernel died, prompt for action or exit
424
424
425 action = "restart" if self.manager else "wait for restart"
425 action = "restart" if self.manager else "wait for restart"
426 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')
427 if ans:
427 if ans:
428 if self.manager:
428 if self.manager:
429 self.manager.restart_kernel(True)
429 self.manager.restart_kernel(True)
430 self.wait_for_kernel(self.kernel_timeout)
430 self.wait_for_kernel(self.kernel_timeout)
431 else:
431 else:
432 self.exit_now = True
432 self.exit_now = True
433 continue
433 continue
434 try:
434 try:
435 # protect prompt block from KeyboardInterrupt
435 # protect prompt block from KeyboardInterrupt
436 # when sitting on ctrl-C
436 # when sitting on ctrl-C
437 self.hooks.pre_prompt_hook()
437 self.hooks.pre_prompt_hook()
438 if more:
438 if more:
439 try:
439 try:
440 prompt = self.prompt_manager.render('in2')
440 prompt = self.prompt_manager.render('in2')
441 except Exception:
441 except Exception:
442 self.showtraceback()
442 self.showtraceback()
443 if self.autoindent:
443 if self.autoindent:
444 self.rl_do_indent = True
444 self.rl_do_indent = True
445
445
446 else:
446 else:
447 try:
447 try:
448 prompt = self.separate_in + self.prompt_manager.render('in')
448 prompt = self.separate_in + self.prompt_manager.render('in')
449 except Exception:
449 except Exception:
450 self.showtraceback()
450 self.showtraceback()
451
451
452 line = self.raw_input(prompt)
452 line = self.raw_input(prompt)
453 if self.exit_now:
453 if self.exit_now:
454 # quick exit on sys.std[in|out] close
454 # quick exit on sys.std[in|out] close
455 break
455 break
456 if self.autoindent:
456 if self.autoindent:
457 self.rl_do_indent = False
457 self.rl_do_indent = False
458
458
459 except KeyboardInterrupt:
459 except KeyboardInterrupt:
460 #double-guard against keyboardinterrupts during kbdint handling
460 #double-guard against keyboardinterrupts during kbdint handling
461 try:
461 try:
462 self.write('\nKeyboardInterrupt\n')
462 self.write('\nKeyboardInterrupt\n')
463 source_raw = self.input_splitter.source_raw_reset()[1]
463 source_raw = self.input_splitter.source_raw_reset()[1]
464 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)
465 more = False
465 more = False
466 except KeyboardInterrupt:
466 except KeyboardInterrupt:
467 pass
467 pass
468 except EOFError:
468 except EOFError:
469 if self.autoindent:
469 if self.autoindent:
470 self.rl_do_indent = False
470 self.rl_do_indent = False
471 if self.has_readline:
471 if self.has_readline:
472 self.readline_startup_hook(None)
472 self.readline_startup_hook(None)
473 self.write('\n')
473 self.write('\n')
474 self.exit()
474 self.exit()
475 except bdb.BdbQuit:
475 except bdb.BdbQuit:
476 warn('The Python debugger has exited with a BdbQuit exception.\n'
476 warn('The Python debugger has exited with a BdbQuit exception.\n'
477 'Because of how pdb handles the stack, it is impossible\n'
477 'Because of how pdb handles the stack, it is impossible\n'
478 'for IPython to properly format this particular exception.\n'
478 'for IPython to properly format this particular exception.\n'
479 'IPython will resume normal operation.')
479 'IPython will resume normal operation.')
480 except:
480 except:
481 # exceptions here are VERY RARE, but they can be triggered
481 # exceptions here are VERY RARE, but they can be triggered
482 # asynchronously by signal handlers, for example.
482 # asynchronously by signal handlers, for example.
483 self.showtraceback()
483 self.showtraceback()
484 else:
484 else:
485 self.input_splitter.push(line)
485 self.input_splitter.push(line)
486 more = self.input_splitter.push_accepts_more()
486 more = self.input_splitter.push_accepts_more()
487 if (self.SyntaxTB.last_syntax_error and
487 if (self.SyntaxTB.last_syntax_error and
488 self.autoedit_syntax):
488 self.autoedit_syntax):
489 self.edit_syntax_error()
489 self.edit_syntax_error()
490 if not more:
490 if not more:
491 source_raw = self.input_splitter.source_raw_reset()[1]
491 source_raw = self.input_splitter.source_raw_reset()[1]
492 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)
493 self.run_cell(source_raw)
493 self.run_cell(source_raw)
494
494
495
495
496 # 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
497 self.exit_now = False
497 self.exit_now = False
General Comments 0
You need to be logged in to leave comments. Login now