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