##// END OF EJS Templates
fix magics history in two-process ipython console...
Paul Ivanov -
Show More
@@ -1,342 +1,342
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Frontend of ipython working with python-zmq
2 """Frontend of ipython working with python-zmq
3
3
4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
5
5
6 For more details, see the ipython-zmq design
6 For more details, see the ipython-zmq design
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 from __future__ import print_function
18 from __future__ import print_function
19
19
20 import bdb
20 import bdb
21 import signal
21 import signal
22 import sys
22 import sys
23 import time
23 import time
24
24
25 from Queue import Empty
25 from Queue import Empty
26
26
27 from IPython.core.alias import AliasManager, AliasError
27 from IPython.core.alias import AliasManager, AliasError
28 from IPython.core import page
28 from IPython.core import page
29 from IPython.utils.warn import warn, error, fatal
29 from IPython.utils.warn import warn, error, fatal
30 from IPython.utils import io
30 from IPython.utils import io
31
31
32 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
32 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
33 from IPython.frontend.terminal.console.completer import ZMQCompleter
33 from IPython.frontend.terminal.console.completer import ZMQCompleter
34
34
35
35
36 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
36 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
37 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
37 """A subclass of TerminalInteractiveShell that uses the 0MQ kernel"""
38 _executing = False
38 _executing = False
39
39
40 def __init__(self, *args, **kwargs):
40 def __init__(self, *args, **kwargs):
41 self.km = kwargs.pop('kernel_manager')
41 self.km = kwargs.pop('kernel_manager')
42 self.session_id = self.km.session.session
42 self.session_id = self.km.session.session
43 super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
43 super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
44
44
45 def init_completer(self):
45 def init_completer(self):
46 """Initialize the completion machinery.
46 """Initialize the completion machinery.
47
47
48 This creates completion machinery that can be used by client code,
48 This creates completion machinery that can be used by client code,
49 either interactively in-process (typically triggered by the readline
49 either interactively in-process (typically triggered by the readline
50 library), programatically (such as in test suites) or out-of-prcess
50 library), programatically (such as in test suites) or out-of-prcess
51 (typically over the network by remote frontends).
51 (typically over the network by remote frontends).
52 """
52 """
53 from IPython.core.completerlib import (module_completer,
53 from IPython.core.completerlib import (module_completer,
54 magic_run_completer, cd_completer)
54 magic_run_completer, cd_completer)
55
55
56 self.Completer = ZMQCompleter(self, self.km)
56 self.Completer = ZMQCompleter(self, self.km)
57
57
58
58
59 self.set_hook('complete_command', module_completer, str_key = 'import')
59 self.set_hook('complete_command', module_completer, str_key = 'import')
60 self.set_hook('complete_command', module_completer, str_key = 'from')
60 self.set_hook('complete_command', module_completer, str_key = 'from')
61 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
61 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
62 self.set_hook('complete_command', cd_completer, str_key = '%cd')
62 self.set_hook('complete_command', cd_completer, str_key = '%cd')
63
63
64 # Only configure readline if we truly are using readline. IPython can
64 # Only configure readline if we truly are using readline. IPython can
65 # do tab-completion over the network, in GUIs, etc, where readline
65 # do tab-completion over the network, in GUIs, etc, where readline
66 # itself may be absent
66 # itself may be absent
67 if self.has_readline:
67 if self.has_readline:
68 self.set_readline_completer()
68 self.set_readline_completer()
69
69
70 def run_cell(self, cell, store_history=True):
70 def run_cell(self, cell, store_history=True):
71 """Run a complete IPython cell.
71 """Run a complete IPython cell.
72
72
73 Parameters
73 Parameters
74 ----------
74 ----------
75 cell : str
75 cell : str
76 The code (including IPython code such as %magic functions) to run.
76 The code (including IPython code such as %magic functions) to run.
77 store_history : bool
77 store_history : bool
78 If True, the raw and translated cell will be stored in IPython's
78 If True, the raw and translated cell will be stored in IPython's
79 history. For user code calling back into IPython's machinery, this
79 history. For user code calling back into IPython's machinery, this
80 should be set to False.
80 should be set to False.
81 """
81 """
82 if (not cell) or cell.isspace():
82 if (not cell) or cell.isspace():
83 return
83 return
84
84
85 if cell.strip() == 'exit':
85 if cell.strip() == 'exit':
86 # explicitly handle 'exit' command
86 # explicitly handle 'exit' command
87 return self.ask_exit()
87 return self.ask_exit()
88
88
89 self._executing = True
89 self._executing = True
90 # flush stale replies, which could have been ignored, due to missed heartbeats
90 # flush stale replies, which could have been ignored, due to missed heartbeats
91 while self.km.shell_channel.msg_ready():
91 while self.km.shell_channel.msg_ready():
92 self.km.shell_channel.get_msg()
92 self.km.shell_channel.get_msg()
93 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
93 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
94 msg_id = self.km.shell_channel.execute(cell, not store_history)
94 msg_id = self.km.shell_channel.execute(cell, not store_history)
95 while not self.km.shell_channel.msg_ready() and self.km.is_alive:
95 while not self.km.shell_channel.msg_ready() and self.km.is_alive:
96 try:
96 try:
97 self.handle_stdin_request(timeout=0.05)
97 self.handle_stdin_request(timeout=0.05)
98 except Empty:
98 except Empty:
99 # display intermediate print statements, etc.
99 # display intermediate print statements, etc.
100 self.handle_iopub()
100 self.handle_iopub()
101 pass
101 pass
102 if self.km.shell_channel.msg_ready():
102 if self.km.shell_channel.msg_ready():
103 self.handle_execute_reply(msg_id)
103 self.handle_execute_reply(msg_id)
104 self._executing = False
104 self._executing = False
105
105
106 #-----------------
106 #-----------------
107 # message handlers
107 # message handlers
108 #-----------------
108 #-----------------
109
109
110 def handle_execute_reply(self, msg_id):
110 def handle_execute_reply(self, msg_id):
111 msg = self.km.shell_channel.get_msg()
111 msg = self.km.shell_channel.get_msg()
112 if msg["parent_header"].get("msg_id", None) == msg_id:
112 if msg["parent_header"].get("msg_id", None) == msg_id:
113
113
114 self.handle_iopub()
114 self.handle_iopub()
115
115
116 content = msg["content"]
116 content = msg["content"]
117 status = content['status']
117 status = content['status']
118
118
119 if status == 'aborted':
119 if status == 'aborted':
120 self.write('Aborted\n')
120 self.write('Aborted\n')
121 return
121 return
122 elif status == 'ok':
122 elif status == 'ok':
123 # print execution payloads as well:
123 # print execution payloads as well:
124 for item in content["payload"]:
124 for item in content["payload"]:
125 text = item.get('text', None)
125 text = item.get('text', None)
126 if text:
126 if text:
127 page.page(text)
127 page.page(text)
128
128
129 elif status == 'error':
129 elif status == 'error':
130 for frame in content["traceback"]:
130 for frame in content["traceback"]:
131 print(frame, file=io.stderr)
131 print(frame, file=io.stderr)
132
132
133 self.execution_count = int(content["execution_count"] + 1)
133 self.execution_count = int(content["execution_count"] + 1)
134
134
135
135
136 def handle_iopub(self):
136 def handle_iopub(self):
137 """ Method to procces subscribe channel's messages
137 """ Method to procces subscribe channel's messages
138
138
139 This method reads a message and processes the content in different
139 This method reads a message and processes the content in different
140 outputs like stdout, stderr, pyout and status
140 outputs like stdout, stderr, pyout and status
141
141
142 Arguments:
142 Arguments:
143 sub_msg: message receive from kernel in the sub socket channel
143 sub_msg: message receive from kernel in the sub socket channel
144 capture by kernel manager.
144 capture by kernel manager.
145 """
145 """
146 while self.km.sub_channel.msg_ready():
146 while self.km.sub_channel.msg_ready():
147 sub_msg = self.km.sub_channel.get_msg()
147 sub_msg = self.km.sub_channel.get_msg()
148 msg_type = sub_msg['header']['msg_type']
148 msg_type = sub_msg['header']['msg_type']
149 parent = sub_msg["parent_header"]
149 parent = sub_msg["parent_header"]
150 if (not parent) or self.session_id == parent['session']:
150 if (not parent) or self.session_id == parent['session']:
151 if msg_type == 'status' :
151 if msg_type == 'status' :
152 if sub_msg["content"]["execution_state"] == "busy" :
152 if sub_msg["content"]["execution_state"] == "busy" :
153 pass
153 pass
154
154
155 elif msg_type == 'stream' :
155 elif msg_type == 'stream' :
156 if sub_msg["content"]["name"] == "stdout":
156 if sub_msg["content"]["name"] == "stdout":
157 print(sub_msg["content"]["data"], file=io.stdout, end="")
157 print(sub_msg["content"]["data"], file=io.stdout, end="")
158 io.stdout.flush()
158 io.stdout.flush()
159 elif sub_msg["content"]["name"] == "stderr" :
159 elif sub_msg["content"]["name"] == "stderr" :
160 print(sub_msg["content"]["data"], file=io.stderr, end="")
160 print(sub_msg["content"]["data"], file=io.stderr, end="")
161 io.stderr.flush()
161 io.stderr.flush()
162
162
163 elif msg_type == 'pyout':
163 elif msg_type == 'pyout':
164 self.execution_count = int(sub_msg["content"]["execution_count"])
164 self.execution_count = int(sub_msg["content"]["execution_count"])
165 format_dict = sub_msg["content"]["data"]
165 format_dict = sub_msg["content"]["data"]
166 # taken from DisplayHook.__call__:
166 # taken from DisplayHook.__call__:
167 hook = self.displayhook
167 hook = self.displayhook
168 hook.start_displayhook()
168 hook.start_displayhook()
169 hook.write_output_prompt()
169 hook.write_output_prompt()
170 hook.write_format_data(format_dict)
170 hook.write_format_data(format_dict)
171 hook.log_output(format_dict)
171 hook.log_output(format_dict)
172 hook.finish_displayhook()
172 hook.finish_displayhook()
173
173
174 def handle_stdin_request(self, timeout=0.1):
174 def handle_stdin_request(self, timeout=0.1):
175 """ Method to capture raw_input
175 """ Method to capture raw_input
176 """
176 """
177 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
177 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
178 # in case any iopub came while we were waiting:
178 # in case any iopub came while we were waiting:
179 self.handle_iopub()
179 self.handle_iopub()
180 if self.session_id == msg_rep["parent_header"].get("session"):
180 if self.session_id == msg_rep["parent_header"].get("session"):
181 # wrap SIGINT handler
181 # wrap SIGINT handler
182 real_handler = signal.getsignal(signal.SIGINT)
182 real_handler = signal.getsignal(signal.SIGINT)
183 def double_int(sig,frame):
183 def double_int(sig,frame):
184 # call real handler (forwards sigint to kernel),
184 # call real handler (forwards sigint to kernel),
185 # then raise local interrupt, stopping local raw_input
185 # then raise local interrupt, stopping local raw_input
186 real_handler(sig,frame)
186 real_handler(sig,frame)
187 raise KeyboardInterrupt
187 raise KeyboardInterrupt
188 signal.signal(signal.SIGINT, double_int)
188 signal.signal(signal.SIGINT, double_int)
189
189
190 try:
190 try:
191 raw_data = raw_input(msg_rep["content"]["prompt"])
191 raw_data = raw_input(msg_rep["content"]["prompt"])
192 except EOFError:
192 except EOFError:
193 # turn EOFError into EOF character
193 # turn EOFError into EOF character
194 raw_data = '\x04'
194 raw_data = '\x04'
195 except KeyboardInterrupt:
195 except KeyboardInterrupt:
196 sys.stdout.write('\n')
196 sys.stdout.write('\n')
197 return
197 return
198 finally:
198 finally:
199 # restore SIGINT handler
199 # restore SIGINT handler
200 signal.signal(signal.SIGINT, real_handler)
200 signal.signal(signal.SIGINT, real_handler)
201
201
202 # only send stdin reply if there *was not* another request
202 # only send stdin reply if there *was not* another request
203 # or execution finished while we were reading.
203 # or execution finished while we were reading.
204 if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()):
204 if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()):
205 self.km.stdin_channel.input(raw_data)
205 self.km.stdin_channel.input(raw_data)
206
206
207 def mainloop(self, display_banner=False):
207 def mainloop(self, display_banner=False):
208 while True:
208 while True:
209 try:
209 try:
210 self.interact(display_banner=display_banner)
210 self.interact(display_banner=display_banner)
211 #self.interact_with_readline()
211 #self.interact_with_readline()
212 # XXX for testing of a readline-decoupled repl loop, call
212 # XXX for testing of a readline-decoupled repl loop, call
213 # interact_with_readline above
213 # interact_with_readline above
214 break
214 break
215 except KeyboardInterrupt:
215 except KeyboardInterrupt:
216 # this should not be necessary, but KeyboardInterrupt
216 # this should not be necessary, but KeyboardInterrupt
217 # handling seems rather unpredictable...
217 # handling seems rather unpredictable...
218 self.write("\nKeyboardInterrupt in interact()\n")
218 self.write("\nKeyboardInterrupt in interact()\n")
219
219
220 def wait_for_kernel(self, timeout=None):
220 def wait_for_kernel(self, timeout=None):
221 """method to wait for a kernel to be ready"""
221 """method to wait for a kernel to be ready"""
222 tic = time.time()
222 tic = time.time()
223 self.km.hb_channel.unpause()
223 self.km.hb_channel.unpause()
224 while True:
224 while True:
225 self.run_cell('1', False)
225 self.run_cell('1', False)
226 if self.km.hb_channel.is_beating():
226 if self.km.hb_channel.is_beating():
227 # heart failure was not the reason this returned
227 # heart failure was not the reason this returned
228 break
228 break
229 else:
229 else:
230 # heart failed
230 # heart failed
231 if timeout is not None and (time.time() - tic) > timeout:
231 if timeout is not None and (time.time() - tic) > timeout:
232 return False
232 return False
233 return True
233 return True
234
234
235 def interact(self, display_banner=None):
235 def interact(self, display_banner=None):
236 """Closely emulate the interactive Python console."""
236 """Closely emulate the interactive Python console."""
237
237
238 # batch run -> do not interact
238 # batch run -> do not interact
239 if self.exit_now:
239 if self.exit_now:
240 return
240 return
241
241
242 if display_banner is None:
242 if display_banner is None:
243 display_banner = self.display_banner
243 display_banner = self.display_banner
244
244
245 if isinstance(display_banner, basestring):
245 if isinstance(display_banner, basestring):
246 self.show_banner(display_banner)
246 self.show_banner(display_banner)
247 elif display_banner:
247 elif display_banner:
248 self.show_banner()
248 self.show_banner()
249
249
250 more = False
250 more = False
251
251
252 # run a non-empty no-op, so that we don't get a prompt until
252 # run a non-empty no-op, so that we don't get a prompt until
253 # we know the kernel is ready. This keeps the connection
253 # we know the kernel is ready. This keeps the connection
254 # message above the first prompt.
254 # message above the first prompt.
255 if not self.wait_for_kernel(3):
255 if not self.wait_for_kernel(3):
256 error("Kernel did not respond\n")
256 error("Kernel did not respond\n")
257 return
257 return
258
258
259 if self.has_readline:
259 if self.has_readline:
260 self.readline_startup_hook(self.pre_readline)
260 self.readline_startup_hook(self.pre_readline)
261 hlen_b4_cell = self.readline.get_current_history_length()
261 hlen_b4_cell = self.readline.get_current_history_length()
262 else:
262 else:
263 hlen_b4_cell = 0
263 hlen_b4_cell = 0
264 # exit_now is set by a call to %Exit or %Quit, through the
264 # exit_now is set by a call to %Exit or %Quit, through the
265 # ask_exit callback.
265 # ask_exit callback.
266
266
267 while not self.exit_now:
267 while not self.exit_now:
268 if not self.km.is_alive:
268 if not self.km.is_alive:
269 # kernel died, prompt for action or exit
269 # kernel died, prompt for action or exit
270 action = "restart" if self.km.has_kernel else "wait for restart"
270 action = "restart" if self.km.has_kernel else "wait for restart"
271 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
271 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
272 if ans:
272 if ans:
273 if self.km.has_kernel:
273 if self.km.has_kernel:
274 self.km.restart_kernel(True)
274 self.km.restart_kernel(True)
275 self.wait_for_kernel(3)
275 self.wait_for_kernel(3)
276 else:
276 else:
277 self.exit_now = True
277 self.exit_now = True
278 continue
278 continue
279 try:
279 try:
280 # protect prompt block from KeyboardInterrupt
280 # protect prompt block from KeyboardInterrupt
281 # when sitting on ctrl-C
281 # when sitting on ctrl-C
282 self.hooks.pre_prompt_hook()
282 self.hooks.pre_prompt_hook()
283 if more:
283 if more:
284 try:
284 try:
285 prompt = self.prompt_manager.render('in2')
285 prompt = self.prompt_manager.render('in2')
286 except Exception:
286 except Exception:
287 self.showtraceback()
287 self.showtraceback()
288 if self.autoindent:
288 if self.autoindent:
289 self.rl_do_indent = True
289 self.rl_do_indent = True
290
290
291 else:
291 else:
292 try:
292 try:
293 prompt = self.separate_in + self.prompt_manager.render('in')
293 prompt = self.separate_in + self.prompt_manager.render('in')
294 except Exception:
294 except Exception:
295 self.showtraceback()
295 self.showtraceback()
296
296
297 line = self.raw_input(prompt)
297 line = self.raw_input(prompt)
298 if self.exit_now:
298 if self.exit_now:
299 # quick exit on sys.std[in|out] close
299 # quick exit on sys.std[in|out] close
300 break
300 break
301 if self.autoindent:
301 if self.autoindent:
302 self.rl_do_indent = False
302 self.rl_do_indent = False
303
303
304 except KeyboardInterrupt:
304 except KeyboardInterrupt:
305 #double-guard against keyboardinterrupts during kbdint handling
305 #double-guard against keyboardinterrupts during kbdint handling
306 try:
306 try:
307 self.write('\nKeyboardInterrupt\n')
307 self.write('\nKeyboardInterrupt\n')
308 source_raw = self.input_splitter.source_raw_reset()[1]
308 source_raw = self.input_splitter.source_raw_reset()[1]
309 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
309 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
310 more = False
310 more = False
311 except KeyboardInterrupt:
311 except KeyboardInterrupt:
312 pass
312 pass
313 except EOFError:
313 except EOFError:
314 if self.autoindent:
314 if self.autoindent:
315 self.rl_do_indent = False
315 self.rl_do_indent = False
316 if self.has_readline:
316 if self.has_readline:
317 self.readline_startup_hook(None)
317 self.readline_startup_hook(None)
318 self.write('\n')
318 self.write('\n')
319 self.exit()
319 self.exit()
320 except bdb.BdbQuit:
320 except bdb.BdbQuit:
321 warn('The Python debugger has exited with a BdbQuit exception.\n'
321 warn('The Python debugger has exited with a BdbQuit exception.\n'
322 'Because of how pdb handles the stack, it is impossible\n'
322 'Because of how pdb handles the stack, it is impossible\n'
323 'for IPython to properly format this particular exception.\n'
323 'for IPython to properly format this particular exception.\n'
324 'IPython will resume normal operation.')
324 'IPython will resume normal operation.')
325 except:
325 except:
326 # exceptions here are VERY RARE, but they can be triggered
326 # exceptions here are VERY RARE, but they can be triggered
327 # asynchronously by signal handlers, for example.
327 # asynchronously by signal handlers, for example.
328 self.showtraceback()
328 self.showtraceback()
329 else:
329 else:
330 self.input_splitter.push(line)
330 self.input_splitter.push(line)
331 more = self.input_splitter.push_accepts_more()
331 more = self.input_splitter.push_accepts_more()
332 if (self.SyntaxTB.last_syntax_error and
332 if (self.SyntaxTB.last_syntax_error and
333 self.autoedit_syntax):
333 self.autoedit_syntax):
334 self.edit_syntax_error()
334 self.edit_syntax_error()
335 if not more:
335 if not more:
336 source_raw = self.input_splitter.source_reset()
336 source_raw = self.input_splitter.source_raw_reset()[1]
337 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
337 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
338 self.run_cell(source_raw)
338 self.run_cell(source_raw)
339
339
340
340
341 # Turn off the exit flag, so the mainloop can be restarted if desired
341 # Turn off the exit flag, so the mainloop can be restarted if desired
342 self.exit_now = False
342 self.exit_now = False
General Comments 0
You need to be logged in to leave comments. Login now