##// END OF EJS Templates
IPKernelApp now based on InteractiveShellApp
MinRK -
Show More
@@ -1,746 +1,673
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25 # System library imports.
25 # System library imports.
26 import zmq
26 import zmq
27
27
28 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.config.application import boolean_flag
30 from IPython.config.application import boolean_flag
31 from IPython.core.newapplication import ProfileDir
31 from IPython.core.newapplication import ProfileDir
32 from IPython.core.shellapp import (
33 InteractiveShellApp, shell_flags, shell_aliases
34 )
32 from IPython.utils import io
35 from IPython.utils import io
33 from IPython.utils.jsonutil import json_clean
36 from IPython.utils.jsonutil import json_clean
34 from IPython.lib import pylabtools
37 from IPython.lib import pylabtools
35 from IPython.utils.traitlets import (
38 from IPython.utils.traitlets import (
36 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
39 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
37 )
40 )
41
38 from entry_point import base_launch_kernel
42 from entry_point import base_launch_kernel
39 from kernelapp import KernelApp, kernel_flags, kernel_aliases
43 from kernelapp import KernelApp, kernel_flags, kernel_aliases
40 from iostream import OutStream
44 from iostream import OutStream
41 from session import Session, Message
45 from session import Session, Message
42 from zmqshell import ZMQInteractiveShell
46 from zmqshell import ZMQInteractiveShell
43
47
44
48
45
49
46 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
47 # Main kernel class
51 # Main kernel class
48 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
49
53
50 class Kernel(Configurable):
54 class Kernel(Configurable):
51
55
52 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
53 # Kernel interface
57 # Kernel interface
54 #---------------------------------------------------------------------------
58 #---------------------------------------------------------------------------
55
59
56 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
60 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
57 session = Instance(Session)
61 session = Instance(Session)
58 shell_socket = Instance('zmq.Socket')
62 shell_socket = Instance('zmq.Socket')
59 iopub_socket = Instance('zmq.Socket')
63 iopub_socket = Instance('zmq.Socket')
60 stdin_socket = Instance('zmq.Socket')
64 stdin_socket = Instance('zmq.Socket')
61 log = Instance(logging.Logger)
65 log = Instance(logging.Logger)
62
66
63 # Private interface
67 # Private interface
64
68
65 # Time to sleep after flushing the stdout/err buffers in each execute
69 # Time to sleep after flushing the stdout/err buffers in each execute
66 # cycle. While this introduces a hard limit on the minimal latency of the
70 # cycle. While this introduces a hard limit on the minimal latency of the
67 # execute cycle, it helps prevent output synchronization problems for
71 # execute cycle, it helps prevent output synchronization problems for
68 # clients.
72 # clients.
69 # Units are in seconds. The minimum zmq latency on local host is probably
73 # Units are in seconds. The minimum zmq latency on local host is probably
70 # ~150 microseconds, set this to 500us for now. We may need to increase it
74 # ~150 microseconds, set this to 500us for now. We may need to increase it
71 # a little if it's not enough after more interactive testing.
75 # a little if it's not enough after more interactive testing.
72 _execute_sleep = Float(0.0005, config=True)
76 _execute_sleep = Float(0.0005, config=True)
73
77
74 # Frequency of the kernel's event loop.
78 # Frequency of the kernel's event loop.
75 # Units are in seconds, kernel subclasses for GUI toolkits may need to
79 # Units are in seconds, kernel subclasses for GUI toolkits may need to
76 # adapt to milliseconds.
80 # adapt to milliseconds.
77 _poll_interval = Float(0.05, config=True)
81 _poll_interval = Float(0.05, config=True)
78
82
79 # If the shutdown was requested over the network, we leave here the
83 # If the shutdown was requested over the network, we leave here the
80 # necessary reply message so it can be sent by our registered atexit
84 # necessary reply message so it can be sent by our registered atexit
81 # handler. This ensures that the reply is only sent to clients truly at
85 # handler. This ensures that the reply is only sent to clients truly at
82 # the end of our shutdown process (which happens after the underlying
86 # the end of our shutdown process (which happens after the underlying
83 # IPython shell's own shutdown).
87 # IPython shell's own shutdown).
84 _shutdown_message = None
88 _shutdown_message = None
85
89
86 # This is a dict of port number that the kernel is listening on. It is set
90 # This is a dict of port number that the kernel is listening on. It is set
87 # by record_ports and used by connect_request.
91 # by record_ports and used by connect_request.
88 _recorded_ports = Dict()
92 _recorded_ports = Dict()
89
93
90
94
91
95
92 def __init__(self, **kwargs):
96 def __init__(self, **kwargs):
93 super(Kernel, self).__init__(**kwargs)
97 super(Kernel, self).__init__(**kwargs)
94
98
95 # Before we even start up the shell, register *first* our exit handlers
99 # Before we even start up the shell, register *first* our exit handlers
96 # so they come before the shell's
100 # so they come before the shell's
97 atexit.register(self._at_shutdown)
101 atexit.register(self._at_shutdown)
98
102
99 # Initialize the InteractiveShell subclass
103 # Initialize the InteractiveShell subclass
100 self.shell = ZMQInteractiveShell.instance(config=self.config)
104 self.shell = ZMQInteractiveShell.instance(config=self.config)
101 self.shell.displayhook.session = self.session
105 self.shell.displayhook.session = self.session
102 self.shell.displayhook.pub_socket = self.iopub_socket
106 self.shell.displayhook.pub_socket = self.iopub_socket
103 self.shell.display_pub.session = self.session
107 self.shell.display_pub.session = self.session
104 self.shell.display_pub.pub_socket = self.iopub_socket
108 self.shell.display_pub.pub_socket = self.iopub_socket
105
109
106 # TMP - hack while developing
110 # TMP - hack while developing
107 self.shell._reply_content = None
111 self.shell._reply_content = None
108
112
109 # Build dict of handlers for message types
113 # Build dict of handlers for message types
110 msg_types = [ 'execute_request', 'complete_request',
114 msg_types = [ 'execute_request', 'complete_request',
111 'object_info_request', 'history_request',
115 'object_info_request', 'history_request',
112 'connect_request', 'shutdown_request']
116 'connect_request', 'shutdown_request']
113 self.handlers = {}
117 self.handlers = {}
114 for msg_type in msg_types:
118 for msg_type in msg_types:
115 self.handlers[msg_type] = getattr(self, msg_type)
119 self.handlers[msg_type] = getattr(self, msg_type)
116
120
117 def do_one_iteration(self):
121 def do_one_iteration(self):
118 """Do one iteration of the kernel's evaluation loop.
122 """Do one iteration of the kernel's evaluation loop.
119 """
123 """
120 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
124 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
121 if msg is None:
125 if msg is None:
122 return
126 return
123
127
124 # This assert will raise in versions of zeromq 2.0.7 and lesser.
128 # This assert will raise in versions of zeromq 2.0.7 and lesser.
125 # We now require 2.0.8 or above, so we can uncomment for safety.
129 # We now require 2.0.8 or above, so we can uncomment for safety.
126 # print(ident,msg, file=sys.__stdout__)
130 # print(ident,msg, file=sys.__stdout__)
127 assert ident is not None, "Missing message part."
131 assert ident is not None, "Missing message part."
128
132
129 # Print some info about this message and leave a '--->' marker, so it's
133 # Print some info about this message and leave a '--->' marker, so it's
130 # easier to trace visually the message chain when debugging. Each
134 # easier to trace visually the message chain when debugging. Each
131 # handler prints its message at the end.
135 # handler prints its message at the end.
132 self.log.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
136 self.log.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
133 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
137 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
134
138
135 # Find and call actual handler for message
139 # Find and call actual handler for message
136 handler = self.handlers.get(msg['msg_type'], None)
140 handler = self.handlers.get(msg['msg_type'], None)
137 if handler is None:
141 if handler is None:
138 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
142 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
139 else:
143 else:
140 handler(ident, msg)
144 handler(ident, msg)
141
145
142 # Check whether we should exit, in case the incoming message set the
146 # Check whether we should exit, in case the incoming message set the
143 # exit flag on
147 # exit flag on
144 if self.shell.exit_now:
148 if self.shell.exit_now:
145 self.log.debug('\nExiting IPython kernel...')
149 self.log.debug('\nExiting IPython kernel...')
146 # We do a normal, clean exit, which allows any actions registered
150 # We do a normal, clean exit, which allows any actions registered
147 # via atexit (such as history saving) to take place.
151 # via atexit (such as history saving) to take place.
148 sys.exit(0)
152 sys.exit(0)
149
153
150
154
151 def start(self):
155 def start(self):
152 """ Start the kernel main loop.
156 """ Start the kernel main loop.
153 """
157 """
154 poller = zmq.Poller()
158 poller = zmq.Poller()
155 poller.register(self.shell_socket, zmq.POLLIN)
159 poller.register(self.shell_socket, zmq.POLLIN)
156 while True:
160 while True:
157 try:
161 try:
158 # scale by extra factor of 10, because there is no
162 # scale by extra factor of 10, because there is no
159 # reason for this to be anything less than ~ 0.1s
163 # reason for this to be anything less than ~ 0.1s
160 # since it is a real poller and will respond
164 # since it is a real poller and will respond
161 # to events immediately
165 # to events immediately
162 poller.poll(10*1000*self._poll_interval)
166 poller.poll(10*1000*self._poll_interval)
163 self.do_one_iteration()
167 self.do_one_iteration()
164 except KeyboardInterrupt:
168 except KeyboardInterrupt:
165 # Ctrl-C shouldn't crash the kernel
169 # Ctrl-C shouldn't crash the kernel
166 io.raw_print("KeyboardInterrupt caught in kernel")
170 io.raw_print("KeyboardInterrupt caught in kernel")
167
171
168 def record_ports(self, ports):
172 def record_ports(self, ports):
169 """Record the ports that this kernel is using.
173 """Record the ports that this kernel is using.
170
174
171 The creator of the Kernel instance must call this methods if they
175 The creator of the Kernel instance must call this methods if they
172 want the :meth:`connect_request` method to return the port numbers.
176 want the :meth:`connect_request` method to return the port numbers.
173 """
177 """
174 self._recorded_ports = ports
178 self._recorded_ports = ports
175
179
176 #---------------------------------------------------------------------------
180 #---------------------------------------------------------------------------
177 # Kernel request handlers
181 # Kernel request handlers
178 #---------------------------------------------------------------------------
182 #---------------------------------------------------------------------------
179
183
180 def _publish_pyin(self, code, parent):
184 def _publish_pyin(self, code, parent):
181 """Publish the code request on the pyin stream."""
185 """Publish the code request on the pyin stream."""
182
186
183 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
187 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
184
188
185 def execute_request(self, ident, parent):
189 def execute_request(self, ident, parent):
186
190
187 status_msg = self.session.send(self.iopub_socket,
191 status_msg = self.session.send(self.iopub_socket,
188 u'status',
192 u'status',
189 {u'execution_state':u'busy'},
193 {u'execution_state':u'busy'},
190 parent=parent
194 parent=parent
191 )
195 )
192
196
193 try:
197 try:
194 content = parent[u'content']
198 content = parent[u'content']
195 code = content[u'code']
199 code = content[u'code']
196 silent = content[u'silent']
200 silent = content[u'silent']
197 except:
201 except:
198 self.log.error("Got bad msg: ")
202 self.log.error("Got bad msg: ")
199 self.log.error(str(Message(parent)))
203 self.log.error(str(Message(parent)))
200 return
204 return
201
205
202 shell = self.shell # we'll need this a lot here
206 shell = self.shell # we'll need this a lot here
203
207
204 # Replace raw_input. Note that is not sufficient to replace
208 # Replace raw_input. Note that is not sufficient to replace
205 # raw_input in the user namespace.
209 # raw_input in the user namespace.
206 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
210 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
207 __builtin__.raw_input = raw_input
211 __builtin__.raw_input = raw_input
208
212
209 # Set the parent message of the display hook and out streams.
213 # Set the parent message of the display hook and out streams.
210 shell.displayhook.set_parent(parent)
214 shell.displayhook.set_parent(parent)
211 shell.display_pub.set_parent(parent)
215 shell.display_pub.set_parent(parent)
212 sys.stdout.set_parent(parent)
216 sys.stdout.set_parent(parent)
213 sys.stderr.set_parent(parent)
217 sys.stderr.set_parent(parent)
214
218
215 # Re-broadcast our input for the benefit of listening clients, and
219 # Re-broadcast our input for the benefit of listening clients, and
216 # start computing output
220 # start computing output
217 if not silent:
221 if not silent:
218 self._publish_pyin(code, parent)
222 self._publish_pyin(code, parent)
219
223
220 reply_content = {}
224 reply_content = {}
221 try:
225 try:
222 if silent:
226 if silent:
223 # run_code uses 'exec' mode, so no displayhook will fire, and it
227 # run_code uses 'exec' mode, so no displayhook will fire, and it
224 # doesn't call logging or history manipulations. Print
228 # doesn't call logging or history manipulations. Print
225 # statements in that code will obviously still execute.
229 # statements in that code will obviously still execute.
226 shell.run_code(code)
230 shell.run_code(code)
227 else:
231 else:
228 # FIXME: the shell calls the exception handler itself.
232 # FIXME: the shell calls the exception handler itself.
229 shell.run_cell(code)
233 shell.run_cell(code)
230 except:
234 except:
231 status = u'error'
235 status = u'error'
232 # FIXME: this code right now isn't being used yet by default,
236 # FIXME: this code right now isn't being used yet by default,
233 # because the run_cell() call above directly fires off exception
237 # because the run_cell() call above directly fires off exception
234 # reporting. This code, therefore, is only active in the scenario
238 # reporting. This code, therefore, is only active in the scenario
235 # where runlines itself has an unhandled exception. We need to
239 # where runlines itself has an unhandled exception. We need to
236 # uniformize this, for all exception construction to come from a
240 # uniformize this, for all exception construction to come from a
237 # single location in the codbase.
241 # single location in the codbase.
238 etype, evalue, tb = sys.exc_info()
242 etype, evalue, tb = sys.exc_info()
239 tb_list = traceback.format_exception(etype, evalue, tb)
243 tb_list = traceback.format_exception(etype, evalue, tb)
240 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
244 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
241 else:
245 else:
242 status = u'ok'
246 status = u'ok'
243
247
244 reply_content[u'status'] = status
248 reply_content[u'status'] = status
245
249
246 # Return the execution counter so clients can display prompts
250 # Return the execution counter so clients can display prompts
247 reply_content['execution_count'] = shell.execution_count -1
251 reply_content['execution_count'] = shell.execution_count -1
248
252
249 # FIXME - fish exception info out of shell, possibly left there by
253 # FIXME - fish exception info out of shell, possibly left there by
250 # runlines. We'll need to clean up this logic later.
254 # runlines. We'll need to clean up this logic later.
251 if shell._reply_content is not None:
255 if shell._reply_content is not None:
252 reply_content.update(shell._reply_content)
256 reply_content.update(shell._reply_content)
253 # reset after use
257 # reset after use
254 shell._reply_content = None
258 shell._reply_content = None
255
259
256 # At this point, we can tell whether the main code execution succeeded
260 # At this point, we can tell whether the main code execution succeeded
257 # or not. If it did, we proceed to evaluate user_variables/expressions
261 # or not. If it did, we proceed to evaluate user_variables/expressions
258 if reply_content['status'] == 'ok':
262 if reply_content['status'] == 'ok':
259 reply_content[u'user_variables'] = \
263 reply_content[u'user_variables'] = \
260 shell.user_variables(content[u'user_variables'])
264 shell.user_variables(content[u'user_variables'])
261 reply_content[u'user_expressions'] = \
265 reply_content[u'user_expressions'] = \
262 shell.user_expressions(content[u'user_expressions'])
266 shell.user_expressions(content[u'user_expressions'])
263 else:
267 else:
264 # If there was an error, don't even try to compute variables or
268 # If there was an error, don't even try to compute variables or
265 # expressions
269 # expressions
266 reply_content[u'user_variables'] = {}
270 reply_content[u'user_variables'] = {}
267 reply_content[u'user_expressions'] = {}
271 reply_content[u'user_expressions'] = {}
268
272
269 # Payloads should be retrieved regardless of outcome, so we can both
273 # Payloads should be retrieved regardless of outcome, so we can both
270 # recover partial output (that could have been generated early in a
274 # recover partial output (that could have been generated early in a
271 # block, before an error) and clear the payload system always.
275 # block, before an error) and clear the payload system always.
272 reply_content[u'payload'] = shell.payload_manager.read_payload()
276 reply_content[u'payload'] = shell.payload_manager.read_payload()
273 # Be agressive about clearing the payload because we don't want
277 # Be agressive about clearing the payload because we don't want
274 # it to sit in memory until the next execute_request comes in.
278 # it to sit in memory until the next execute_request comes in.
275 shell.payload_manager.clear_payload()
279 shell.payload_manager.clear_payload()
276
280
277 # Flush output before sending the reply.
281 # Flush output before sending the reply.
278 sys.stdout.flush()
282 sys.stdout.flush()
279 sys.stderr.flush()
283 sys.stderr.flush()
280 # FIXME: on rare occasions, the flush doesn't seem to make it to the
284 # FIXME: on rare occasions, the flush doesn't seem to make it to the
281 # clients... This seems to mitigate the problem, but we definitely need
285 # clients... This seems to mitigate the problem, but we definitely need
282 # to better understand what's going on.
286 # to better understand what's going on.
283 if self._execute_sleep:
287 if self._execute_sleep:
284 time.sleep(self._execute_sleep)
288 time.sleep(self._execute_sleep)
285
289
286 # Send the reply.
290 # Send the reply.
287 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
291 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
288 reply_content, parent, ident=ident)
292 reply_content, parent, ident=ident)
289 self.log.debug(str(reply_msg))
293 self.log.debug(str(reply_msg))
290
294
291 if reply_msg['content']['status'] == u'error':
295 if reply_msg['content']['status'] == u'error':
292 self._abort_queue()
296 self._abort_queue()
293
297
294 status_msg = self.session.send(self.iopub_socket,
298 status_msg = self.session.send(self.iopub_socket,
295 u'status',
299 u'status',
296 {u'execution_state':u'idle'},
300 {u'execution_state':u'idle'},
297 parent=parent
301 parent=parent
298 )
302 )
299
303
300 def complete_request(self, ident, parent):
304 def complete_request(self, ident, parent):
301 txt, matches = self._complete(parent)
305 txt, matches = self._complete(parent)
302 matches = {'matches' : matches,
306 matches = {'matches' : matches,
303 'matched_text' : txt,
307 'matched_text' : txt,
304 'status' : 'ok'}
308 'status' : 'ok'}
305 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
309 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
306 matches, parent, ident)
310 matches, parent, ident)
307 self.log.debug(str(completion_msg))
311 self.log.debug(str(completion_msg))
308
312
309 def object_info_request(self, ident, parent):
313 def object_info_request(self, ident, parent):
310 object_info = self.shell.object_inspect(parent['content']['oname'])
314 object_info = self.shell.object_inspect(parent['content']['oname'])
311 # Before we send this object over, we scrub it for JSON usage
315 # Before we send this object over, we scrub it for JSON usage
312 oinfo = json_clean(object_info)
316 oinfo = json_clean(object_info)
313 msg = self.session.send(self.shell_socket, 'object_info_reply',
317 msg = self.session.send(self.shell_socket, 'object_info_reply',
314 oinfo, parent, ident)
318 oinfo, parent, ident)
315 self.log.debug(msg)
319 self.log.debug(msg)
316
320
317 def history_request(self, ident, parent):
321 def history_request(self, ident, parent):
318 # We need to pull these out, as passing **kwargs doesn't work with
322 # We need to pull these out, as passing **kwargs doesn't work with
319 # unicode keys before Python 2.6.5.
323 # unicode keys before Python 2.6.5.
320 hist_access_type = parent['content']['hist_access_type']
324 hist_access_type = parent['content']['hist_access_type']
321 raw = parent['content']['raw']
325 raw = parent['content']['raw']
322 output = parent['content']['output']
326 output = parent['content']['output']
323 if hist_access_type == 'tail':
327 if hist_access_type == 'tail':
324 n = parent['content']['n']
328 n = parent['content']['n']
325 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
329 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
326 include_latest=True)
330 include_latest=True)
327
331
328 elif hist_access_type == 'range':
332 elif hist_access_type == 'range':
329 session = parent['content']['session']
333 session = parent['content']['session']
330 start = parent['content']['start']
334 start = parent['content']['start']
331 stop = parent['content']['stop']
335 stop = parent['content']['stop']
332 hist = self.shell.history_manager.get_range(session, start, stop,
336 hist = self.shell.history_manager.get_range(session, start, stop,
333 raw=raw, output=output)
337 raw=raw, output=output)
334
338
335 elif hist_access_type == 'search':
339 elif hist_access_type == 'search':
336 pattern = parent['content']['pattern']
340 pattern = parent['content']['pattern']
337 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
341 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
338
342
339 else:
343 else:
340 hist = []
344 hist = []
341 content = {'history' : list(hist)}
345 content = {'history' : list(hist)}
342 msg = self.session.send(self.shell_socket, 'history_reply',
346 msg = self.session.send(self.shell_socket, 'history_reply',
343 content, parent, ident)
347 content, parent, ident)
344 self.log.debug(str(msg))
348 self.log.debug(str(msg))
345
349
346 def connect_request(self, ident, parent):
350 def connect_request(self, ident, parent):
347 if self._recorded_ports is not None:
351 if self._recorded_ports is not None:
348 content = self._recorded_ports.copy()
352 content = self._recorded_ports.copy()
349 else:
353 else:
350 content = {}
354 content = {}
351 msg = self.session.send(self.shell_socket, 'connect_reply',
355 msg = self.session.send(self.shell_socket, 'connect_reply',
352 content, parent, ident)
356 content, parent, ident)
353 self.log.debug(msg)
357 self.log.debug(msg)
354
358
355 def shutdown_request(self, ident, parent):
359 def shutdown_request(self, ident, parent):
356 self.shell.exit_now = True
360 self.shell.exit_now = True
357 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
361 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
358 sys.exit(0)
362 sys.exit(0)
359
363
360 #---------------------------------------------------------------------------
364 #---------------------------------------------------------------------------
361 # Protected interface
365 # Protected interface
362 #---------------------------------------------------------------------------
366 #---------------------------------------------------------------------------
363
367
364 def _abort_queue(self):
368 def _abort_queue(self):
365 while True:
369 while True:
366 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
370 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
367 if msg is None:
371 if msg is None:
368 break
372 break
369 else:
373 else:
370 assert ident is not None, \
374 assert ident is not None, \
371 "Unexpected missing message part."
375 "Unexpected missing message part."
372
376
373 self.log.debug("Aborting:\n"+str(Message(msg)))
377 self.log.debug("Aborting:\n"+str(Message(msg)))
374 msg_type = msg['msg_type']
378 msg_type = msg['msg_type']
375 reply_type = msg_type.split('_')[0] + '_reply'
379 reply_type = msg_type.split('_')[0] + '_reply'
376 reply_msg = self.session.send(self.shell_socket, reply_type,
380 reply_msg = self.session.send(self.shell_socket, reply_type,
377 {'status' : 'aborted'}, msg, ident=ident)
381 {'status' : 'aborted'}, msg, ident=ident)
378 self.log.debug(reply_msg)
382 self.log.debug(reply_msg)
379 # We need to wait a bit for requests to come in. This can probably
383 # We need to wait a bit for requests to come in. This can probably
380 # be set shorter for true asynchronous clients.
384 # be set shorter for true asynchronous clients.
381 time.sleep(0.1)
385 time.sleep(0.1)
382
386
383 def _raw_input(self, prompt, ident, parent):
387 def _raw_input(self, prompt, ident, parent):
384 # Flush output before making the request.
388 # Flush output before making the request.
385 sys.stderr.flush()
389 sys.stderr.flush()
386 sys.stdout.flush()
390 sys.stdout.flush()
387
391
388 # Send the input request.
392 # Send the input request.
389 content = dict(prompt=prompt)
393 content = dict(prompt=prompt)
390 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
394 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
391
395
392 # Await a response.
396 # Await a response.
393 ident, reply = self.session.recv(self.stdin_socket, 0)
397 ident, reply = self.session.recv(self.stdin_socket, 0)
394 try:
398 try:
395 value = reply['content']['value']
399 value = reply['content']['value']
396 except:
400 except:
397 self.log.error("Got bad raw_input reply: ")
401 self.log.error("Got bad raw_input reply: ")
398 self.log.error(str(Message(parent)))
402 self.log.error(str(Message(parent)))
399 value = ''
403 value = ''
400 return value
404 return value
401
405
402 def _complete(self, msg):
406 def _complete(self, msg):
403 c = msg['content']
407 c = msg['content']
404 try:
408 try:
405 cpos = int(c['cursor_pos'])
409 cpos = int(c['cursor_pos'])
406 except:
410 except:
407 # If we don't get something that we can convert to an integer, at
411 # If we don't get something that we can convert to an integer, at
408 # least attempt the completion guessing the cursor is at the end of
412 # least attempt the completion guessing the cursor is at the end of
409 # the text, if there's any, and otherwise of the line
413 # the text, if there's any, and otherwise of the line
410 cpos = len(c['text'])
414 cpos = len(c['text'])
411 if cpos==0:
415 if cpos==0:
412 cpos = len(c['line'])
416 cpos = len(c['line'])
413 return self.shell.complete(c['text'], c['line'], cpos)
417 return self.shell.complete(c['text'], c['line'], cpos)
414
418
415 def _object_info(self, context):
419 def _object_info(self, context):
416 symbol, leftover = self._symbol_from_context(context)
420 symbol, leftover = self._symbol_from_context(context)
417 if symbol is not None and not leftover:
421 if symbol is not None and not leftover:
418 doc = getattr(symbol, '__doc__', '')
422 doc = getattr(symbol, '__doc__', '')
419 else:
423 else:
420 doc = ''
424 doc = ''
421 object_info = dict(docstring = doc)
425 object_info = dict(docstring = doc)
422 return object_info
426 return object_info
423
427
424 def _symbol_from_context(self, context):
428 def _symbol_from_context(self, context):
425 if not context:
429 if not context:
426 return None, context
430 return None, context
427
431
428 base_symbol_string = context[0]
432 base_symbol_string = context[0]
429 symbol = self.shell.user_ns.get(base_symbol_string, None)
433 symbol = self.shell.user_ns.get(base_symbol_string, None)
430 if symbol is None:
434 if symbol is None:
431 symbol = __builtin__.__dict__.get(base_symbol_string, None)
435 symbol = __builtin__.__dict__.get(base_symbol_string, None)
432 if symbol is None:
436 if symbol is None:
433 return None, context
437 return None, context
434
438
435 context = context[1:]
439 context = context[1:]
436 for i, name in enumerate(context):
440 for i, name in enumerate(context):
437 new_symbol = getattr(symbol, name, None)
441 new_symbol = getattr(symbol, name, None)
438 if new_symbol is None:
442 if new_symbol is None:
439 return symbol, context[i:]
443 return symbol, context[i:]
440 else:
444 else:
441 symbol = new_symbol
445 symbol = new_symbol
442
446
443 return symbol, []
447 return symbol, []
444
448
445 def _at_shutdown(self):
449 def _at_shutdown(self):
446 """Actions taken at shutdown by the kernel, called by python's atexit.
450 """Actions taken at shutdown by the kernel, called by python's atexit.
447 """
451 """
448 # io.rprint("Kernel at_shutdown") # dbg
452 # io.rprint("Kernel at_shutdown") # dbg
449 if self._shutdown_message is not None:
453 if self._shutdown_message is not None:
450 self.session.send(self.shell_socket, self._shutdown_message)
454 self.session.send(self.shell_socket, self._shutdown_message)
451 self.session.send(self.iopub_socket, self._shutdown_message)
455 self.session.send(self.iopub_socket, self._shutdown_message)
452 self.log.debug(str(self._shutdown_message))
456 self.log.debug(str(self._shutdown_message))
453 # A very short sleep to give zmq time to flush its message buffers
457 # A very short sleep to give zmq time to flush its message buffers
454 # before Python truly shuts down.
458 # before Python truly shuts down.
455 time.sleep(0.01)
459 time.sleep(0.01)
456
460
457
461
458 class QtKernel(Kernel):
462 class QtKernel(Kernel):
459 """A Kernel subclass with Qt support."""
463 """A Kernel subclass with Qt support."""
460
464
461 def start(self):
465 def start(self):
462 """Start a kernel with QtPy4 event loop integration."""
466 """Start a kernel with QtPy4 event loop integration."""
463
467
464 from PyQt4 import QtCore
468 from PyQt4 import QtCore
465 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
469 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
466
470
467 self.app = get_app_qt4([" "])
471 self.app = get_app_qt4([" "])
468 self.app.setQuitOnLastWindowClosed(False)
472 self.app.setQuitOnLastWindowClosed(False)
469 self.timer = QtCore.QTimer()
473 self.timer = QtCore.QTimer()
470 self.timer.timeout.connect(self.do_one_iteration)
474 self.timer.timeout.connect(self.do_one_iteration)
471 # Units for the timer are in milliseconds
475 # Units for the timer are in milliseconds
472 self.timer.start(1000*self._poll_interval)
476 self.timer.start(1000*self._poll_interval)
473 start_event_loop_qt4(self.app)
477 start_event_loop_qt4(self.app)
474
478
475
479
476 class WxKernel(Kernel):
480 class WxKernel(Kernel):
477 """A Kernel subclass with Wx support."""
481 """A Kernel subclass with Wx support."""
478
482
479 def start(self):
483 def start(self):
480 """Start a kernel with wx event loop support."""
484 """Start a kernel with wx event loop support."""
481
485
482 import wx
486 import wx
483 from IPython.lib.guisupport import start_event_loop_wx
487 from IPython.lib.guisupport import start_event_loop_wx
484
488
485 doi = self.do_one_iteration
489 doi = self.do_one_iteration
486 # Wx uses milliseconds
490 # Wx uses milliseconds
487 poll_interval = int(1000*self._poll_interval)
491 poll_interval = int(1000*self._poll_interval)
488
492
489 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
493 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
490 # We make the Frame hidden when we create it in the main app below.
494 # We make the Frame hidden when we create it in the main app below.
491 class TimerFrame(wx.Frame):
495 class TimerFrame(wx.Frame):
492 def __init__(self, func):
496 def __init__(self, func):
493 wx.Frame.__init__(self, None, -1)
497 wx.Frame.__init__(self, None, -1)
494 self.timer = wx.Timer(self)
498 self.timer = wx.Timer(self)
495 # Units for the timer are in milliseconds
499 # Units for the timer are in milliseconds
496 self.timer.Start(poll_interval)
500 self.timer.Start(poll_interval)
497 self.Bind(wx.EVT_TIMER, self.on_timer)
501 self.Bind(wx.EVT_TIMER, self.on_timer)
498 self.func = func
502 self.func = func
499
503
500 def on_timer(self, event):
504 def on_timer(self, event):
501 self.func()
505 self.func()
502
506
503 # We need a custom wx.App to create our Frame subclass that has the
507 # We need a custom wx.App to create our Frame subclass that has the
504 # wx.Timer to drive the ZMQ event loop.
508 # wx.Timer to drive the ZMQ event loop.
505 class IPWxApp(wx.App):
509 class IPWxApp(wx.App):
506 def OnInit(self):
510 def OnInit(self):
507 self.frame = TimerFrame(doi)
511 self.frame = TimerFrame(doi)
508 self.frame.Show(False)
512 self.frame.Show(False)
509 return True
513 return True
510
514
511 # The redirect=False here makes sure that wx doesn't replace
515 # The redirect=False here makes sure that wx doesn't replace
512 # sys.stdout/stderr with its own classes.
516 # sys.stdout/stderr with its own classes.
513 self.app = IPWxApp(redirect=False)
517 self.app = IPWxApp(redirect=False)
514 start_event_loop_wx(self.app)
518 start_event_loop_wx(self.app)
515
519
516
520
517 class TkKernel(Kernel):
521 class TkKernel(Kernel):
518 """A Kernel subclass with Tk support."""
522 """A Kernel subclass with Tk support."""
519
523
520 def start(self):
524 def start(self):
521 """Start a Tk enabled event loop."""
525 """Start a Tk enabled event loop."""
522
526
523 import Tkinter
527 import Tkinter
524 doi = self.do_one_iteration
528 doi = self.do_one_iteration
525 # Tk uses milliseconds
529 # Tk uses milliseconds
526 poll_interval = int(1000*self._poll_interval)
530 poll_interval = int(1000*self._poll_interval)
527 # For Tkinter, we create a Tk object and call its withdraw method.
531 # For Tkinter, we create a Tk object and call its withdraw method.
528 class Timer(object):
532 class Timer(object):
529 def __init__(self, func):
533 def __init__(self, func):
530 self.app = Tkinter.Tk()
534 self.app = Tkinter.Tk()
531 self.app.withdraw()
535 self.app.withdraw()
532 self.func = func
536 self.func = func
533
537
534 def on_timer(self):
538 def on_timer(self):
535 self.func()
539 self.func()
536 self.app.after(poll_interval, self.on_timer)
540 self.app.after(poll_interval, self.on_timer)
537
541
538 def start(self):
542 def start(self):
539 self.on_timer() # Call it once to get things going.
543 self.on_timer() # Call it once to get things going.
540 self.app.mainloop()
544 self.app.mainloop()
541
545
542 self.timer = Timer(doi)
546 self.timer = Timer(doi)
543 self.timer.start()
547 self.timer.start()
544
548
545
549
546 class GTKKernel(Kernel):
550 class GTKKernel(Kernel):
547 """A Kernel subclass with GTK support."""
551 """A Kernel subclass with GTK support."""
548
552
549 def start(self):
553 def start(self):
550 """Start the kernel, coordinating with the GTK event loop"""
554 """Start the kernel, coordinating with the GTK event loop"""
551 from .gui.gtkembed import GTKEmbed
555 from .gui.gtkembed import GTKEmbed
552
556
553 gtk_kernel = GTKEmbed(self)
557 gtk_kernel = GTKEmbed(self)
554 gtk_kernel.start()
558 gtk_kernel.start()
555
559
556
560
557 #-----------------------------------------------------------------------------
561 #-----------------------------------------------------------------------------
558 # Aliases and Flags for the IPKernelApp
562 # Aliases and Flags for the IPKernelApp
559 #-----------------------------------------------------------------------------
563 #-----------------------------------------------------------------------------
560
564
561 flags = dict(kernel_flags)
565 flags = dict(kernel_flags)
566 flags.update(shell_flags)
562
567
563 addflag = lambda *args: flags.update(boolean_flag(*args))
568 addflag = lambda *args: flags.update(boolean_flag(*args))
564 addflag('automagic', 'InteractiveShell.automagic',
565 """Turn on the auto calling of magic commands. Type %%magic at the
566 IPython prompt for more information.""",
567 'Turn off the auto calling of magic commands.'
568 )
569 addflag('banner', 'InteractiveShell.display_banner',
570 "Display a banner upon starting IPython.",
571 "Don't display a banner upon starting IPython."
572 )
573 addflag('pdb', 'InteractiveShell.pdb',
574 "Enable auto calling the pdb debugger after every exception.",
575 "Disable auto calling the pdb debugger after every exception."
576 )
577 addflag('pprint', 'PlainTextFormatter.pprint',
578 "Enable auto pretty printing of results.",
579 "Disable auto auto pretty printing of results."
580 )
581 addflag('color-info', 'InteractiveShell.color_info',
582 """IPython can display information about objects via a set of func-
583 tions, and optionally can use colors for this, syntax highlighting
584 source code and various other elements. However, because this
585 information is passed through a pager (like 'less') and many pagers get
586 confused with color codes, this option is off by default. You can test
587 it and turn it on permanently in your ipython_config.py file if it
588 works for you. Test it and turn it on permanently if it works with
589 your system. The magic function %%color_info allows you to toggle this
590 inter- actively for testing.""",
591 "Disable using colors for info related things."
592 )
593 addflag('deep-reload', 'InteractiveShell.deep_reload',
594 """Enable deep (recursive) reloading by default. IPython can use the
595 deep_reload module which reloads changes in modules recursively (it
596 replaces the reload() function, so you don't need to change anything to
597 use it). deep_reload() forces a full reload of modules whose code may
598 have changed, which the default reload() function does not. When
599 deep_reload is off, IPython will use the normal reload(), but
600 deep_reload will still be available as dreload(). This fea- ture is off
601 by default [which means that you have both normal reload() and
602 dreload()].""",
603 "Disable deep (recursive) reloading by default."
604 )
605 addflag('readline', 'InteractiveShell.readline_use',
606 "Enable readline for command line usage.",
607 "Disable readline for command line usage."
608 )
609
569
610 flags['pylab'] = (
570 flags['pylab'] = (
611 {'IPKernelApp' : {'pylab' : 'auto'}},
571 {'IPKernelApp' : {'pylab' : 'auto'}},
612 """Pre-load matplotlib and numpy for interactive use with
572 """Pre-load matplotlib and numpy for interactive use with
613 the default matplotlib backend."""
573 the default matplotlib backend."""
614 )
574 )
615
575
616 aliases = dict(kernel_aliases)
576 aliases = dict(kernel_aliases)
577 aliases.update(shell_aliases)
617
578
618 # it's possible we don't want short aliases for *all* of these:
579 # it's possible we don't want short aliases for *all* of these:
619 aliases.update(dict(
580 aliases.update(dict(
620 autocall='InteractiveShell.autocall',
621 cache_size='InteractiveShell.cache_size',
622 colors='InteractiveShell.colors',
623 logfile='InteractiveShell.logfile',
624 log_append='InteractiveShell.logappend',
625 pi1='InteractiveShell.prompt_in1',
626 pi2='InteractiveShell.prompt_in2',
627 po='InteractiveShell.prompt_out',
628 si='InteractiveShell.separate_in',
629 so='InteractiveShell.separate_out',
630 so2='InteractiveShell.separate_out2',
631 xmode='InteractiveShell.xmode',
632 c='IPKernelApp.code_to_run',
633 ext='IPKernelApp.extra_extension',
634 pylab='IPKernelApp.pylab',
581 pylab='IPKernelApp.pylab',
635 ))
582 ))
636
583
637 #-----------------------------------------------------------------------------
584 #-----------------------------------------------------------------------------
638 # The IPKernelApp class
585 # The IPKernelApp class
639 #-----------------------------------------------------------------------------
586 #-----------------------------------------------------------------------------
640
587
641 class IPKernelApp(KernelApp):
588 class IPKernelApp(KernelApp, InteractiveShellApp):
642 name = 'ipkernel'
589 name = 'ipkernel'
643
590
644 aliases = Dict(aliases)
591 aliases = Dict(aliases)
645 flags = Dict(flags)
592 flags = Dict(flags)
646 classes = [Kernel, ZMQInteractiveShell, ProfileDir]
593 classes = [Kernel, ZMQInteractiveShell, ProfileDir]
647 # configurables
594 # configurables
648 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
595 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
649 config=True,
596 config=True,
650 help="""Pre-load matplotlib and numpy for interactive use,
597 help="""Pre-load matplotlib and numpy for interactive use,
651 selecting a particular matplotlib backend and loop integration.
598 selecting a particular matplotlib backend and loop integration.
652 """
599 """
653 )
600 )
654 extensions = List(Unicode, config=True,
601 def initialize(self, argv=None):
655 help="A list of dotted module names of IPython extensions to load."
602 super(IPKernelApp, self).initialize(argv)
656 )
603 self.init_shell()
657 extra_extension = Unicode('', config=True,
604 self.init_extensions()
658 help="dotted module name of an IPython extension to load."
605 self.init_code()
659 )
660 def _extra_extension_changed(self, name, old, new):
661 if new:
662 # add to self.extensions
663 self.extensions.append(new)
664
665 exec_files = List(Unicode, config=True,
666 help="""List of files to run at IPython startup."""
667 )
668 file_to_run = Unicode('', config=True,
669 help="""A file to be run""")
670 def _file_to_run_changed(self, name, old, new):
671 self.exec_files.append(new)
672
673 exec_lines = List(Unicode, config=True,
674 help="""lines of code to run at IPython startup."""
675 )
676 code_to_run = Unicode('', config=True,
677 help="Execute the given command string."
678 )
679 def _code_to_run_changed(self, name, old, new):
680 self.exec_lines.append(new)
681
606
682 def init_kernel(self):
607 def init_kernel(self):
683 kernel_factory = Kernel
608 kernel_factory = Kernel
684
609
685 kernel_map = {
610 kernel_map = {
686 'qt' : QtKernel,
611 'qt' : QtKernel,
687 'qt4': QtKernel,
612 'qt4': QtKernel,
688 'inline': Kernel,
613 'inline': Kernel,
689 'osx': TkKernel,
614 'osx': TkKernel,
690 'wx' : WxKernel,
615 'wx' : WxKernel,
691 'tk' : TkKernel,
616 'tk' : TkKernel,
692 'gtk': GTKKernel,
617 'gtk': GTKKernel,
693 }
618 }
694
619
695 if self.pylab:
620 if self.pylab:
696 key = None if self.pylab == 'auto' else self.pylab
621 key = None if self.pylab == 'auto' else self.pylab
697 gui, backend = pylabtools.find_gui_and_backend(key)
622 gui, backend = pylabtools.find_gui_and_backend(key)
698 kernel_factory = kernel_map.get(gui)
623 kernel_factory = kernel_map.get(gui)
699 if kernel_factory is None:
624 if kernel_factory is None:
700 raise ValueError('GUI is not supported: %r' % gui)
625 raise ValueError('GUI is not supported: %r' % gui)
701 pylabtools.activate_matplotlib(backend)
626 pylabtools.activate_matplotlib(backend)
702
627
703 kernel = kernel_factory(config=self.config, session=self.session,
628 kernel = kernel_factory(config=self.config, session=self.session,
704 shell_socket=self.shell_socket,
629 shell_socket=self.shell_socket,
705 iopub_socket=self.iopub_socket,
630 iopub_socket=self.iopub_socket,
706 stdin_socket=self.stdin_socket,
631 stdin_socket=self.stdin_socket,
707 log=self.log
632 log=self.log
708 )
633 )
709 self.kernel = kernel
634 self.kernel = kernel
710 kernel.record_ports(self.ports)
635 kernel.record_ports(self.ports)
711
636
712 if self.pylab:
637 if self.pylab:
713 pylabtools.import_pylab(kernel.shell.user_ns, backend,
638 pylabtools.import_pylab(kernel.shell.user_ns, backend,
714 shell=kernel.shell)
639 shell=kernel.shell)
715
640
641 def init_shell(self):
642 self.shell = self.kernel.shell
716
643
717
644
718 #-----------------------------------------------------------------------------
645 #-----------------------------------------------------------------------------
719 # Kernel main and launch functions
646 # Kernel main and launch functions
720 #-----------------------------------------------------------------------------
647 #-----------------------------------------------------------------------------
721
648
722 def launch_kernel(*args, **kwargs):
649 def launch_kernel(*args, **kwargs):
723 """Launches a localhost IPython kernel, binding to the specified ports.
650 """Launches a localhost IPython kernel, binding to the specified ports.
724
651
725 This function simply calls entry_point.base_launch_kernel with the right first
652 This function simply calls entry_point.base_launch_kernel with the right first
726 command to start an ipkernel. See base_launch_kernel for arguments.
653 command to start an ipkernel. See base_launch_kernel for arguments.
727
654
728 Returns
655 Returns
729 -------
656 -------
730 A tuple of form:
657 A tuple of form:
731 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
658 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
732 where kernel_process is a Popen object and the ports are integers.
659 where kernel_process is a Popen object and the ports are integers.
733 """
660 """
734 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
661 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
735 *args, **kwargs)
662 *args, **kwargs)
736
663
737
664
738 def main():
665 def main():
739 """Run a PyKernel as an application"""
666 """Run an IPKernel as an application"""
740 app = IPKernelApp()
667 app = IPKernelApp()
741 app.initialize()
668 app.initialize()
742 app.start()
669 app.start()
743
670
744
671
745 if __name__ == '__main__':
672 if __name__ == '__main__':
746 main()
673 main()
General Comments 0
You need to be logged in to leave comments. Login now