##// END OF EJS Templates
Add support for (manually) activating logging, for now....
Fernando Perez -
Show More
@@ -1,641 +1,656 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25 # System library imports.
25 # System library imports.
26 import zmq
26 import zmq
27
27
28 # Local imports.
28 # Local imports.
29 from IPython.config.configurable import Configurable
29 from IPython.config.configurable import Configurable
30 from IPython.utils import io
30 from IPython.utils import io
31 from IPython.utils.jsonutil import json_clean
31 from IPython.utils.jsonutil import json_clean
32 from IPython.lib import pylabtools
32 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
33 from IPython.utils.traitlets import Instance, Float
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 start_kernel)
35 start_kernel)
36 from iostream import OutStream
36 from iostream import OutStream
37 from session import Session, Message
37 from session import Session, Message
38 from zmqshell import ZMQInteractiveShell
38 from zmqshell import ZMQInteractiveShell
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Globals
41 # Globals
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 # Module-level logger
44 # Module-level logger
45 logger = logging.getLogger(__name__)
45 logger = logging.getLogger(__name__)
46
46
47 # FIXME: this needs to be done more cleanly later, once we have proper
48 # configuration support. This is a library, so it shouldn't set a stream
49 # handler, see:
50 # http://docs.python.org/library/logging.html#configuring-logging-for-a-library
51 # But this lets us at least do developer debugging for now by manually turning
52 # it on/off. And once we have full config support, the client entry points
53 # will select their logging handlers, as well as passing to this library the
54 # logging level.
55
56 if 0: # dbg - set to 1 to actually see the messages.
57 logger.addHandler(logging.StreamHandler())
58 logger.setLevel(logging.DEBUG)
59
60 # /FIXME
61
47 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
48 # Main kernel class
63 # Main kernel class
49 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
50
65
51 class Kernel(Configurable):
66 class Kernel(Configurable):
52
67
53 #---------------------------------------------------------------------------
68 #---------------------------------------------------------------------------
54 # Kernel interface
69 # Kernel interface
55 #---------------------------------------------------------------------------
70 #---------------------------------------------------------------------------
56
71
57 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
58 session = Instance(Session)
73 session = Instance(Session)
59 reply_socket = Instance('zmq.Socket')
74 reply_socket = Instance('zmq.Socket')
60 pub_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
61 req_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
62
77
63 # Private interface
78 # Private interface
64
79
65 # Time to sleep after flushing the stdout/err buffers in each execute
80 # Time to sleep after flushing the stdout/err buffers in each execute
66 # cycle. While this introduces a hard limit on the minimal latency of the
81 # cycle. While this introduces a hard limit on the minimal latency of the
67 # execute cycle, it helps prevent output synchronization problems for
82 # execute cycle, it helps prevent output synchronization problems for
68 # clients.
83 # clients.
69 # Units are in seconds. The minimum zmq latency on local host is probably
84 # Units are in seconds. The minimum zmq latency on local host is probably
70 # ~150 microseconds, set this to 500us for now. We may need to increase it
85 # ~150 microseconds, set this to 500us for now. We may need to increase it
71 # a little if it's not enough after more interactive testing.
86 # a little if it's not enough after more interactive testing.
72 _execute_sleep = Float(0.0005, config=True)
87 _execute_sleep = Float(0.0005, config=True)
73
88
74 # Frequency of the kernel's event loop.
89 # Frequency of the kernel's event loop.
75 # Units are in seconds, kernel subclasses for GUI toolkits may need to
90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
76 # adapt to milliseconds.
91 # adapt to milliseconds.
77 _poll_interval = Float(0.05, config=True)
92 _poll_interval = Float(0.05, config=True)
78
93
79 # If the shutdown was requested over the network, we leave here the
94 # If the shutdown was requested over the network, we leave here the
80 # necessary reply message so it can be sent by our registered atexit
95 # necessary reply message so it can be sent by our registered atexit
81 # handler. This ensures that the reply is only sent to clients truly at
96 # handler. This ensures that the reply is only sent to clients truly at
82 # the end of our shutdown process (which happens after the underlying
97 # the end of our shutdown process (which happens after the underlying
83 # IPython shell's own shutdown).
98 # IPython shell's own shutdown).
84 _shutdown_message = None
99 _shutdown_message = None
85
100
86 # This is a dict of port number that the kernel is listening on. It is set
101 # This is a dict of port number that the kernel is listening on. It is set
87 # by record_ports and used by connect_request.
102 # by record_ports and used by connect_request.
88 _recorded_ports = None
103 _recorded_ports = None
89
104
90
105
91 def __init__(self, **kwargs):
106 def __init__(self, **kwargs):
92 super(Kernel, self).__init__(**kwargs)
107 super(Kernel, self).__init__(**kwargs)
93
108
94 # Before we even start up the shell, register *first* our exit handlers
109 # Before we even start up the shell, register *first* our exit handlers
95 # so they come before the shell's
110 # so they come before the shell's
96 atexit.register(self._at_shutdown)
111 atexit.register(self._at_shutdown)
97
112
98 # Initialize the InteractiveShell subclass
113 # Initialize the InteractiveShell subclass
99 self.shell = ZMQInteractiveShell.instance()
114 self.shell = ZMQInteractiveShell.instance()
100 self.shell.displayhook.session = self.session
115 self.shell.displayhook.session = self.session
101 self.shell.displayhook.pub_socket = self.pub_socket
116 self.shell.displayhook.pub_socket = self.pub_socket
102 self.shell.display_pub.session = self.session
117 self.shell.display_pub.session = self.session
103 self.shell.display_pub.pub_socket = self.pub_socket
118 self.shell.display_pub.pub_socket = self.pub_socket
104
119
105 # TMP - hack while developing
120 # TMP - hack while developing
106 self.shell._reply_content = None
121 self.shell._reply_content = None
107
122
108 # Build dict of handlers for message types
123 # Build dict of handlers for message types
109 msg_types = [ 'execute_request', 'complete_request',
124 msg_types = [ 'execute_request', 'complete_request',
110 'object_info_request', 'history_request',
125 'object_info_request', 'history_request',
111 'connect_request', 'shutdown_request']
126 'connect_request', 'shutdown_request']
112 self.handlers = {}
127 self.handlers = {}
113 for msg_type in msg_types:
128 for msg_type in msg_types:
114 self.handlers[msg_type] = getattr(self, msg_type)
129 self.handlers[msg_type] = getattr(self, msg_type)
115
130
116 def do_one_iteration(self):
131 def do_one_iteration(self):
117 """Do one iteration of the kernel's evaluation loop.
132 """Do one iteration of the kernel's evaluation loop.
118 """
133 """
119 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
120 if msg is None:
135 if msg is None:
121 return
136 return
122
137
123 # This assert will raise in versions of zeromq 2.0.7 and lesser.
138 # This assert will raise in versions of zeromq 2.0.7 and lesser.
124 # We now require 2.0.8 or above, so we can uncomment for safety.
139 # We now require 2.0.8 or above, so we can uncomment for safety.
125 # print(ident,msg, file=sys.__stdout__)
140 # print(ident,msg, file=sys.__stdout__)
126 assert ident is not None, "Missing message part."
141 assert ident is not None, "Missing message part."
127
142
128 # Print some info about this message and leave a '--->' marker, so it's
143 # Print some info about this message and leave a '--->' marker, so it's
129 # easier to trace visually the message chain when debugging. Each
144 # easier to trace visually the message chain when debugging. Each
130 # handler prints its message at the end.
145 # handler prints its message at the end.
131 # Eventually we'll move these from stdout to a logger.
146 # Eventually we'll move these from stdout to a logger.
132 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
133 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
134
149
135 # Find and call actual handler for message
150 # Find and call actual handler for message
136 handler = self.handlers.get(msg['msg_type'], None)
151 handler = self.handlers.get(msg['msg_type'], None)
137 if handler is None:
152 if handler is None:
138 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
139 else:
154 else:
140 handler(ident, msg)
155 handler(ident, msg)
141
156
142 # Check whether we should exit, in case the incoming message set the
157 # Check whether we should exit, in case the incoming message set the
143 # exit flag on
158 # exit flag on
144 if self.shell.exit_now:
159 if self.shell.exit_now:
145 logger.debug('\nExiting IPython kernel...')
160 logger.debug('\nExiting IPython kernel...')
146 # We do a normal, clean exit, which allows any actions registered
161 # We do a normal, clean exit, which allows any actions registered
147 # via atexit (such as history saving) to take place.
162 # via atexit (such as history saving) to take place.
148 sys.exit(0)
163 sys.exit(0)
149
164
150
165
151 def start(self):
166 def start(self):
152 """ Start the kernel main loop.
167 """ Start the kernel main loop.
153 """
168 """
154 while True:
169 while True:
155 time.sleep(self._poll_interval)
170 time.sleep(self._poll_interval)
156 self.do_one_iteration()
171 self.do_one_iteration()
157
172
158 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
173 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
159 """Record the ports that this kernel is using.
174 """Record the ports that this kernel is using.
160
175
161 The creator of the Kernel instance must call this methods if they
176 The creator of the Kernel instance must call this methods if they
162 want the :meth:`connect_request` method to return the port numbers.
177 want the :meth:`connect_request` method to return the port numbers.
163 """
178 """
164 self._recorded_ports = {
179 self._recorded_ports = {
165 'xrep_port' : xrep_port,
180 'xrep_port' : xrep_port,
166 'pub_port' : pub_port,
181 'pub_port' : pub_port,
167 'req_port' : req_port,
182 'req_port' : req_port,
168 'hb_port' : hb_port
183 'hb_port' : hb_port
169 }
184 }
170
185
171 #---------------------------------------------------------------------------
186 #---------------------------------------------------------------------------
172 # Kernel request handlers
187 # Kernel request handlers
173 #---------------------------------------------------------------------------
188 #---------------------------------------------------------------------------
174
189
175 def _publish_pyin(self, code, parent):
190 def _publish_pyin(self, code, parent):
176 """Publish the code request on the pyin stream."""
191 """Publish the code request on the pyin stream."""
177
192
178 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
193 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
179
194
180 def execute_request(self, ident, parent):
195 def execute_request(self, ident, parent):
181
196
182 status_msg = self.session.send(self.pub_socket,
197 status_msg = self.session.send(self.pub_socket,
183 u'status',
198 u'status',
184 {u'execution_state':u'busy'},
199 {u'execution_state':u'busy'},
185 parent=parent
200 parent=parent
186 )
201 )
187
202
188 try:
203 try:
189 content = parent[u'content']
204 content = parent[u'content']
190 code = content[u'code']
205 code = content[u'code']
191 silent = content[u'silent']
206 silent = content[u'silent']
192 except:
207 except:
193 logger.error("Got bad msg: ")
208 logger.error("Got bad msg: ")
194 logger.error(str(Message(parent)))
209 logger.error(str(Message(parent)))
195 return
210 return
196
211
197 shell = self.shell # we'll need this a lot here
212 shell = self.shell # we'll need this a lot here
198
213
199 # Replace raw_input. Note that is not sufficient to replace
214 # Replace raw_input. Note that is not sufficient to replace
200 # raw_input in the user namespace.
215 # raw_input in the user namespace.
201 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
216 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
202 __builtin__.raw_input = raw_input
217 __builtin__.raw_input = raw_input
203
218
204 # Set the parent message of the display hook and out streams.
219 # Set the parent message of the display hook and out streams.
205 shell.displayhook.set_parent(parent)
220 shell.displayhook.set_parent(parent)
206 shell.display_pub.set_parent(parent)
221 shell.display_pub.set_parent(parent)
207 sys.stdout.set_parent(parent)
222 sys.stdout.set_parent(parent)
208 sys.stderr.set_parent(parent)
223 sys.stderr.set_parent(parent)
209
224
210 # Re-broadcast our input for the benefit of listening clients, and
225 # Re-broadcast our input for the benefit of listening clients, and
211 # start computing output
226 # start computing output
212 if not silent:
227 if not silent:
213 self._publish_pyin(code, parent)
228 self._publish_pyin(code, parent)
214
229
215 reply_content = {}
230 reply_content = {}
216 try:
231 try:
217 if silent:
232 if silent:
218 # run_code uses 'exec' mode, so no displayhook will fire, and it
233 # run_code uses 'exec' mode, so no displayhook will fire, and it
219 # doesn't call logging or history manipulations. Print
234 # doesn't call logging or history manipulations. Print
220 # statements in that code will obviously still execute.
235 # statements in that code will obviously still execute.
221 shell.run_code(code)
236 shell.run_code(code)
222 else:
237 else:
223 # FIXME: the shell calls the exception handler itself.
238 # FIXME: the shell calls the exception handler itself.
224 shell._reply_content = None
239 shell._reply_content = None
225 shell.run_cell(code)
240 shell.run_cell(code)
226 except:
241 except:
227 status = u'error'
242 status = u'error'
228 # FIXME: this code right now isn't being used yet by default,
243 # FIXME: this code right now isn't being used yet by default,
229 # because the runlines() call above directly fires off exception
244 # because the runlines() call above directly fires off exception
230 # reporting. This code, therefore, is only active in the scenario
245 # reporting. This code, therefore, is only active in the scenario
231 # where runlines itself has an unhandled exception. We need to
246 # where runlines itself has an unhandled exception. We need to
232 # uniformize this, for all exception construction to come from a
247 # uniformize this, for all exception construction to come from a
233 # single location in the codbase.
248 # single location in the codbase.
234 etype, evalue, tb = sys.exc_info()
249 etype, evalue, tb = sys.exc_info()
235 tb_list = traceback.format_exception(etype, evalue, tb)
250 tb_list = traceback.format_exception(etype, evalue, tb)
236 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
251 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
237 else:
252 else:
238 status = u'ok'
253 status = u'ok'
239
254
240 reply_content[u'status'] = status
255 reply_content[u'status'] = status
241
256
242 # Return the execution counter so clients can display prompts
257 # Return the execution counter so clients can display prompts
243 reply_content['execution_count'] = shell.execution_count -1
258 reply_content['execution_count'] = shell.execution_count -1
244
259
245 # FIXME - fish exception info out of shell, possibly left there by
260 # FIXME - fish exception info out of shell, possibly left there by
246 # runlines. We'll need to clean up this logic later.
261 # runlines. We'll need to clean up this logic later.
247 if shell._reply_content is not None:
262 if shell._reply_content is not None:
248 reply_content.update(shell._reply_content)
263 reply_content.update(shell._reply_content)
249
264
250 # At this point, we can tell whether the main code execution succeeded
265 # At this point, we can tell whether the main code execution succeeded
251 # or not. If it did, we proceed to evaluate user_variables/expressions
266 # or not. If it did, we proceed to evaluate user_variables/expressions
252 if reply_content['status'] == 'ok':
267 if reply_content['status'] == 'ok':
253 reply_content[u'user_variables'] = \
268 reply_content[u'user_variables'] = \
254 shell.user_variables(content[u'user_variables'])
269 shell.user_variables(content[u'user_variables'])
255 reply_content[u'user_expressions'] = \
270 reply_content[u'user_expressions'] = \
256 shell.user_expressions(content[u'user_expressions'])
271 shell.user_expressions(content[u'user_expressions'])
257 else:
272 else:
258 # If there was an error, don't even try to compute variables or
273 # If there was an error, don't even try to compute variables or
259 # expressions
274 # expressions
260 reply_content[u'user_variables'] = {}
275 reply_content[u'user_variables'] = {}
261 reply_content[u'user_expressions'] = {}
276 reply_content[u'user_expressions'] = {}
262
277
263 # Payloads should be retrieved regardless of outcome, so we can both
278 # Payloads should be retrieved regardless of outcome, so we can both
264 # recover partial output (that could have been generated early in a
279 # recover partial output (that could have been generated early in a
265 # block, before an error) and clear the payload system always.
280 # block, before an error) and clear the payload system always.
266 reply_content[u'payload'] = shell.payload_manager.read_payload()
281 reply_content[u'payload'] = shell.payload_manager.read_payload()
267 # Be agressive about clearing the payload because we don't want
282 # Be agressive about clearing the payload because we don't want
268 # it to sit in memory until the next execute_request comes in.
283 # it to sit in memory until the next execute_request comes in.
269 shell.payload_manager.clear_payload()
284 shell.payload_manager.clear_payload()
270
285
271 # Flush output before sending the reply.
286 # Flush output before sending the reply.
272 sys.stdout.flush()
287 sys.stdout.flush()
273 sys.stderr.flush()
288 sys.stderr.flush()
274 # FIXME: on rare occasions, the flush doesn't seem to make it to the
289 # FIXME: on rare occasions, the flush doesn't seem to make it to the
275 # clients... This seems to mitigate the problem, but we definitely need
290 # clients... This seems to mitigate the problem, but we definitely need
276 # to better understand what's going on.
291 # to better understand what's going on.
277 if self._execute_sleep:
292 if self._execute_sleep:
278 time.sleep(self._execute_sleep)
293 time.sleep(self._execute_sleep)
279
294
280 # Send the reply.
295 # Send the reply.
281 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
296 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
282 reply_content, parent, ident=ident)
297 reply_content, parent, ident=ident)
283 logger.debug(str(reply_msg))
298 logger.debug(str(reply_msg))
284
299
285 if reply_msg['content']['status'] == u'error':
300 if reply_msg['content']['status'] == u'error':
286 self._abort_queue()
301 self._abort_queue()
287
302
288 status_msg = self.session.send(self.pub_socket,
303 status_msg = self.session.send(self.pub_socket,
289 u'status',
304 u'status',
290 {u'execution_state':u'idle'},
305 {u'execution_state':u'idle'},
291 parent=parent
306 parent=parent
292 )
307 )
293
308
294 def complete_request(self, ident, parent):
309 def complete_request(self, ident, parent):
295 txt, matches = self._complete(parent)
310 txt, matches = self._complete(parent)
296 matches = {'matches' : matches,
311 matches = {'matches' : matches,
297 'matched_text' : txt,
312 'matched_text' : txt,
298 'status' : 'ok'}
313 'status' : 'ok'}
299 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
314 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
300 matches, parent, ident)
315 matches, parent, ident)
301 logger.debug(str(completion_msg))
316 logger.debug(str(completion_msg))
302
317
303 def object_info_request(self, ident, parent):
318 def object_info_request(self, ident, parent):
304 object_info = self.shell.object_inspect(parent['content']['oname'])
319 object_info = self.shell.object_inspect(parent['content']['oname'])
305 # Before we send this object over, we scrub it for JSON usage
320 # Before we send this object over, we scrub it for JSON usage
306 oinfo = json_clean(object_info)
321 oinfo = json_clean(object_info)
307 msg = self.session.send(self.reply_socket, 'object_info_reply',
322 msg = self.session.send(self.reply_socket, 'object_info_reply',
308 oinfo, parent, ident)
323 oinfo, parent, ident)
309 logger.debug(msg)
324 logger.debug(msg)
310
325
311 def history_request(self, ident, parent):
326 def history_request(self, ident, parent):
312 output = parent['content']['output']
327 output = parent['content']['output']
313 index = parent['content']['index']
328 index = parent['content']['index']
314 raw = parent['content']['raw']
329 raw = parent['content']['raw']
315 hist = self.shell.get_history(index=index, raw=raw, output=output)
330 hist = self.shell.get_history(index=index, raw=raw, output=output)
316 content = {'history' : hist}
331 content = {'history' : hist}
317 msg = self.session.send(self.reply_socket, 'history_reply',
332 msg = self.session.send(self.reply_socket, 'history_reply',
318 content, parent, ident)
333 content, parent, ident)
319 logger.debug(str(msg))
334 logger.debug(str(msg))
320
335
321 def connect_request(self, ident, parent):
336 def connect_request(self, ident, parent):
322 if self._recorded_ports is not None:
337 if self._recorded_ports is not None:
323 content = self._recorded_ports.copy()
338 content = self._recorded_ports.copy()
324 else:
339 else:
325 content = {}
340 content = {}
326 msg = self.session.send(self.reply_socket, 'connect_reply',
341 msg = self.session.send(self.reply_socket, 'connect_reply',
327 content, parent, ident)
342 content, parent, ident)
328 logger.debug(msg)
343 logger.debug(msg)
329
344
330 def shutdown_request(self, ident, parent):
345 def shutdown_request(self, ident, parent):
331 self.shell.exit_now = True
346 self.shell.exit_now = True
332 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
347 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
333 sys.exit(0)
348 sys.exit(0)
334
349
335 #---------------------------------------------------------------------------
350 #---------------------------------------------------------------------------
336 # Protected interface
351 # Protected interface
337 #---------------------------------------------------------------------------
352 #---------------------------------------------------------------------------
338
353
339 def _abort_queue(self):
354 def _abort_queue(self):
340 while True:
355 while True:
341 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
356 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
342 if msg is None:
357 if msg is None:
343 break
358 break
344 else:
359 else:
345 assert ident is not None, \
360 assert ident is not None, \
346 "Unexpected missing message part."
361 "Unexpected missing message part."
347
362
348 logger.debug("Aborting:\n"+str(Message(msg)))
363 logger.debug("Aborting:\n"+str(Message(msg)))
349 msg_type = msg['msg_type']
364 msg_type = msg['msg_type']
350 reply_type = msg_type.split('_')[0] + '_reply'
365 reply_type = msg_type.split('_')[0] + '_reply'
351 reply_msg = self.session.send(self.reply_socket, reply_type,
366 reply_msg = self.session.send(self.reply_socket, reply_type,
352 {'status' : 'aborted'}, msg, ident=ident)
367 {'status' : 'aborted'}, msg, ident=ident)
353 logger.debug(reply_msg)
368 logger.debug(reply_msg)
354 # We need to wait a bit for requests to come in. This can probably
369 # We need to wait a bit for requests to come in. This can probably
355 # be set shorter for true asynchronous clients.
370 # be set shorter for true asynchronous clients.
356 time.sleep(0.1)
371 time.sleep(0.1)
357
372
358 def _raw_input(self, prompt, ident, parent):
373 def _raw_input(self, prompt, ident, parent):
359 # Flush output before making the request.
374 # Flush output before making the request.
360 sys.stderr.flush()
375 sys.stderr.flush()
361 sys.stdout.flush()
376 sys.stdout.flush()
362
377
363 # Send the input request.
378 # Send the input request.
364 content = dict(prompt=prompt)
379 content = dict(prompt=prompt)
365 msg = self.session.send(self.req_socket, u'input_request', content, parent)
380 msg = self.session.send(self.req_socket, u'input_request', content, parent)
366
381
367 # Await a response.
382 # Await a response.
368 ident, reply = self.session.recv(self.req_socket, 0)
383 ident, reply = self.session.recv(self.req_socket, 0)
369 try:
384 try:
370 value = reply['content']['value']
385 value = reply['content']['value']
371 except:
386 except:
372 logger.error("Got bad raw_input reply: ")
387 logger.error("Got bad raw_input reply: ")
373 logger.error(str(Message(parent)))
388 logger.error(str(Message(parent)))
374 value = ''
389 value = ''
375 return value
390 return value
376
391
377 def _complete(self, msg):
392 def _complete(self, msg):
378 c = msg['content']
393 c = msg['content']
379 try:
394 try:
380 cpos = int(c['cursor_pos'])
395 cpos = int(c['cursor_pos'])
381 except:
396 except:
382 # If we don't get something that we can convert to an integer, at
397 # If we don't get something that we can convert to an integer, at
383 # least attempt the completion guessing the cursor is at the end of
398 # least attempt the completion guessing the cursor is at the end of
384 # the text, if there's any, and otherwise of the line
399 # the text, if there's any, and otherwise of the line
385 cpos = len(c['text'])
400 cpos = len(c['text'])
386 if cpos==0:
401 if cpos==0:
387 cpos = len(c['line'])
402 cpos = len(c['line'])
388 return self.shell.complete(c['text'], c['line'], cpos)
403 return self.shell.complete(c['text'], c['line'], cpos)
389
404
390 def _object_info(self, context):
405 def _object_info(self, context):
391 symbol, leftover = self._symbol_from_context(context)
406 symbol, leftover = self._symbol_from_context(context)
392 if symbol is not None and not leftover:
407 if symbol is not None and not leftover:
393 doc = getattr(symbol, '__doc__', '')
408 doc = getattr(symbol, '__doc__', '')
394 else:
409 else:
395 doc = ''
410 doc = ''
396 object_info = dict(docstring = doc)
411 object_info = dict(docstring = doc)
397 return object_info
412 return object_info
398
413
399 def _symbol_from_context(self, context):
414 def _symbol_from_context(self, context):
400 if not context:
415 if not context:
401 return None, context
416 return None, context
402
417
403 base_symbol_string = context[0]
418 base_symbol_string = context[0]
404 symbol = self.shell.user_ns.get(base_symbol_string, None)
419 symbol = self.shell.user_ns.get(base_symbol_string, None)
405 if symbol is None:
420 if symbol is None:
406 symbol = __builtin__.__dict__.get(base_symbol_string, None)
421 symbol = __builtin__.__dict__.get(base_symbol_string, None)
407 if symbol is None:
422 if symbol is None:
408 return None, context
423 return None, context
409
424
410 context = context[1:]
425 context = context[1:]
411 for i, name in enumerate(context):
426 for i, name in enumerate(context):
412 new_symbol = getattr(symbol, name, None)
427 new_symbol = getattr(symbol, name, None)
413 if new_symbol is None:
428 if new_symbol is None:
414 return symbol, context[i:]
429 return symbol, context[i:]
415 else:
430 else:
416 symbol = new_symbol
431 symbol = new_symbol
417
432
418 return symbol, []
433 return symbol, []
419
434
420 def _at_shutdown(self):
435 def _at_shutdown(self):
421 """Actions taken at shutdown by the kernel, called by python's atexit.
436 """Actions taken at shutdown by the kernel, called by python's atexit.
422 """
437 """
423 # io.rprint("Kernel at_shutdown") # dbg
438 # io.rprint("Kernel at_shutdown") # dbg
424 if self._shutdown_message is not None:
439 if self._shutdown_message is not None:
425 self.session.send(self.reply_socket, self._shutdown_message)
440 self.session.send(self.reply_socket, self._shutdown_message)
426 self.session.send(self.pub_socket, self._shutdown_message)
441 self.session.send(self.pub_socket, self._shutdown_message)
427 logger.debug(str(self._shutdown_message))
442 logger.debug(str(self._shutdown_message))
428 # A very short sleep to give zmq time to flush its message buffers
443 # A very short sleep to give zmq time to flush its message buffers
429 # before Python truly shuts down.
444 # before Python truly shuts down.
430 time.sleep(0.01)
445 time.sleep(0.01)
431
446
432
447
433 class QtKernel(Kernel):
448 class QtKernel(Kernel):
434 """A Kernel subclass with Qt support."""
449 """A Kernel subclass with Qt support."""
435
450
436 def start(self):
451 def start(self):
437 """Start a kernel with QtPy4 event loop integration."""
452 """Start a kernel with QtPy4 event loop integration."""
438
453
439 from PyQt4 import QtCore
454 from PyQt4 import QtCore
440 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
455 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
441
456
442 self.app = get_app_qt4([" "])
457 self.app = get_app_qt4([" "])
443 self.app.setQuitOnLastWindowClosed(False)
458 self.app.setQuitOnLastWindowClosed(False)
444 self.timer = QtCore.QTimer()
459 self.timer = QtCore.QTimer()
445 self.timer.timeout.connect(self.do_one_iteration)
460 self.timer.timeout.connect(self.do_one_iteration)
446 # Units for the timer are in milliseconds
461 # Units for the timer are in milliseconds
447 self.timer.start(1000*self._poll_interval)
462 self.timer.start(1000*self._poll_interval)
448 start_event_loop_qt4(self.app)
463 start_event_loop_qt4(self.app)
449
464
450
465
451 class WxKernel(Kernel):
466 class WxKernel(Kernel):
452 """A Kernel subclass with Wx support."""
467 """A Kernel subclass with Wx support."""
453
468
454 def start(self):
469 def start(self):
455 """Start a kernel with wx event loop support."""
470 """Start a kernel with wx event loop support."""
456
471
457 import wx
472 import wx
458 from IPython.lib.guisupport import start_event_loop_wx
473 from IPython.lib.guisupport import start_event_loop_wx
459
474
460 doi = self.do_one_iteration
475 doi = self.do_one_iteration
461 # Wx uses milliseconds
476 # Wx uses milliseconds
462 poll_interval = int(1000*self._poll_interval)
477 poll_interval = int(1000*self._poll_interval)
463
478
464 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
479 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
465 # We make the Frame hidden when we create it in the main app below.
480 # We make the Frame hidden when we create it in the main app below.
466 class TimerFrame(wx.Frame):
481 class TimerFrame(wx.Frame):
467 def __init__(self, func):
482 def __init__(self, func):
468 wx.Frame.__init__(self, None, -1)
483 wx.Frame.__init__(self, None, -1)
469 self.timer = wx.Timer(self)
484 self.timer = wx.Timer(self)
470 # Units for the timer are in milliseconds
485 # Units for the timer are in milliseconds
471 self.timer.Start(poll_interval)
486 self.timer.Start(poll_interval)
472 self.Bind(wx.EVT_TIMER, self.on_timer)
487 self.Bind(wx.EVT_TIMER, self.on_timer)
473 self.func = func
488 self.func = func
474
489
475 def on_timer(self, event):
490 def on_timer(self, event):
476 self.func()
491 self.func()
477
492
478 # We need a custom wx.App to create our Frame subclass that has the
493 # We need a custom wx.App to create our Frame subclass that has the
479 # wx.Timer to drive the ZMQ event loop.
494 # wx.Timer to drive the ZMQ event loop.
480 class IPWxApp(wx.App):
495 class IPWxApp(wx.App):
481 def OnInit(self):
496 def OnInit(self):
482 self.frame = TimerFrame(doi)
497 self.frame = TimerFrame(doi)
483 self.frame.Show(False)
498 self.frame.Show(False)
484 return True
499 return True
485
500
486 # The redirect=False here makes sure that wx doesn't replace
501 # The redirect=False here makes sure that wx doesn't replace
487 # sys.stdout/stderr with its own classes.
502 # sys.stdout/stderr with its own classes.
488 self.app = IPWxApp(redirect=False)
503 self.app = IPWxApp(redirect=False)
489 start_event_loop_wx(self.app)
504 start_event_loop_wx(self.app)
490
505
491
506
492 class TkKernel(Kernel):
507 class TkKernel(Kernel):
493 """A Kernel subclass with Tk support."""
508 """A Kernel subclass with Tk support."""
494
509
495 def start(self):
510 def start(self):
496 """Start a Tk enabled event loop."""
511 """Start a Tk enabled event loop."""
497
512
498 import Tkinter
513 import Tkinter
499 doi = self.do_one_iteration
514 doi = self.do_one_iteration
500 # Tk uses milliseconds
515 # Tk uses milliseconds
501 poll_interval = int(1000*self._poll_interval)
516 poll_interval = int(1000*self._poll_interval)
502 # For Tkinter, we create a Tk object and call its withdraw method.
517 # For Tkinter, we create a Tk object and call its withdraw method.
503 class Timer(object):
518 class Timer(object):
504 def __init__(self, func):
519 def __init__(self, func):
505 self.app = Tkinter.Tk()
520 self.app = Tkinter.Tk()
506 self.app.withdraw()
521 self.app.withdraw()
507 self.func = func
522 self.func = func
508
523
509 def on_timer(self):
524 def on_timer(self):
510 self.func()
525 self.func()
511 self.app.after(poll_interval, self.on_timer)
526 self.app.after(poll_interval, self.on_timer)
512
527
513 def start(self):
528 def start(self):
514 self.on_timer() # Call it once to get things going.
529 self.on_timer() # Call it once to get things going.
515 self.app.mainloop()
530 self.app.mainloop()
516
531
517 self.timer = Timer(doi)
532 self.timer = Timer(doi)
518 self.timer.start()
533 self.timer.start()
519
534
520
535
521 class GTKKernel(Kernel):
536 class GTKKernel(Kernel):
522 """A Kernel subclass with GTK support."""
537 """A Kernel subclass with GTK support."""
523
538
524 def start(self):
539 def start(self):
525 """Start the kernel, coordinating with the GTK event loop"""
540 """Start the kernel, coordinating with the GTK event loop"""
526 from .gui.gtkembed import GTKEmbed
541 from .gui.gtkembed import GTKEmbed
527
542
528 gtk_kernel = GTKEmbed(self)
543 gtk_kernel = GTKEmbed(self)
529 gtk_kernel.start()
544 gtk_kernel.start()
530
545
531
546
532 #-----------------------------------------------------------------------------
547 #-----------------------------------------------------------------------------
533 # Kernel main and launch functions
548 # Kernel main and launch functions
534 #-----------------------------------------------------------------------------
549 #-----------------------------------------------------------------------------
535
550
536 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
551 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
537 independent=False, pylab=False, colors=None):
552 independent=False, pylab=False, colors=None):
538 """Launches a localhost kernel, binding to the specified ports.
553 """Launches a localhost kernel, binding to the specified ports.
539
554
540 Parameters
555 Parameters
541 ----------
556 ----------
542 ip : str, optional
557 ip : str, optional
543 The ip address the kernel will bind to.
558 The ip address the kernel will bind to.
544
559
545 xrep_port : int, optional
560 xrep_port : int, optional
546 The port to use for XREP channel.
561 The port to use for XREP channel.
547
562
548 pub_port : int, optional
563 pub_port : int, optional
549 The port to use for the SUB channel.
564 The port to use for the SUB channel.
550
565
551 req_port : int, optional
566 req_port : int, optional
552 The port to use for the REQ (raw input) channel.
567 The port to use for the REQ (raw input) channel.
553
568
554 hb_port : int, optional
569 hb_port : int, optional
555 The port to use for the hearbeat REP channel.
570 The port to use for the hearbeat REP channel.
556
571
557 independent : bool, optional (default False)
572 independent : bool, optional (default False)
558 If set, the kernel process is guaranteed to survive if this process
573 If set, the kernel process is guaranteed to survive if this process
559 dies. If not set, an effort is made to ensure that the kernel is killed
574 dies. If not set, an effort is made to ensure that the kernel is killed
560 when this process dies. Note that in this case it is still good practice
575 when this process dies. Note that in this case it is still good practice
561 to kill kernels manually before exiting.
576 to kill kernels manually before exiting.
562
577
563 pylab : bool or string, optional (default False)
578 pylab : bool or string, optional (default False)
564 If not False, the kernel will be launched with pylab enabled. If a
579 If not False, the kernel will be launched with pylab enabled. If a
565 string is passed, matplotlib will use the specified backend. Otherwise,
580 string is passed, matplotlib will use the specified backend. Otherwise,
566 matplotlib's default backend will be used.
581 matplotlib's default backend will be used.
567
582
568 colors : None or string, optional (default None)
583 colors : None or string, optional (default None)
569 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
584 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
570
585
571 Returns
586 Returns
572 -------
587 -------
573 A tuple of form:
588 A tuple of form:
574 (kernel_process, xrep_port, pub_port, req_port)
589 (kernel_process, xrep_port, pub_port, req_port)
575 where kernel_process is a Popen object and the ports are integers.
590 where kernel_process is a Popen object and the ports are integers.
576 """
591 """
577 extra_arguments = []
592 extra_arguments = []
578 if pylab:
593 if pylab:
579 extra_arguments.append('--pylab')
594 extra_arguments.append('--pylab')
580 if isinstance(pylab, basestring):
595 if isinstance(pylab, basestring):
581 extra_arguments.append(pylab)
596 extra_arguments.append(pylab)
582 if ip is not None:
597 if ip is not None:
583 extra_arguments.append('--ip')
598 extra_arguments.append('--ip')
584 if isinstance(ip, basestring):
599 if isinstance(ip, basestring):
585 extra_arguments.append(ip)
600 extra_arguments.append(ip)
586 if colors is not None:
601 if colors is not None:
587 extra_arguments.append('--colors')
602 extra_arguments.append('--colors')
588 extra_arguments.append(colors)
603 extra_arguments.append(colors)
589 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
604 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
590 xrep_port, pub_port, req_port, hb_port,
605 xrep_port, pub_port, req_port, hb_port,
591 independent, extra_arguments)
606 independent, extra_arguments)
592
607
593
608
594 def main():
609 def main():
595 """ The IPython kernel main entry point.
610 """ The IPython kernel main entry point.
596 """
611 """
597 parser = make_argument_parser()
612 parser = make_argument_parser()
598 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
613 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
599 const='auto', help = \
614 const='auto', help = \
600 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
615 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
601 given, the GUI backend is matplotlib's, otherwise use one of: \
616 given, the GUI backend is matplotlib's, otherwise use one of: \
602 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
617 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
603 parser.add_argument('--colors',
618 parser.add_argument('--colors',
604 type=str, dest='colors',
619 type=str, dest='colors',
605 help="Set the color scheme (NoColor, Linux, and LightBG).",
620 help="Set the color scheme (NoColor, Linux, and LightBG).",
606 metavar='ZMQInteractiveShell.colors')
621 metavar='ZMQInteractiveShell.colors')
607 namespace = parser.parse_args()
622 namespace = parser.parse_args()
608
623
609 kernel_class = Kernel
624 kernel_class = Kernel
610
625
611 kernel_classes = {
626 kernel_classes = {
612 'qt' : QtKernel,
627 'qt' : QtKernel,
613 'qt4': QtKernel,
628 'qt4': QtKernel,
614 'inline': Kernel,
629 'inline': Kernel,
615 'wx' : WxKernel,
630 'wx' : WxKernel,
616 'tk' : TkKernel,
631 'tk' : TkKernel,
617 'gtk': GTKKernel,
632 'gtk': GTKKernel,
618 }
633 }
619 if namespace.pylab:
634 if namespace.pylab:
620 if namespace.pylab == 'auto':
635 if namespace.pylab == 'auto':
621 gui, backend = pylabtools.find_gui_and_backend()
636 gui, backend = pylabtools.find_gui_and_backend()
622 else:
637 else:
623 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
638 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
624 kernel_class = kernel_classes.get(gui)
639 kernel_class = kernel_classes.get(gui)
625 if kernel_class is None:
640 if kernel_class is None:
626 raise ValueError('GUI is not supported: %r' % gui)
641 raise ValueError('GUI is not supported: %r' % gui)
627 pylabtools.activate_matplotlib(backend)
642 pylabtools.activate_matplotlib(backend)
628 if namespace.colors:
643 if namespace.colors:
629 ZMQInteractiveShell.colors=namespace.colors
644 ZMQInteractiveShell.colors=namespace.colors
630
645
631 kernel = make_kernel(namespace, kernel_class, OutStream)
646 kernel = make_kernel(namespace, kernel_class, OutStream)
632
647
633 if namespace.pylab:
648 if namespace.pylab:
634 pylabtools.import_pylab(kernel.shell.user_ns, backend,
649 pylabtools.import_pylab(kernel.shell.user_ns, backend,
635 shell=kernel.shell)
650 shell=kernel.shell)
636
651
637 start_kernel(namespace, kernel)
652 start_kernel(namespace, kernel)
638
653
639
654
640 if __name__ == '__main__':
655 if __name__ == '__main__':
641 main()
656 main()
General Comments 0
You need to be logged in to leave comments. Login now