##// END OF EJS Templates
protect term console from empty parent header (startup messages)
MinRK -
Show More
@@ -1,337 +1,338 b''
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 self._executing = True
85 self._executing = True
86 # flush stale replies, which could have been ignored, due to missed heartbeats
86 # flush stale replies, which could have been ignored, due to missed heartbeats
87 while self.km.shell_channel.msg_ready():
87 while self.km.shell_channel.msg_ready():
88 self.km.shell_channel.get_msg()
88 self.km.shell_channel.get_msg()
89 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
89 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
90 msg_id = self.km.shell_channel.execute(cell, not store_history)
90 msg_id = self.km.shell_channel.execute(cell, not store_history)
91 while not self.km.shell_channel.msg_ready() and self.km.is_alive:
91 while not self.km.shell_channel.msg_ready() and self.km.is_alive:
92 try:
92 try:
93 self.handle_stdin_request(timeout=0.05)
93 self.handle_stdin_request(timeout=0.05)
94 except Empty:
94 except Empty:
95 # display intermediate print statements, etc.
95 # display intermediate print statements, etc.
96 self.handle_iopub()
96 self.handle_iopub()
97 pass
97 pass
98 if self.km.shell_channel.msg_ready():
98 if self.km.shell_channel.msg_ready():
99 self.handle_execute_reply(msg_id)
99 self.handle_execute_reply(msg_id)
100 self._executing = False
100 self._executing = False
101
101
102 #-----------------
102 #-----------------
103 # message handlers
103 # message handlers
104 #-----------------
104 #-----------------
105
105
106 def handle_execute_reply(self, msg_id):
106 def handle_execute_reply(self, msg_id):
107 msg = self.km.shell_channel.get_msg()
107 msg = self.km.shell_channel.get_msg()
108 if msg["parent_header"].get("msg_id", None) == msg_id:
108 if msg["parent_header"].get("msg_id", None) == msg_id:
109
109
110 self.handle_iopub()
110 self.handle_iopub()
111
111
112 content = msg["content"]
112 content = msg["content"]
113 status = content['status']
113 status = content['status']
114
114
115 if status == 'aborted':
115 if status == 'aborted':
116 self.write('Aborted\n')
116 self.write('Aborted\n')
117 return
117 return
118 elif status == 'ok':
118 elif status == 'ok':
119 # print execution payloads as well:
119 # print execution payloads as well:
120 for item in content["payload"]:
120 for item in content["payload"]:
121 text = item.get('text', None)
121 text = item.get('text', None)
122 if text:
122 if text:
123 page.page(text)
123 page.page(text)
124
124
125 elif status == 'error':
125 elif status == 'error':
126 for frame in content["traceback"]:
126 for frame in content["traceback"]:
127 print(frame, file=io.stderr)
127 print(frame, file=io.stderr)
128
128
129 self.execution_count = int(content["execution_count"] + 1)
129 self.execution_count = int(content["execution_count"] + 1)
130
130
131
131
132 def handle_iopub(self):
132 def handle_iopub(self):
133 """ Method to procces subscribe channel's messages
133 """ Method to procces subscribe channel's messages
134
134
135 This method reads a message and processes the content in different
135 This method reads a message and processes the content in different
136 outputs like stdout, stderr, pyout and status
136 outputs like stdout, stderr, pyout and status
137
137
138 Arguments:
138 Arguments:
139 sub_msg: message receive from kernel in the sub socket channel
139 sub_msg: message receive from kernel in the sub socket channel
140 capture by kernel manager.
140 capture by kernel manager.
141 """
141 """
142 while self.km.sub_channel.msg_ready():
142 while self.km.sub_channel.msg_ready():
143 sub_msg = self.km.sub_channel.get_msg()
143 sub_msg = self.km.sub_channel.get_msg()
144 msg_type = sub_msg['header']['msg_type']
144 msg_type = sub_msg['header']['msg_type']
145 if self.session_id == sub_msg['parent_header']['session']:
145 parent = sub_msg["parent_header"]
146 if (not parent) or self.session_id == parent['session']:
146 if msg_type == 'status' :
147 if msg_type == 'status' :
147 if sub_msg["content"]["execution_state"] == "busy" :
148 if sub_msg["content"]["execution_state"] == "busy" :
148 pass
149 pass
149
150
150 elif msg_type == 'stream' :
151 elif msg_type == 'stream' :
151 if sub_msg["content"]["name"] == "stdout":
152 if sub_msg["content"]["name"] == "stdout":
152 print(sub_msg["content"]["data"], file=io.stdout, end="")
153 print(sub_msg["content"]["data"], file=io.stdout, end="")
153 io.stdout.flush()
154 io.stdout.flush()
154 elif sub_msg["content"]["name"] == "stderr" :
155 elif sub_msg["content"]["name"] == "stderr" :
155 print(sub_msg["content"]["data"], file=io.stderr, end="")
156 print(sub_msg["content"]["data"], file=io.stderr, end="")
156 io.stderr.flush()
157 io.stderr.flush()
157
158
158 elif msg_type == 'pyout':
159 elif msg_type == 'pyout':
159 self.execution_count = int(sub_msg["content"]["execution_count"])
160 self.execution_count = int(sub_msg["content"]["execution_count"])
160 format_dict = sub_msg["content"]["data"]
161 format_dict = sub_msg["content"]["data"]
161 # taken from DisplayHook.__call__:
162 # taken from DisplayHook.__call__:
162 hook = self.displayhook
163 hook = self.displayhook
163 hook.start_displayhook()
164 hook.start_displayhook()
164 hook.write_output_prompt()
165 hook.write_output_prompt()
165 hook.write_format_data(format_dict)
166 hook.write_format_data(format_dict)
166 hook.log_output(format_dict)
167 hook.log_output(format_dict)
167 hook.finish_displayhook()
168 hook.finish_displayhook()
168
169
169 def handle_stdin_request(self, timeout=0.1):
170 def handle_stdin_request(self, timeout=0.1):
170 """ Method to capture raw_input
171 """ Method to capture raw_input
171 """
172 """
172 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
173 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
173 # in case any iopub came while we were waiting:
174 # in case any iopub came while we were waiting:
174 self.handle_iopub()
175 self.handle_iopub()
175 if self.session_id == msg_rep["parent_header"]["session"]:
176 if self.session_id == msg_rep["parent_header"].get("session"):
176 # wrap SIGINT handler
177 # wrap SIGINT handler
177 real_handler = signal.getsignal(signal.SIGINT)
178 real_handler = signal.getsignal(signal.SIGINT)
178 def double_int(sig,frame):
179 def double_int(sig,frame):
179 # call real handler (forwards sigint to kernel),
180 # call real handler (forwards sigint to kernel),
180 # then raise local interrupt, stopping local raw_input
181 # then raise local interrupt, stopping local raw_input
181 real_handler(sig,frame)
182 real_handler(sig,frame)
182 raise KeyboardInterrupt
183 raise KeyboardInterrupt
183 signal.signal(signal.SIGINT, double_int)
184 signal.signal(signal.SIGINT, double_int)
184
185
185 try:
186 try:
186 raw_data = raw_input(msg_rep["content"]["prompt"])
187 raw_data = raw_input(msg_rep["content"]["prompt"])
187 except EOFError:
188 except EOFError:
188 # turn EOFError into EOF character
189 # turn EOFError into EOF character
189 raw_data = '\x04'
190 raw_data = '\x04'
190 except KeyboardInterrupt:
191 except KeyboardInterrupt:
191 sys.stdout.write('\n')
192 sys.stdout.write('\n')
192 return
193 return
193 finally:
194 finally:
194 # restore SIGINT handler
195 # restore SIGINT handler
195 signal.signal(signal.SIGINT, real_handler)
196 signal.signal(signal.SIGINT, real_handler)
196
197
197 # only send stdin reply if there *was not* another request
198 # only send stdin reply if there *was not* another request
198 # or execution finished while we were reading.
199 # or execution finished while we were reading.
199 if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()):
200 if not (self.km.stdin_channel.msg_ready() or self.km.shell_channel.msg_ready()):
200 self.km.stdin_channel.input(raw_data)
201 self.km.stdin_channel.input(raw_data)
201
202
202 def mainloop(self, display_banner=False):
203 def mainloop(self, display_banner=False):
203 while True:
204 while True:
204 try:
205 try:
205 self.interact(display_banner=display_banner)
206 self.interact(display_banner=display_banner)
206 #self.interact_with_readline()
207 #self.interact_with_readline()
207 # XXX for testing of a readline-decoupled repl loop, call
208 # XXX for testing of a readline-decoupled repl loop, call
208 # interact_with_readline above
209 # interact_with_readline above
209 break
210 break
210 except KeyboardInterrupt:
211 except KeyboardInterrupt:
211 # this should not be necessary, but KeyboardInterrupt
212 # this should not be necessary, but KeyboardInterrupt
212 # handling seems rather unpredictable...
213 # handling seems rather unpredictable...
213 self.write("\nKeyboardInterrupt in interact()\n")
214 self.write("\nKeyboardInterrupt in interact()\n")
214
215
215 def wait_for_kernel(self, timeout=None):
216 def wait_for_kernel(self, timeout=None):
216 """method to wait for a kernel to be ready"""
217 """method to wait for a kernel to be ready"""
217 tic = time.time()
218 tic = time.time()
218 self.km.hb_channel.unpause()
219 self.km.hb_channel.unpause()
219 while True:
220 while True:
220 self.run_cell('1', False)
221 self.run_cell('1', False)
221 if self.km.hb_channel.is_beating():
222 if self.km.hb_channel.is_beating():
222 # heart failure was not the reason this returned
223 # heart failure was not the reason this returned
223 break
224 break
224 else:
225 else:
225 # heart failed
226 # heart failed
226 if timeout is not None and (time.time() - tic) > timeout:
227 if timeout is not None and (time.time() - tic) > timeout:
227 return False
228 return False
228 return True
229 return True
229
230
230 def interact(self, display_banner=None):
231 def interact(self, display_banner=None):
231 """Closely emulate the interactive Python console."""
232 """Closely emulate the interactive Python console."""
232
233
233 # batch run -> do not interact
234 # batch run -> do not interact
234 if self.exit_now:
235 if self.exit_now:
235 return
236 return
236
237
237 if display_banner is None:
238 if display_banner is None:
238 display_banner = self.display_banner
239 display_banner = self.display_banner
239
240
240 if isinstance(display_banner, basestring):
241 if isinstance(display_banner, basestring):
241 self.show_banner(display_banner)
242 self.show_banner(display_banner)
242 elif display_banner:
243 elif display_banner:
243 self.show_banner()
244 self.show_banner()
244
245
245 more = False
246 more = False
246
247
247 # run a non-empty no-op, so that we don't get a prompt until
248 # run a non-empty no-op, so that we don't get a prompt until
248 # we know the kernel is ready. This keeps the connection
249 # we know the kernel is ready. This keeps the connection
249 # message above the first prompt.
250 # message above the first prompt.
250 if not self.wait_for_kernel(3):
251 if not self.wait_for_kernel(3):
251 error("Kernel did not respond\n")
252 error("Kernel did not respond\n")
252 return
253 return
253
254
254 if self.has_readline:
255 if self.has_readline:
255 self.readline_startup_hook(self.pre_readline)
256 self.readline_startup_hook(self.pre_readline)
256 hlen_b4_cell = self.readline.get_current_history_length()
257 hlen_b4_cell = self.readline.get_current_history_length()
257 else:
258 else:
258 hlen_b4_cell = 0
259 hlen_b4_cell = 0
259 # exit_now is set by a call to %Exit or %Quit, through the
260 # exit_now is set by a call to %Exit or %Quit, through the
260 # ask_exit callback.
261 # ask_exit callback.
261
262
262 while not self.exit_now:
263 while not self.exit_now:
263 if not self.km.is_alive:
264 if not self.km.is_alive:
264 # kernel died, prompt for action or exit
265 # kernel died, prompt for action or exit
265 action = "restart" if self.km.has_kernel else "wait for restart"
266 action = "restart" if self.km.has_kernel else "wait for restart"
266 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
267 ans = self.ask_yes_no("kernel died, %s ([y]/n)?" % action, default='y')
267 if ans:
268 if ans:
268 if self.km.has_kernel:
269 if self.km.has_kernel:
269 self.km.restart_kernel(True)
270 self.km.restart_kernel(True)
270 self.wait_for_kernel(3)
271 self.wait_for_kernel(3)
271 else:
272 else:
272 self.exit_now = True
273 self.exit_now = True
273 continue
274 continue
274 try:
275 try:
275 # protect prompt block from KeyboardInterrupt
276 # protect prompt block from KeyboardInterrupt
276 # when sitting on ctrl-C
277 # when sitting on ctrl-C
277 self.hooks.pre_prompt_hook()
278 self.hooks.pre_prompt_hook()
278 if more:
279 if more:
279 try:
280 try:
280 prompt = self.prompt_manager.render('in2')
281 prompt = self.prompt_manager.render('in2')
281 except Exception:
282 except Exception:
282 self.showtraceback()
283 self.showtraceback()
283 if self.autoindent:
284 if self.autoindent:
284 self.rl_do_indent = True
285 self.rl_do_indent = True
285
286
286 else:
287 else:
287 try:
288 try:
288 prompt = self.separate_in + self.prompt_manager.render('in')
289 prompt = self.separate_in + self.prompt_manager.render('in')
289 except Exception:
290 except Exception:
290 self.showtraceback()
291 self.showtraceback()
291
292
292 line = self.raw_input(prompt)
293 line = self.raw_input(prompt)
293 if self.exit_now:
294 if self.exit_now:
294 # quick exit on sys.std[in|out] close
295 # quick exit on sys.std[in|out] close
295 break
296 break
296 if self.autoindent:
297 if self.autoindent:
297 self.rl_do_indent = False
298 self.rl_do_indent = False
298
299
299 except KeyboardInterrupt:
300 except KeyboardInterrupt:
300 #double-guard against keyboardinterrupts during kbdint handling
301 #double-guard against keyboardinterrupts during kbdint handling
301 try:
302 try:
302 self.write('\nKeyboardInterrupt\n')
303 self.write('\nKeyboardInterrupt\n')
303 source_raw = self.input_splitter.source_raw_reset()[1]
304 source_raw = self.input_splitter.source_raw_reset()[1]
304 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
305 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
305 more = False
306 more = False
306 except KeyboardInterrupt:
307 except KeyboardInterrupt:
307 pass
308 pass
308 except EOFError:
309 except EOFError:
309 if self.autoindent:
310 if self.autoindent:
310 self.rl_do_indent = False
311 self.rl_do_indent = False
311 if self.has_readline:
312 if self.has_readline:
312 self.readline_startup_hook(None)
313 self.readline_startup_hook(None)
313 self.write('\n')
314 self.write('\n')
314 self.exit()
315 self.exit()
315 except bdb.BdbQuit:
316 except bdb.BdbQuit:
316 warn('The Python debugger has exited with a BdbQuit exception.\n'
317 warn('The Python debugger has exited with a BdbQuit exception.\n'
317 'Because of how pdb handles the stack, it is impossible\n'
318 'Because of how pdb handles the stack, it is impossible\n'
318 'for IPython to properly format this particular exception.\n'
319 'for IPython to properly format this particular exception.\n'
319 'IPython will resume normal operation.')
320 'IPython will resume normal operation.')
320 except:
321 except:
321 # exceptions here are VERY RARE, but they can be triggered
322 # exceptions here are VERY RARE, but they can be triggered
322 # asynchronously by signal handlers, for example.
323 # asynchronously by signal handlers, for example.
323 self.showtraceback()
324 self.showtraceback()
324 else:
325 else:
325 self.input_splitter.push(line)
326 self.input_splitter.push(line)
326 more = self.input_splitter.push_accepts_more()
327 more = self.input_splitter.push_accepts_more()
327 if (self.SyntaxTB.last_syntax_error and
328 if (self.SyntaxTB.last_syntax_error and
328 self.autoedit_syntax):
329 self.autoedit_syntax):
329 self.edit_syntax_error()
330 self.edit_syntax_error()
330 if not more:
331 if not more:
331 source_raw = self.input_splitter.source_reset()
332 source_raw = self.input_splitter.source_reset()
332 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
333 hlen_b4_cell = self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
333 self.run_cell(source_raw)
334 self.run_cell(source_raw)
334
335
335
336
336 # Turn off the exit flag, so the mainloop can be restarted if desired
337 # Turn off the exit flag, so the mainloop can be restarted if desired
337 self.exit_now = False
338 self.exit_now = False
General Comments 0
You need to be logged in to leave comments. Login now