##// END OF EJS Templates
Register the Widget target with the comm manager after construction.
Jonathan Frederic -
Show More
@@ -1,320 +1,323 b''
1 """The IPython kernel implementation"""
1 """The IPython kernel implementation"""
2
2
3 import getpass
3 import getpass
4 import sys
4 import sys
5 import traceback
5 import traceback
6
6
7 from IPython.core import release
7 from IPython.core import release
8 from IPython.html.widgets import Widget
8 from IPython.utils.py3compat import builtin_mod, PY3
9 from IPython.utils.py3compat import builtin_mod, PY3
9 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
10 from IPython.utils.tokenutil import token_at_cursor, line_at_cursor
10 from IPython.utils.traitlets import Instance, Type, Any
11 from IPython.utils.traitlets import Instance, Type, Any
11 from IPython.utils.decorators import undoc
12 from IPython.utils.decorators import undoc
12
13
13 from ..comm import CommManager
14 from ..comm import CommManager
14 from .kernelbase import Kernel as KernelBase
15 from .kernelbase import Kernel as KernelBase
15 from .serialize import serialize_object, unpack_apply_message
16 from .serialize import serialize_object, unpack_apply_message
16 from .zmqshell import ZMQInteractiveShell
17 from .zmqshell import ZMQInteractiveShell
17
18
18 class IPythonKernel(KernelBase):
19 class IPythonKernel(KernelBase):
19 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
20 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
20 shell_class = Type(ZMQInteractiveShell)
21 shell_class = Type(ZMQInteractiveShell)
21
22
22 user_module = Any()
23 user_module = Any()
23 def _user_module_changed(self, name, old, new):
24 def _user_module_changed(self, name, old, new):
24 if self.shell is not None:
25 if self.shell is not None:
25 self.shell.user_module = new
26 self.shell.user_module = new
26
27
27 user_ns = Instance(dict, args=None, allow_none=True)
28 user_ns = Instance(dict, args=None, allow_none=True)
28 def _user_ns_changed(self, name, old, new):
29 def _user_ns_changed(self, name, old, new):
29 if self.shell is not None:
30 if self.shell is not None:
30 self.shell.user_ns = new
31 self.shell.user_ns = new
31 self.shell.init_user_ns()
32 self.shell.init_user_ns()
32
33
33 # A reference to the Python builtin 'raw_input' function.
34 # A reference to the Python builtin 'raw_input' function.
34 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
35 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
35 _sys_raw_input = Any()
36 _sys_raw_input = Any()
36 _sys_eval_input = Any()
37 _sys_eval_input = Any()
37
38
38 def __init__(self, **kwargs):
39 def __init__(self, **kwargs):
39 super(IPythonKernel, self).__init__(**kwargs)
40 super(IPythonKernel, self).__init__(**kwargs)
40
41
41 # Initialize the InteractiveShell subclass
42 # Initialize the InteractiveShell subclass
42 self.shell = self.shell_class.instance(parent=self,
43 self.shell = self.shell_class.instance(parent=self,
43 profile_dir = self.profile_dir,
44 profile_dir = self.profile_dir,
44 user_module = self.user_module,
45 user_module = self.user_module,
45 user_ns = self.user_ns,
46 user_ns = self.user_ns,
46 kernel = self,
47 kernel = self,
47 )
48 )
48 self.shell.displayhook.session = self.session
49 self.shell.displayhook.session = self.session
49 self.shell.displayhook.pub_socket = self.iopub_socket
50 self.shell.displayhook.pub_socket = self.iopub_socket
50 self.shell.displayhook.topic = self._topic('execute_result')
51 self.shell.displayhook.topic = self._topic('execute_result')
51 self.shell.display_pub.session = self.session
52 self.shell.display_pub.session = self.session
52 self.shell.display_pub.pub_socket = self.iopub_socket
53 self.shell.display_pub.pub_socket = self.iopub_socket
53 self.shell.data_pub.session = self.session
54 self.shell.data_pub.session = self.session
54 self.shell.data_pub.pub_socket = self.iopub_socket
55 self.shell.data_pub.pub_socket = self.iopub_socket
55
56
56 # TMP - hack while developing
57 # TMP - hack while developing
57 self.shell._reply_content = None
58 self.shell._reply_content = None
58
59
59 self.comm_manager = CommManager(shell=self.shell, parent=self,
60 self.comm_manager = CommManager(shell=self.shell, parent=self,
60 kernel=self)
61 kernel=self)
62 self.comm_manager.register_target('ipython.widget', Widget.handle_comm_opened)
63
61 self.shell.configurables.append(self.comm_manager)
64 self.shell.configurables.append(self.comm_manager)
62 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
65 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
63 for msg_type in comm_msg_types:
66 for msg_type in comm_msg_types:
64 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
67 self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
65
68
66 # Kernel info fields
69 # Kernel info fields
67 implementation = 'ipython'
70 implementation = 'ipython'
68 implementation_version = release.version
71 implementation_version = release.version
69 language = 'python'
72 language = 'python'
70 language_version = sys.version.split()[0]
73 language_version = sys.version.split()[0]
71 @property
74 @property
72 def banner(self):
75 def banner(self):
73 return self.shell.banner
76 return self.shell.banner
74
77
75 def start(self):
78 def start(self):
76 self.shell.exit_now = False
79 self.shell.exit_now = False
77 super(IPythonKernel, self).start()
80 super(IPythonKernel, self).start()
78
81
79 def set_parent(self, ident, parent):
82 def set_parent(self, ident, parent):
80 """Overridden from parent to tell the display hook and output streams
83 """Overridden from parent to tell the display hook and output streams
81 about the parent message.
84 about the parent message.
82 """
85 """
83 super(IPythonKernel, self).set_parent(ident, parent)
86 super(IPythonKernel, self).set_parent(ident, parent)
84 self.shell.set_parent(parent)
87 self.shell.set_parent(parent)
85
88
86 def _forward_input(self, allow_stdin=False):
89 def _forward_input(self, allow_stdin=False):
87 """Forward raw_input and getpass to the current frontend.
90 """Forward raw_input and getpass to the current frontend.
88
91
89 via input_request
92 via input_request
90 """
93 """
91 self._allow_stdin = allow_stdin
94 self._allow_stdin = allow_stdin
92
95
93 if PY3:
96 if PY3:
94 self._sys_raw_input = builtin_mod.input
97 self._sys_raw_input = builtin_mod.input
95 builtin_mod.input = self.raw_input
98 builtin_mod.input = self.raw_input
96 else:
99 else:
97 self._sys_raw_input = builtin_mod.raw_input
100 self._sys_raw_input = builtin_mod.raw_input
98 self._sys_eval_input = builtin_mod.input
101 self._sys_eval_input = builtin_mod.input
99 builtin_mod.raw_input = self.raw_input
102 builtin_mod.raw_input = self.raw_input
100 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
103 builtin_mod.input = lambda prompt='': eval(self.raw_input(prompt))
101 self._save_getpass = getpass.getpass
104 self._save_getpass = getpass.getpass
102 getpass.getpass = self.getpass
105 getpass.getpass = self.getpass
103
106
104 def _restore_input(self):
107 def _restore_input(self):
105 """Restore raw_input, getpass"""
108 """Restore raw_input, getpass"""
106 if PY3:
109 if PY3:
107 builtin_mod.input = self._sys_raw_input
110 builtin_mod.input = self._sys_raw_input
108 else:
111 else:
109 builtin_mod.raw_input = self._sys_raw_input
112 builtin_mod.raw_input = self._sys_raw_input
110 builtin_mod.input = self._sys_eval_input
113 builtin_mod.input = self._sys_eval_input
111
114
112 getpass.getpass = self._save_getpass
115 getpass.getpass = self._save_getpass
113
116
114 @property
117 @property
115 def execution_count(self):
118 def execution_count(self):
116 return self.shell.execution_count
119 return self.shell.execution_count
117
120
118 @execution_count.setter
121 @execution_count.setter
119 def execution_count(self, value):
122 def execution_count(self, value):
120 # Ignore the incrememnting done by KernelBase, in favour of our shell's
123 # Ignore the incrememnting done by KernelBase, in favour of our shell's
121 # execution counter.
124 # execution counter.
122 pass
125 pass
123
126
124 def do_execute(self, code, silent, store_history=True,
127 def do_execute(self, code, silent, store_history=True,
125 user_expressions=None, allow_stdin=False):
128 user_expressions=None, allow_stdin=False):
126 shell = self.shell # we'll need this a lot here
129 shell = self.shell # we'll need this a lot here
127
130
128 self._forward_input(allow_stdin)
131 self._forward_input(allow_stdin)
129
132
130 reply_content = {}
133 reply_content = {}
131 # FIXME: the shell calls the exception handler itself.
134 # FIXME: the shell calls the exception handler itself.
132 shell._reply_content = None
135 shell._reply_content = None
133 try:
136 try:
134 shell.run_cell(code, store_history=store_history, silent=silent)
137 shell.run_cell(code, store_history=store_history, silent=silent)
135 except:
138 except:
136 status = u'error'
139 status = u'error'
137 # FIXME: this code right now isn't being used yet by default,
140 # FIXME: this code right now isn't being used yet by default,
138 # because the run_cell() call above directly fires off exception
141 # because the run_cell() call above directly fires off exception
139 # reporting. This code, therefore, is only active in the scenario
142 # reporting. This code, therefore, is only active in the scenario
140 # where runlines itself has an unhandled exception. We need to
143 # where runlines itself has an unhandled exception. We need to
141 # uniformize this, for all exception construction to come from a
144 # uniformize this, for all exception construction to come from a
142 # single location in the codbase.
145 # single location in the codbase.
143 etype, evalue, tb = sys.exc_info()
146 etype, evalue, tb = sys.exc_info()
144 tb_list = traceback.format_exception(etype, evalue, tb)
147 tb_list = traceback.format_exception(etype, evalue, tb)
145 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
148 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
146 else:
149 else:
147 status = u'ok'
150 status = u'ok'
148 finally:
151 finally:
149 self._restore_input()
152 self._restore_input()
150
153
151 reply_content[u'status'] = status
154 reply_content[u'status'] = status
152
155
153 # Return the execution counter so clients can display prompts
156 # Return the execution counter so clients can display prompts
154 reply_content['execution_count'] = shell.execution_count - 1
157 reply_content['execution_count'] = shell.execution_count - 1
155
158
156 # FIXME - fish exception info out of shell, possibly left there by
159 # FIXME - fish exception info out of shell, possibly left there by
157 # runlines. We'll need to clean up this logic later.
160 # runlines. We'll need to clean up this logic later.
158 if shell._reply_content is not None:
161 if shell._reply_content is not None:
159 reply_content.update(shell._reply_content)
162 reply_content.update(shell._reply_content)
160 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
163 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
161 reply_content['engine_info'] = e_info
164 reply_content['engine_info'] = e_info
162 # reset after use
165 # reset after use
163 shell._reply_content = None
166 shell._reply_content = None
164
167
165 if 'traceback' in reply_content:
168 if 'traceback' in reply_content:
166 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
169 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
167
170
168
171
169 # At this point, we can tell whether the main code execution succeeded
172 # At this point, we can tell whether the main code execution succeeded
170 # or not. If it did, we proceed to evaluate user_expressions
173 # or not. If it did, we proceed to evaluate user_expressions
171 if reply_content['status'] == 'ok':
174 if reply_content['status'] == 'ok':
172 reply_content[u'user_expressions'] = \
175 reply_content[u'user_expressions'] = \
173 shell.user_expressions(user_expressions or {})
176 shell.user_expressions(user_expressions or {})
174 else:
177 else:
175 # If there was an error, don't even try to compute expressions
178 # If there was an error, don't even try to compute expressions
176 reply_content[u'user_expressions'] = {}
179 reply_content[u'user_expressions'] = {}
177
180
178 # Payloads should be retrieved regardless of outcome, so we can both
181 # Payloads should be retrieved regardless of outcome, so we can both
179 # recover partial output (that could have been generated early in a
182 # recover partial output (that could have been generated early in a
180 # block, before an error) and clear the payload system always.
183 # block, before an error) and clear the payload system always.
181 reply_content[u'payload'] = shell.payload_manager.read_payload()
184 reply_content[u'payload'] = shell.payload_manager.read_payload()
182 # Be agressive about clearing the payload because we don't want
185 # Be agressive about clearing the payload because we don't want
183 # it to sit in memory until the next execute_request comes in.
186 # it to sit in memory until the next execute_request comes in.
184 shell.payload_manager.clear_payload()
187 shell.payload_manager.clear_payload()
185
188
186 return reply_content
189 return reply_content
187
190
188 def do_complete(self, code, cursor_pos):
191 def do_complete(self, code, cursor_pos):
189 # FIXME: IPython completers currently assume single line,
192 # FIXME: IPython completers currently assume single line,
190 # but completion messages give multi-line context
193 # but completion messages give multi-line context
191 # For now, extract line from cell, based on cursor_pos:
194 # For now, extract line from cell, based on cursor_pos:
192 if cursor_pos is None:
195 if cursor_pos is None:
193 cursor_pos = len(code)
196 cursor_pos = len(code)
194 line, offset = line_at_cursor(code, cursor_pos)
197 line, offset = line_at_cursor(code, cursor_pos)
195 line_cursor = cursor_pos - offset
198 line_cursor = cursor_pos - offset
196
199
197 txt, matches = self.shell.complete('', line, line_cursor)
200 txt, matches = self.shell.complete('', line, line_cursor)
198 return {'matches' : matches,
201 return {'matches' : matches,
199 'cursor_end' : cursor_pos,
202 'cursor_end' : cursor_pos,
200 'cursor_start' : cursor_pos - len(txt),
203 'cursor_start' : cursor_pos - len(txt),
201 'metadata' : {},
204 'metadata' : {},
202 'status' : 'ok'}
205 'status' : 'ok'}
203
206
204 def do_inspect(self, code, cursor_pos, detail_level=0):
207 def do_inspect(self, code, cursor_pos, detail_level=0):
205 name = token_at_cursor(code, cursor_pos)
208 name = token_at_cursor(code, cursor_pos)
206 info = self.shell.object_inspect(name)
209 info = self.shell.object_inspect(name)
207
210
208 reply_content = {'status' : 'ok'}
211 reply_content = {'status' : 'ok'}
209 reply_content['data'] = data = {}
212 reply_content['data'] = data = {}
210 reply_content['metadata'] = {}
213 reply_content['metadata'] = {}
211 reply_content['found'] = info['found']
214 reply_content['found'] = info['found']
212 if info['found']:
215 if info['found']:
213 info_text = self.shell.object_inspect_text(
216 info_text = self.shell.object_inspect_text(
214 name,
217 name,
215 detail_level=detail_level,
218 detail_level=detail_level,
216 )
219 )
217 data['text/plain'] = info_text
220 data['text/plain'] = info_text
218
221
219 return reply_content
222 return reply_content
220
223
221 def do_history(self, hist_access_type, output, raw, session=None, start=None,
224 def do_history(self, hist_access_type, output, raw, session=None, start=None,
222 stop=None, n=None, pattern=None, unique=False):
225 stop=None, n=None, pattern=None, unique=False):
223 if hist_access_type == 'tail':
226 if hist_access_type == 'tail':
224 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
227 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
225 include_latest=True)
228 include_latest=True)
226
229
227 elif hist_access_type == 'range':
230 elif hist_access_type == 'range':
228 hist = self.shell.history_manager.get_range(session, start, stop,
231 hist = self.shell.history_manager.get_range(session, start, stop,
229 raw=raw, output=output)
232 raw=raw, output=output)
230
233
231 elif hist_access_type == 'search':
234 elif hist_access_type == 'search':
232 hist = self.shell.history_manager.search(
235 hist = self.shell.history_manager.search(
233 pattern, raw=raw, output=output, n=n, unique=unique)
236 pattern, raw=raw, output=output, n=n, unique=unique)
234 else:
237 else:
235 hist = []
238 hist = []
236
239
237 return {'history' : list(hist)}
240 return {'history' : list(hist)}
238
241
239 def do_shutdown(self, restart):
242 def do_shutdown(self, restart):
240 self.shell.exit_now = True
243 self.shell.exit_now = True
241 return dict(status='ok', restart=restart)
244 return dict(status='ok', restart=restart)
242
245
243 def do_is_complete(self, code):
246 def do_is_complete(self, code):
244 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
247 status, indent_spaces = self.shell.input_transformer_manager.check_complete(code)
245 r = {'status': status}
248 r = {'status': status}
246 if status == 'incomplete':
249 if status == 'incomplete':
247 r['indent'] = ' ' * indent_spaces
250 r['indent'] = ' ' * indent_spaces
248 return r
251 return r
249
252
250 def do_apply(self, content, bufs, msg_id, reply_metadata):
253 def do_apply(self, content, bufs, msg_id, reply_metadata):
251 shell = self.shell
254 shell = self.shell
252 try:
255 try:
253 working = shell.user_ns
256 working = shell.user_ns
254
257
255 prefix = "_"+str(msg_id).replace("-","")+"_"
258 prefix = "_"+str(msg_id).replace("-","")+"_"
256
259
257 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
260 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
258
261
259 fname = getattr(f, '__name__', 'f')
262 fname = getattr(f, '__name__', 'f')
260
263
261 fname = prefix+"f"
264 fname = prefix+"f"
262 argname = prefix+"args"
265 argname = prefix+"args"
263 kwargname = prefix+"kwargs"
266 kwargname = prefix+"kwargs"
264 resultname = prefix+"result"
267 resultname = prefix+"result"
265
268
266 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
269 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
267 # print ns
270 # print ns
268 working.update(ns)
271 working.update(ns)
269 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
272 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
270 try:
273 try:
271 exec(code, shell.user_global_ns, shell.user_ns)
274 exec(code, shell.user_global_ns, shell.user_ns)
272 result = working.get(resultname)
275 result = working.get(resultname)
273 finally:
276 finally:
274 for key in ns:
277 for key in ns:
275 working.pop(key)
278 working.pop(key)
276
279
277 result_buf = serialize_object(result,
280 result_buf = serialize_object(result,
278 buffer_threshold=self.session.buffer_threshold,
281 buffer_threshold=self.session.buffer_threshold,
279 item_threshold=self.session.item_threshold,
282 item_threshold=self.session.item_threshold,
280 )
283 )
281
284
282 except:
285 except:
283 # invoke IPython traceback formatting
286 # invoke IPython traceback formatting
284 shell.showtraceback()
287 shell.showtraceback()
285 # FIXME - fish exception info out of shell, possibly left there by
288 # FIXME - fish exception info out of shell, possibly left there by
286 # run_code. We'll need to clean up this logic later.
289 # run_code. We'll need to clean up this logic later.
287 reply_content = {}
290 reply_content = {}
288 if shell._reply_content is not None:
291 if shell._reply_content is not None:
289 reply_content.update(shell._reply_content)
292 reply_content.update(shell._reply_content)
290 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
293 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
291 reply_content['engine_info'] = e_info
294 reply_content['engine_info'] = e_info
292 # reset after use
295 # reset after use
293 shell._reply_content = None
296 shell._reply_content = None
294
297
295 self.send_response(self.iopub_socket, u'error', reply_content,
298 self.send_response(self.iopub_socket, u'error', reply_content,
296 ident=self._topic('error'))
299 ident=self._topic('error'))
297 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
300 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
298 result_buf = []
301 result_buf = []
299
302
300 if reply_content['ename'] == 'UnmetDependency':
303 if reply_content['ename'] == 'UnmetDependency':
301 reply_metadata['dependencies_met'] = False
304 reply_metadata['dependencies_met'] = False
302 else:
305 else:
303 reply_content = {'status' : 'ok'}
306 reply_content = {'status' : 'ok'}
304
307
305 return reply_content, result_buf
308 return reply_content, result_buf
306
309
307 def do_clear(self):
310 def do_clear(self):
308 self.shell.reset(False)
311 self.shell.reset(False)
309 return dict(status='ok')
312 return dict(status='ok')
310
313
311
314
312 # This exists only for backwards compatibility - use IPythonKernel instead
315 # This exists only for backwards compatibility - use IPythonKernel instead
313
316
314 @undoc
317 @undoc
315 class Kernel(IPythonKernel):
318 class Kernel(IPythonKernel):
316 def __init__(self, *args, **kwargs):
319 def __init__(self, *args, **kwargs):
317 import warnings
320 import warnings
318 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
321 warnings.warn('Kernel is a deprecated alias of IPython.kernel.zmq.ipkernel.IPythonKernel',
319 DeprecationWarning)
322 DeprecationWarning)
320 super(Kernel, self).__init__(*args, **kwargs) No newline at end of file
323 super(Kernel, self).__init__(*args, **kwargs)
General Comments 0
You need to be logged in to leave comments. Login now