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