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