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