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