##// END OF EJS Templates
fixup shutdown/exit now that we use IOLoop...
MinRK -
Show More
@@ -1,883 +1,888 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 import uuid
25 import uuid
26
26
27 from datetime import datetime
27 from datetime import datetime
28 from signal import (
28 from signal import (
29 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
29 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
30 )
30 )
31
31
32 # System library imports
32 # System library imports
33 import zmq
33 import zmq
34 from zmq.eventloop import ioloop
34 from zmq.eventloop import ioloop
35 from zmq.eventloop.zmqstream import ZMQStream
35 from zmq.eventloop.zmqstream import ZMQStream
36
36
37 # Local imports
37 # Local imports
38 from IPython.core import pylabtools
38 from IPython.core import pylabtools
39 from IPython.config.configurable import Configurable
39 from IPython.config.configurable import Configurable
40 from IPython.config.application import boolean_flag, catch_config_error
40 from IPython.config.application import boolean_flag, catch_config_error
41 from IPython.core.application import ProfileDir
41 from IPython.core.application import ProfileDir
42 from IPython.core.error import StdinNotImplementedError
42 from IPython.core.error import StdinNotImplementedError
43 from IPython.core.shellapp import (
43 from IPython.core.shellapp import (
44 InteractiveShellApp, shell_flags, shell_aliases
44 InteractiveShellApp, shell_flags, shell_aliases
45 )
45 )
46 from IPython.utils import io
46 from IPython.utils import io
47 from IPython.utils import py3compat
47 from IPython.utils import py3compat
48 from IPython.utils.frame import extract_module_locals
48 from IPython.utils.frame import extract_module_locals
49 from IPython.utils.jsonutil import json_clean
49 from IPython.utils.jsonutil import json_clean
50 from IPython.utils.traitlets import (
50 from IPython.utils.traitlets import (
51 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
51 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
52 )
52 )
53
53
54 from entry_point import base_launch_kernel
54 from entry_point import base_launch_kernel
55 from kernelapp import KernelApp, kernel_flags, kernel_aliases
55 from kernelapp import KernelApp, kernel_flags, kernel_aliases
56 from serialize import serialize_object, unpack_apply_message
56 from serialize import serialize_object, unpack_apply_message
57 from session import Session, Message
57 from session import Session, Message
58 from zmqshell import ZMQInteractiveShell
58 from zmqshell import ZMQInteractiveShell
59
59
60
60
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 # Main kernel class
62 # Main kernel class
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64
64
65 class Kernel(Configurable):
65 class Kernel(Configurable):
66
66
67 #---------------------------------------------------------------------------
67 #---------------------------------------------------------------------------
68 # Kernel interface
68 # Kernel interface
69 #---------------------------------------------------------------------------
69 #---------------------------------------------------------------------------
70
70
71 # attribute to override with a GUI
71 # attribute to override with a GUI
72 eventloop = Any(None)
72 eventloop = Any(None)
73 def _eventloop_changed(self, name, old, new):
73 def _eventloop_changed(self, name, old, new):
74 """schedule call to eventloop from IOLoop"""
74 """schedule call to eventloop from IOLoop"""
75 loop = ioloop.IOLoop.instance()
75 loop = ioloop.IOLoop.instance()
76 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
76 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
77
77
78 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
78 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
79 session = Instance(Session)
79 session = Instance(Session)
80 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
80 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
81 shell_streams = List()
81 shell_streams = List()
82 control_stream = Instance(ZMQStream)
82 control_stream = Instance(ZMQStream)
83 iopub_stream = Instance(ZMQStream)
83 iopub_stream = Instance(ZMQStream)
84 stdin_socket = Instance(zmq.Socket)
84 stdin_socket = Instance(zmq.Socket)
85 log = Instance(logging.Logger)
85 log = Instance(logging.Logger)
86
86
87 user_module = Instance('types.ModuleType')
87 user_module = Instance('types.ModuleType')
88 def _user_module_changed(self, name, old, new):
88 def _user_module_changed(self, name, old, new):
89 if self.shell is not None:
89 if self.shell is not None:
90 self.shell.user_module = new
90 self.shell.user_module = new
91
91
92 user_ns = Dict(default_value=None)
92 user_ns = Dict(default_value=None)
93 def _user_ns_changed(self, name, old, new):
93 def _user_ns_changed(self, name, old, new):
94 if self.shell is not None:
94 if self.shell is not None:
95 self.shell.user_ns = new
95 self.shell.user_ns = new
96 self.shell.init_user_ns()
96 self.shell.init_user_ns()
97
97
98 # identities:
98 # identities:
99 int_id = Integer(-1)
99 int_id = Integer(-1)
100 ident = Unicode()
100 ident = Unicode()
101
101
102 def _ident_default(self):
102 def _ident_default(self):
103 return unicode(uuid.uuid4())
103 return unicode(uuid.uuid4())
104
104
105
105
106 # Private interface
106 # Private interface
107
107
108 # Time to sleep after flushing the stdout/err buffers in each execute
108 # Time to sleep after flushing the stdout/err buffers in each execute
109 # cycle. While this introduces a hard limit on the minimal latency of the
109 # cycle. While this introduces a hard limit on the minimal latency of the
110 # execute cycle, it helps prevent output synchronization problems for
110 # execute cycle, it helps prevent output synchronization problems for
111 # clients.
111 # clients.
112 # Units are in seconds. The minimum zmq latency on local host is probably
112 # Units are in seconds. The minimum zmq latency on local host is probably
113 # ~150 microseconds, set this to 500us for now. We may need to increase it
113 # ~150 microseconds, set this to 500us for now. We may need to increase it
114 # a little if it's not enough after more interactive testing.
114 # a little if it's not enough after more interactive testing.
115 _execute_sleep = Float(0.0005, config=True)
115 _execute_sleep = Float(0.0005, config=True)
116
116
117 # Frequency of the kernel's event loop.
117 # Frequency of the kernel's event loop.
118 # Units are in seconds, kernel subclasses for GUI toolkits may need to
118 # Units are in seconds, kernel subclasses for GUI toolkits may need to
119 # adapt to milliseconds.
119 # adapt to milliseconds.
120 _poll_interval = Float(0.05, config=True)
120 _poll_interval = Float(0.05, config=True)
121
121
122 # If the shutdown was requested over the network, we leave here the
122 # If the shutdown was requested over the network, we leave here the
123 # necessary reply message so it can be sent by our registered atexit
123 # necessary reply message so it can be sent by our registered atexit
124 # handler. This ensures that the reply is only sent to clients truly at
124 # handler. This ensures that the reply is only sent to clients truly at
125 # the end of our shutdown process (which happens after the underlying
125 # the end of our shutdown process (which happens after the underlying
126 # IPython shell's own shutdown).
126 # IPython shell's own shutdown).
127 _shutdown_message = None
127 _shutdown_message = None
128
128
129 # This is a dict of port number that the kernel is listening on. It is set
129 # This is a dict of port number that the kernel is listening on. It is set
130 # by record_ports and used by connect_request.
130 # by record_ports and used by connect_request.
131 _recorded_ports = Dict()
131 _recorded_ports = Dict()
132
132
133 # set of aborted msg_ids
133 # set of aborted msg_ids
134 aborted = Set()
134 aborted = Set()
135
135
136
136
137 def __init__(self, **kwargs):
137 def __init__(self, **kwargs):
138 super(Kernel, self).__init__(**kwargs)
138 super(Kernel, self).__init__(**kwargs)
139
139
140 # Initialize the InteractiveShell subclass
140 # Initialize the InteractiveShell subclass
141 self.shell = ZMQInteractiveShell.instance(config=self.config,
141 self.shell = ZMQInteractiveShell.instance(config=self.config,
142 profile_dir = self.profile_dir,
142 profile_dir = self.profile_dir,
143 user_module = self.user_module,
143 user_module = self.user_module,
144 user_ns = self.user_ns,
144 user_ns = self.user_ns,
145 )
145 )
146 self.shell.displayhook.session = self.session
146 self.shell.displayhook.session = self.session
147 self.shell.displayhook.pub_socket = self.iopub_stream.socket
147 self.shell.displayhook.pub_socket = self.iopub_stream.socket
148 self.shell.display_pub.session = self.session
148 self.shell.display_pub.session = self.session
149 self.shell.display_pub.pub_socket = self.iopub_stream.socket
149 self.shell.display_pub.pub_socket = self.iopub_stream.socket
150
150
151 # TMP - hack while developing
151 # TMP - hack while developing
152 self.shell._reply_content = None
152 self.shell._reply_content = None
153
153
154 # Build dict of handlers for message types
154 # Build dict of handlers for message types
155 msg_types = [ 'execute_request', 'complete_request',
155 msg_types = [ 'execute_request', 'complete_request',
156 'object_info_request', 'history_request',
156 'object_info_request', 'history_request',
157 'connect_request', 'shutdown_request',
157 'connect_request', 'shutdown_request',
158 'apply_request',
158 'apply_request',
159 ]
159 ]
160 self.shell_handlers = {}
160 self.shell_handlers = {}
161 for msg_type in msg_types:
161 for msg_type in msg_types:
162 self.shell_handlers[msg_type] = getattr(self, msg_type)
162 self.shell_handlers[msg_type] = getattr(self, msg_type)
163
163
164 control_msg_types = [ 'clear_request', 'abort_request' ]
164 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
165 self.control_handlers = {}
165 self.control_handlers = {}
166 for msg_type in control_msg_types:
166 for msg_type in control_msg_types:
167 self.control_handlers[msg_type] = getattr(self, msg_type)
167 self.control_handlers[msg_type] = getattr(self, msg_type)
168
168
169 def dispatch_control(self, msg):
169 def dispatch_control(self, msg):
170 """dispatch control requests"""
170 """dispatch control requests"""
171 idents,msg = self.session.feed_identities(msg, copy=False)
171 idents,msg = self.session.feed_identities(msg, copy=False)
172 try:
172 try:
173 msg = self.session.unserialize(msg, content=True, copy=False)
173 msg = self.session.unserialize(msg, content=True, copy=False)
174 except:
174 except:
175 self.log.error("Invalid Control Message", exc_info=True)
175 self.log.error("Invalid Control Message", exc_info=True)
176 return
176 return
177
177
178 self.log.debug("Control received: %s", msg)
178 self.log.debug("Control received: %s", msg)
179
179
180 header = msg['header']
180 header = msg['header']
181 msg_id = header['msg_id']
181 msg_id = header['msg_id']
182 msg_type = header['msg_type']
182 msg_type = header['msg_type']
183
183
184 handler = self.control_handlers.get(msg_type, None)
184 handler = self.control_handlers.get(msg_type, None)
185 if handler is None:
185 if handler is None:
186 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
186 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
187 else:
187 else:
188 try:
188 try:
189 handler(self.control_stream, idents, msg)
189 handler(self.control_stream, idents, msg)
190 except Exception:
190 except Exception:
191 self.log.error("Exception in control handler:", exc_info=True)
191 self.log.error("Exception in control handler:", exc_info=True)
192
192
193 def dispatch_shell(self, stream, msg):
193 def dispatch_shell(self, stream, msg):
194 """dispatch shell requests"""
194 """dispatch shell requests"""
195 # flush control requests first
195 # flush control requests first
196 if self.control_stream:
196 if self.control_stream:
197 self.control_stream.flush()
197 self.control_stream.flush()
198
198
199 idents,msg = self.session.feed_identities(msg, copy=False)
199 idents,msg = self.session.feed_identities(msg, copy=False)
200 try:
200 try:
201 msg = self.session.unserialize(msg, content=True, copy=False)
201 msg = self.session.unserialize(msg, content=True, copy=False)
202 except:
202 except:
203 self.log.error("Invalid Message", exc_info=True)
203 self.log.error("Invalid Message", exc_info=True)
204 return
204 return
205
205
206 header = msg['header']
206 header = msg['header']
207 msg_id = header['msg_id']
207 msg_id = header['msg_id']
208 msg_type = msg['header']['msg_type']
208 msg_type = msg['header']['msg_type']
209
209
210 # Print some info about this message and leave a '--->' marker, so it's
210 # Print some info about this message and leave a '--->' marker, so it's
211 # easier to trace visually the message chain when debugging. Each
211 # easier to trace visually the message chain when debugging. Each
212 # handler prints its message at the end.
212 # handler prints its message at the end.
213 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
213 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
214 self.log.debug(' Content: %s\n --->\n ', msg['content'])
214 self.log.debug(' Content: %s\n --->\n ', msg['content'])
215
215
216 if msg_id in self.aborted:
216 if msg_id in self.aborted:
217 self.aborted.remove(msg_id)
217 self.aborted.remove(msg_id)
218 # is it safe to assume a msg_id will not be resubmitted?
218 # is it safe to assume a msg_id will not be resubmitted?
219 reply_type = msg_type.split('_')[0] + '_reply'
219 reply_type = msg_type.split('_')[0] + '_reply'
220 status = {'status' : 'aborted'}
220 status = {'status' : 'aborted'}
221 reply_msg = self.session.send(stream, reply_type, subheader=status,
221 reply_msg = self.session.send(stream, reply_type, subheader=status,
222 content=status, parent=msg, ident=idents)
222 content=status, parent=msg, ident=idents)
223 return
223 return
224
224
225 handler = self.shell_handlers.get(msg_type, None)
225 handler = self.shell_handlers.get(msg_type, None)
226 if handler is None:
226 if handler is None:
227 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
227 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
228 else:
228 else:
229 # ensure default_int_handler during handler call
229 # ensure default_int_handler during handler call
230 sig = signal(SIGINT, default_int_handler)
230 sig = signal(SIGINT, default_int_handler)
231 try:
231 try:
232 handler(stream, idents, msg)
232 handler(stream, idents, msg)
233 except Exception:
233 except Exception:
234 self.log.error("Exception in message handler:", exc_info=True)
234 self.log.error("Exception in message handler:", exc_info=True)
235 finally:
235 finally:
236 signal(SIGINT, sig)
236 signal(SIGINT, sig)
237
237
238 def enter_eventloop(self):
238 def enter_eventloop(self):
239 """enter eventloop"""
239 """enter eventloop"""
240 self.log.critical("entering eventloop")
240 self.log.critical("entering eventloop")
241 # restore default_int_handler
241 # restore default_int_handler
242 signal(SIGINT, default_int_handler)
242 signal(SIGINT, default_int_handler)
243 self.eventloop(self)
243 self.eventloop(self)
244 self.log.critical("exiting eventloop")
244 self.log.critical("exiting eventloop")
245 # if eventloop exits, IOLoop should stop
245 # if eventloop exits, IOLoop should stop
246 ioloop.IOLoop.instance().stop()
246 ioloop.IOLoop.instance().stop()
247
247
248 def start(self):
248 def start(self):
249 """register dispatchers for streams"""
249 """register dispatchers for streams"""
250 if self.control_stream:
250 if self.control_stream:
251 self.control_stream.on_recv(self.dispatch_control, copy=False)
251 self.control_stream.on_recv(self.dispatch_control, copy=False)
252
252
253 def make_dispatcher(stream):
253 def make_dispatcher(stream):
254 def dispatcher(msg):
254 def dispatcher(msg):
255 return self.dispatch_shell(stream, msg)
255 return self.dispatch_shell(stream, msg)
256 return dispatcher
256 return dispatcher
257
257
258 for s in self.shell_streams:
258 for s in self.shell_streams:
259 s.on_recv(make_dispatcher(s), copy=False)
259 s.on_recv(make_dispatcher(s), copy=False)
260
260
261 def do_one_iteration(self):
261 def do_one_iteration(self):
262 """step eventloop just once"""
262 """step eventloop just once"""
263 if self.control_stream:
263 if self.control_stream:
264 self.control_stream.flush()
264 self.control_stream.flush()
265 for stream in self.shell_streams:
265 for stream in self.shell_streams:
266 # handle at most one request per iteration
266 # handle at most one request per iteration
267 stream.flush(zmq.POLLIN, 1)
267 stream.flush(zmq.POLLIN, 1)
268 stream.flush(zmq.POLLOUT)
268 stream.flush(zmq.POLLOUT)
269 self.iopub_stream.flush()
269 self.iopub_stream.flush()
270
270
271
271
272 def record_ports(self, ports):
272 def record_ports(self, ports):
273 """Record the ports that this kernel is using.
273 """Record the ports that this kernel is using.
274
274
275 The creator of the Kernel instance must call this methods if they
275 The creator of the Kernel instance must call this methods if they
276 want the :meth:`connect_request` method to return the port numbers.
276 want the :meth:`connect_request` method to return the port numbers.
277 """
277 """
278 self._recorded_ports = ports
278 self._recorded_ports = ports
279
279
280 #---------------------------------------------------------------------------
280 #---------------------------------------------------------------------------
281 # Kernel request handlers
281 # Kernel request handlers
282 #---------------------------------------------------------------------------
282 #---------------------------------------------------------------------------
283
283
284 def _publish_pyin(self, code, parent, execution_count):
284 def _publish_pyin(self, code, parent, execution_count):
285 """Publish the code request on the pyin stream."""
285 """Publish the code request on the pyin stream."""
286
286
287 self.session.send(self.iopub_stream, u'pyin',
287 self.session.send(self.iopub_stream, u'pyin',
288 {u'code':code, u'execution_count': execution_count},
288 {u'code':code, u'execution_count': execution_count},
289 parent=parent, ident=self._topic('pyin')
289 parent=parent, ident=self._topic('pyin')
290 )
290 )
291
291
292 def execute_request(self, stream, ident, parent):
292 def execute_request(self, stream, ident, parent):
293
293
294 self.session.send(self.iopub_stream,
294 self.session.send(self.iopub_stream,
295 u'status',
295 u'status',
296 {u'execution_state':u'busy'},
296 {u'execution_state':u'busy'},
297 parent=parent,
297 parent=parent,
298 ident=self._topic('status'),
298 ident=self._topic('status'),
299 )
299 )
300
300
301 try:
301 try:
302 content = parent[u'content']
302 content = parent[u'content']
303 code = content[u'code']
303 code = content[u'code']
304 silent = content[u'silent']
304 silent = content[u'silent']
305 except:
305 except:
306 self.log.error("Got bad msg: ")
306 self.log.error("Got bad msg: ")
307 self.log.error("%s", parent)
307 self.log.error("%s", parent)
308 return
308 return
309
309
310 shell = self.shell # we'll need this a lot here
310 shell = self.shell # we'll need this a lot here
311
311
312 # Replace raw_input. Note that is not sufficient to replace
312 # Replace raw_input. Note that is not sufficient to replace
313 # raw_input in the user namespace.
313 # raw_input in the user namespace.
314 if content.get('allow_stdin', False):
314 if content.get('allow_stdin', False):
315 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
315 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
316 else:
316 else:
317 raw_input = lambda prompt='' : self._no_raw_input()
317 raw_input = lambda prompt='' : self._no_raw_input()
318
318
319 if py3compat.PY3:
319 if py3compat.PY3:
320 __builtin__.input = raw_input
320 __builtin__.input = raw_input
321 else:
321 else:
322 __builtin__.raw_input = raw_input
322 __builtin__.raw_input = raw_input
323
323
324 # Set the parent message of the display hook and out streams.
324 # Set the parent message of the display hook and out streams.
325 shell.displayhook.set_parent(parent)
325 shell.displayhook.set_parent(parent)
326 shell.display_pub.set_parent(parent)
326 shell.display_pub.set_parent(parent)
327 sys.stdout.set_parent(parent)
327 sys.stdout.set_parent(parent)
328 sys.stderr.set_parent(parent)
328 sys.stderr.set_parent(parent)
329
329
330 # Re-broadcast our input for the benefit of listening clients, and
330 # Re-broadcast our input for the benefit of listening clients, and
331 # start computing output
331 # start computing output
332 if not silent:
332 if not silent:
333 self._publish_pyin(code, parent, shell.execution_count)
333 self._publish_pyin(code, parent, shell.execution_count)
334
334
335 reply_content = {}
335 reply_content = {}
336 try:
336 try:
337 if silent:
337 if silent:
338 # run_code uses 'exec' mode, so no displayhook will fire, and it
338 # run_code uses 'exec' mode, so no displayhook will fire, and it
339 # doesn't call logging or history manipulations. Print
339 # doesn't call logging or history manipulations. Print
340 # statements in that code will obviously still execute.
340 # statements in that code will obviously still execute.
341 shell.run_code(code)
341 shell.run_code(code)
342 else:
342 else:
343 # FIXME: the shell calls the exception handler itself.
343 # FIXME: the shell calls the exception handler itself.
344 shell.run_cell(code, store_history=True)
344 shell.run_cell(code, store_history=True)
345 except:
345 except:
346 status = u'error'
346 status = u'error'
347 # FIXME: this code right now isn't being used yet by default,
347 # FIXME: this code right now isn't being used yet by default,
348 # because the run_cell() call above directly fires off exception
348 # because the run_cell() call above directly fires off exception
349 # reporting. This code, therefore, is only active in the scenario
349 # reporting. This code, therefore, is only active in the scenario
350 # where runlines itself has an unhandled exception. We need to
350 # where runlines itself has an unhandled exception. We need to
351 # uniformize this, for all exception construction to come from a
351 # uniformize this, for all exception construction to come from a
352 # single location in the codbase.
352 # single location in the codbase.
353 etype, evalue, tb = sys.exc_info()
353 etype, evalue, tb = sys.exc_info()
354 tb_list = traceback.format_exception(etype, evalue, tb)
354 tb_list = traceback.format_exception(etype, evalue, tb)
355 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
355 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
356 else:
356 else:
357 status = u'ok'
357 status = u'ok'
358
358
359 reply_content[u'status'] = status
359 reply_content[u'status'] = status
360
360
361 # Return the execution counter so clients can display prompts
361 # Return the execution counter so clients can display prompts
362 reply_content['execution_count'] = shell.execution_count -1
362 reply_content['execution_count'] = shell.execution_count -1
363
363
364 # FIXME - fish exception info out of shell, possibly left there by
364 # FIXME - fish exception info out of shell, possibly left there by
365 # runlines. We'll need to clean up this logic later.
365 # runlines. We'll need to clean up this logic later.
366 if shell._reply_content is not None:
366 if shell._reply_content is not None:
367 reply_content.update(shell._reply_content)
367 reply_content.update(shell._reply_content)
368 # reset after use
368 # reset after use
369 shell._reply_content = None
369 shell._reply_content = None
370
370
371 # At this point, we can tell whether the main code execution succeeded
371 # At this point, we can tell whether the main code execution succeeded
372 # or not. If it did, we proceed to evaluate user_variables/expressions
372 # or not. If it did, we proceed to evaluate user_variables/expressions
373 if reply_content['status'] == 'ok':
373 if reply_content['status'] == 'ok':
374 reply_content[u'user_variables'] = \
374 reply_content[u'user_variables'] = \
375 shell.user_variables(content.get(u'user_variables', []))
375 shell.user_variables(content.get(u'user_variables', []))
376 reply_content[u'user_expressions'] = \
376 reply_content[u'user_expressions'] = \
377 shell.user_expressions(content.get(u'user_expressions', {}))
377 shell.user_expressions(content.get(u'user_expressions', {}))
378 else:
378 else:
379 # If there was an error, don't even try to compute variables or
379 # If there was an error, don't even try to compute variables or
380 # expressions
380 # expressions
381 reply_content[u'user_variables'] = {}
381 reply_content[u'user_variables'] = {}
382 reply_content[u'user_expressions'] = {}
382 reply_content[u'user_expressions'] = {}
383
383
384 # Payloads should be retrieved regardless of outcome, so we can both
384 # Payloads should be retrieved regardless of outcome, so we can both
385 # recover partial output (that could have been generated early in a
385 # recover partial output (that could have been generated early in a
386 # block, before an error) and clear the payload system always.
386 # block, before an error) and clear the payload system always.
387 reply_content[u'payload'] = shell.payload_manager.read_payload()
387 reply_content[u'payload'] = shell.payload_manager.read_payload()
388 # Be agressive about clearing the payload because we don't want
388 # Be agressive about clearing the payload because we don't want
389 # it to sit in memory until the next execute_request comes in.
389 # it to sit in memory until the next execute_request comes in.
390 shell.payload_manager.clear_payload()
390 shell.payload_manager.clear_payload()
391
391
392 # Flush output before sending the reply.
392 # Flush output before sending the reply.
393 sys.stdout.flush()
393 sys.stdout.flush()
394 sys.stderr.flush()
394 sys.stderr.flush()
395 # FIXME: on rare occasions, the flush doesn't seem to make it to the
395 # FIXME: on rare occasions, the flush doesn't seem to make it to the
396 # clients... This seems to mitigate the problem, but we definitely need
396 # clients... This seems to mitigate the problem, but we definitely need
397 # to better understand what's going on.
397 # to better understand what's going on.
398 if self._execute_sleep:
398 if self._execute_sleep:
399 time.sleep(self._execute_sleep)
399 time.sleep(self._execute_sleep)
400
400
401 # Send the reply.
401 # Send the reply.
402 reply_content = json_clean(reply_content)
402 reply_content = json_clean(reply_content)
403 reply_msg = self.session.send(stream, u'execute_reply',
403 reply_msg = self.session.send(stream, u'execute_reply',
404 reply_content, parent, ident=ident)
404 reply_content, parent, ident=ident)
405 self.log.debug("%s", reply_msg)
405 self.log.debug("%s", reply_msg)
406
406
407 if reply_msg['content']['status'] == u'error':
407 if reply_msg['content']['status'] == u'error':
408 # FIXME: queue_abort on error is disabled
408 # FIXME: queue_abort on error is disabled
409 # A policy decision must be made
409 # A policy decision must be made
410 pass
410 pass
411 # self._abort_queues()
411 # self._abort_queues()
412
412
413 self.session.send(self.iopub_stream,
413 self.session.send(self.iopub_stream,
414 u'status',
414 u'status',
415 {u'execution_state':u'idle'},
415 {u'execution_state':u'idle'},
416 parent=parent,
416 parent=parent,
417 ident=self._topic('status'))
417 ident=self._topic('status'))
418
418
419 def complete_request(self, stream, ident, parent):
419 def complete_request(self, stream, ident, parent):
420 txt, matches = self._complete(parent)
420 txt, matches = self._complete(parent)
421 matches = {'matches' : matches,
421 matches = {'matches' : matches,
422 'matched_text' : txt,
422 'matched_text' : txt,
423 'status' : 'ok'}
423 'status' : 'ok'}
424 matches = json_clean(matches)
424 matches = json_clean(matches)
425 completion_msg = self.session.send(stream, 'complete_reply',
425 completion_msg = self.session.send(stream, 'complete_reply',
426 matches, parent, ident)
426 matches, parent, ident)
427 self.log.debug("%s", completion_msg)
427 self.log.debug("%s", completion_msg)
428
428
429 def object_info_request(self, stream, ident, parent):
429 def object_info_request(self, stream, ident, parent):
430 content = parent['content']
430 content = parent['content']
431 object_info = self.shell.object_inspect(content['oname'],
431 object_info = self.shell.object_inspect(content['oname'],
432 detail_level = content.get('detail_level', 0)
432 detail_level = content.get('detail_level', 0)
433 )
433 )
434 # Before we send this object over, we scrub it for JSON usage
434 # Before we send this object over, we scrub it for JSON usage
435 oinfo = json_clean(object_info)
435 oinfo = json_clean(object_info)
436 msg = self.session.send(stream, 'object_info_reply',
436 msg = self.session.send(stream, 'object_info_reply',
437 oinfo, parent, ident)
437 oinfo, parent, ident)
438 self.log.debug("%s", msg)
438 self.log.debug("%s", msg)
439
439
440 def history_request(self, stream, ident, parent):
440 def history_request(self, stream, ident, parent):
441 # We need to pull these out, as passing **kwargs doesn't work with
441 # We need to pull these out, as passing **kwargs doesn't work with
442 # unicode keys before Python 2.6.5.
442 # unicode keys before Python 2.6.5.
443 hist_access_type = parent['content']['hist_access_type']
443 hist_access_type = parent['content']['hist_access_type']
444 raw = parent['content']['raw']
444 raw = parent['content']['raw']
445 output = parent['content']['output']
445 output = parent['content']['output']
446 if hist_access_type == 'tail':
446 if hist_access_type == 'tail':
447 n = parent['content']['n']
447 n = parent['content']['n']
448 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
448 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
449 include_latest=True)
449 include_latest=True)
450
450
451 elif hist_access_type == 'range':
451 elif hist_access_type == 'range':
452 session = parent['content']['session']
452 session = parent['content']['session']
453 start = parent['content']['start']
453 start = parent['content']['start']
454 stop = parent['content']['stop']
454 stop = parent['content']['stop']
455 hist = self.shell.history_manager.get_range(session, start, stop,
455 hist = self.shell.history_manager.get_range(session, start, stop,
456 raw=raw, output=output)
456 raw=raw, output=output)
457
457
458 elif hist_access_type == 'search':
458 elif hist_access_type == 'search':
459 pattern = parent['content']['pattern']
459 pattern = parent['content']['pattern']
460 hist = self.shell.history_manager.search(pattern, raw=raw,
460 hist = self.shell.history_manager.search(pattern, raw=raw,
461 output=output)
461 output=output)
462
462
463 else:
463 else:
464 hist = []
464 hist = []
465 hist = list(hist)
465 hist = list(hist)
466 content = {'history' : hist}
466 content = {'history' : hist}
467 content = json_clean(content)
467 content = json_clean(content)
468 msg = self.session.send(stream, 'history_reply',
468 msg = self.session.send(stream, 'history_reply',
469 content, parent, ident)
469 content, parent, ident)
470 self.log.debug("Sending history reply with %i entries", len(hist))
470 self.log.debug("Sending history reply with %i entries", len(hist))
471
471
472 def connect_request(self, stream, ident, parent):
472 def connect_request(self, stream, ident, parent):
473 if self._recorded_ports is not None:
473 if self._recorded_ports is not None:
474 content = self._recorded_ports.copy()
474 content = self._recorded_ports.copy()
475 else:
475 else:
476 content = {}
476 content = {}
477 msg = self.session.send(stream, 'connect_reply',
477 msg = self.session.send(stream, 'connect_reply',
478 content, parent, ident)
478 content, parent, ident)
479 self.log.debug("%s", msg)
479 self.log.debug("%s", msg)
480
480
481 def shutdown_request(self, stream, ident, parent):
481 def shutdown_request(self, stream, ident, parent):
482 self.shell.exit_now = True
482 self.shell.exit_now = True
483 content = dict(status='ok')
484 content.update(parent['content'])
485 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
486 # same content, but different msg_id for broadcasting on IOPub
483 self._shutdown_message = self.session.msg(u'shutdown_reply',
487 self._shutdown_message = self.session.msg(u'shutdown_reply',
484 parent['content'], parent
488 content, parent
485 )
489 )
486 # self.session.send(stream, self._shutdown_message, ident=ident)
487
490
488 self._at_shutdown()
491 self._at_shutdown()
489 # call sys.exit after a short delay
492 # call sys.exit after a short delay
490 ioloop.IOLoop.instance().add_timeout(time.time()+0.05, lambda : sys.exit(0))
493 ioloop.IOLoop.instance().add_timeout(time.time()+0.1, lambda : sys.exit(0))
491
494
492 #---------------------------------------------------------------------------
495 #---------------------------------------------------------------------------
493 # Engine methods
496 # Engine methods
494 #---------------------------------------------------------------------------
497 #---------------------------------------------------------------------------
495
498
496 def apply_request(self, stream, ident, parent):
499 def apply_request(self, stream, ident, parent):
497 try:
500 try:
498 content = parent[u'content']
501 content = parent[u'content']
499 bufs = parent[u'buffers']
502 bufs = parent[u'buffers']
500 msg_id = parent['header']['msg_id']
503 msg_id = parent['header']['msg_id']
501 except:
504 except:
502 self.log.error("Got bad msg: %s", parent, exc_info=True)
505 self.log.error("Got bad msg: %s", parent, exc_info=True)
503 return
506 return
504 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
507 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
505 # self.iopub_stream.send(pyin_msg)
508 # self.iopub_stream.send(pyin_msg)
506 # self.session.send(self.iopub_stream, u'pyin', {u'code':code},parent=parent)
509 # self.session.send(self.iopub_stream, u'pyin', {u'code':code},parent=parent)
507 sub = {'dependencies_met' : True, 'engine' : self.ident,
510 sub = {'dependencies_met' : True, 'engine' : self.ident,
508 'started': datetime.now()}
511 'started': datetime.now()}
509 try:
512 try:
510 # allow for not overriding displayhook
513 # allow for not overriding displayhook
511 if hasattr(sys.displayhook, 'set_parent'):
514 if hasattr(sys.displayhook, 'set_parent'):
512 sys.displayhook.set_parent(parent)
515 sys.displayhook.set_parent(parent)
513 sys.stdout.set_parent(parent)
516 sys.stdout.set_parent(parent)
514 sys.stderr.set_parent(parent)
517 sys.stderr.set_parent(parent)
515 working = self.shell.user_ns
518 working = self.shell.user_ns
516
519
517 prefix = "_"+str(msg_id).replace("-","")+"_"
520 prefix = "_"+str(msg_id).replace("-","")+"_"
518
521
519 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
522 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
520
523
521 fname = getattr(f, '__name__', 'f')
524 fname = getattr(f, '__name__', 'f')
522
525
523 fname = prefix+"f"
526 fname = prefix+"f"
524 argname = prefix+"args"
527 argname = prefix+"args"
525 kwargname = prefix+"kwargs"
528 kwargname = prefix+"kwargs"
526 resultname = prefix+"result"
529 resultname = prefix+"result"
527
530
528 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
531 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
529 # print ns
532 # print ns
530 working.update(ns)
533 working.update(ns)
531 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
534 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
532 try:
535 try:
533 exec code in self.shell.user_global_ns, self.shell.user_ns
536 exec code in self.shell.user_global_ns, self.shell.user_ns
534 result = working.get(resultname)
537 result = working.get(resultname)
535 finally:
538 finally:
536 for key in ns.iterkeys():
539 for key in ns.iterkeys():
537 working.pop(key)
540 working.pop(key)
538
541
539 packed_result,buf = serialize_object(result)
542 packed_result,buf = serialize_object(result)
540 result_buf = [packed_result]+buf
543 result_buf = [packed_result]+buf
541 except:
544 except:
542 exc_content = self._wrap_exception('apply')
545 exc_content = self._wrap_exception('apply')
543 # exc_msg = self.session.msg(u'pyerr', exc_content, parent)
546 # exc_msg = self.session.msg(u'pyerr', exc_content, parent)
544 self.session.send(self.iopub_stream, u'pyerr', exc_content, parent=parent,
547 self.session.send(self.iopub_stream, u'pyerr', exc_content, parent=parent,
545 ident=self._topic('pyerr'))
548 ident=self._topic('pyerr'))
546 reply_content = exc_content
549 reply_content = exc_content
547 result_buf = []
550 result_buf = []
548
551
549 if exc_content['ename'] == 'UnmetDependency':
552 if exc_content['ename'] == 'UnmetDependency':
550 sub['dependencies_met'] = False
553 sub['dependencies_met'] = False
551 else:
554 else:
552 reply_content = {'status' : 'ok'}
555 reply_content = {'status' : 'ok'}
553
556
554 # put 'ok'/'error' status in header, for scheduler introspection:
557 # put 'ok'/'error' status in header, for scheduler introspection:
555 sub['status'] = reply_content['status']
558 sub['status'] = reply_content['status']
556
559
557 # flush i/o
560 # flush i/o
558 sys.stdout.flush()
561 sys.stdout.flush()
559 sys.stderr.flush()
562 sys.stderr.flush()
560
563
561 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
564 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
562 parent=parent, ident=ident,buffers=result_buf, subheader=sub)
565 parent=parent, ident=ident,buffers=result_buf, subheader=sub)
563
566
564 #---------------------------------------------------------------------------
567 #---------------------------------------------------------------------------
565 # Control messages
568 # Control messages
566 #---------------------------------------------------------------------------
569 #---------------------------------------------------------------------------
567
570
568 def abort_request(self, stream, ident, parent):
571 def abort_request(self, stream, ident, parent):
569 """abort a specifig msg by id"""
572 """abort a specifig msg by id"""
570 msg_ids = parent['content'].get('msg_ids', None)
573 msg_ids = parent['content'].get('msg_ids', None)
571 if isinstance(msg_ids, basestring):
574 if isinstance(msg_ids, basestring):
572 msg_ids = [msg_ids]
575 msg_ids = [msg_ids]
573 if not msg_ids:
576 if not msg_ids:
574 self.abort_queues()
577 self.abort_queues()
575 for mid in msg_ids:
578 for mid in msg_ids:
576 self.aborted.add(str(mid))
579 self.aborted.add(str(mid))
577
580
578 content = dict(status='ok')
581 content = dict(status='ok')
579 reply_msg = self.session.send(stream, 'abort_reply', content=content,
582 reply_msg = self.session.send(stream, 'abort_reply', content=content,
580 parent=parent, ident=ident)
583 parent=parent, ident=ident)
581 self.log.debug("%s", reply_msg)
584 self.log.debug("%s", reply_msg)
582
585
583 def clear_request(self, stream, idents, parent):
586 def clear_request(self, stream, idents, parent):
584 """Clear our namespace."""
587 """Clear our namespace."""
585 self.shell.reset(False)
588 self.shell.reset(False)
586 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
589 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
587 content = dict(status='ok'))
590 content = dict(status='ok'))
588
591
589
592
590 #---------------------------------------------------------------------------
593 #---------------------------------------------------------------------------
591 # Protected interface
594 # Protected interface
592 #---------------------------------------------------------------------------
595 #---------------------------------------------------------------------------
593
596
594
597
595 def _wrap_exception(self, method=None):
598 def _wrap_exception(self, method=None):
596 # import here, because _wrap_exception is only used in parallel,
599 # import here, because _wrap_exception is only used in parallel,
597 # and parallel has higher min pyzmq version
600 # and parallel has higher min pyzmq version
598 from IPython.parallel.error import wrap_exception
601 from IPython.parallel.error import wrap_exception
599 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
602 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
600 content = wrap_exception(e_info)
603 content = wrap_exception(e_info)
601 return content
604 return content
602
605
603 def _topic(self, topic):
606 def _topic(self, topic):
604 """prefixed topic for IOPub messages"""
607 """prefixed topic for IOPub messages"""
605 if self.int_id >= 0:
608 if self.int_id >= 0:
606 base = "engine.%i" % self.int_id
609 base = "engine.%i" % self.int_id
607 else:
610 else:
608 base = "kernel.%s" % self.ident
611 base = "kernel.%s" % self.ident
609
612
610 return py3compat.cast_bytes("%s.%s" % (base, topic))
613 return py3compat.cast_bytes("%s.%s" % (base, topic))
611
614
612 def _abort_queues(self):
615 def _abort_queues(self):
613 for stream in self.shell_streams:
616 for stream in self.shell_streams:
614 if stream:
617 if stream:
615 self._abort_queue(stream)
618 self._abort_queue(stream)
616
619
617 def _abort_queue(self, stream):
620 def _abort_queue(self, stream):
621 poller = zmq.Poller()
622 poller.register(stream.socket, zmq.POLLIN)
618 while True:
623 while True:
619 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
624 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
620 if msg is None:
625 if msg is None:
621 return
626 return
622
627
623 self.log.info("Aborting:")
628 self.log.info("Aborting:")
624 self.log.info("%s", msg)
629 self.log.info("%s", msg)
625 msg_type = msg['header']['msg_type']
630 msg_type = msg['header']['msg_type']
626 reply_type = msg_type.split('_')[0] + '_reply'
631 reply_type = msg_type.split('_')[0] + '_reply'
627 # reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
632 # reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
628 # self.reply_stream.send(ident,zmq.SNDMORE)
633 # self.reply_stream.send(ident,zmq.SNDMORE)
629 # self.reply_stream.send_json(reply_msg)
634 # self.reply_stream.send_json(reply_msg)
630 reply_msg = self.session.send(stream, reply_type,
635 reply_msg = self.session.send(stream, reply_type,
631 content={'status' : 'aborted'}, parent=msg, ident=idents)
636 content={'status' : 'aborted'}, parent=msg, ident=idents)
632 self.log.debug("%s", reply_msg)
637 self.log.debug("%s", reply_msg)
633 # We need to wait a bit for requests to come in. This can probably
638 # We need to wait a bit for requests to come in. This can probably
634 # be set shorter for true asynchronous clients.
639 # be set shorter for true asynchronous clients.
635 time.sleep(0.05)
640 poller.poll(50)
636
641
637
642
638 def _no_raw_input(self):
643 def _no_raw_input(self):
639 """Raise StdinNotImplentedError if active frontend doesn't support
644 """Raise StdinNotImplentedError if active frontend doesn't support
640 stdin."""
645 stdin."""
641 raise StdinNotImplementedError("raw_input was called, but this "
646 raise StdinNotImplementedError("raw_input was called, but this "
642 "frontend does not support stdin.")
647 "frontend does not support stdin.")
643
648
644 def _raw_input(self, prompt, ident, parent):
649 def _raw_input(self, prompt, ident, parent):
645 # Flush output before making the request.
650 # Flush output before making the request.
646 sys.stderr.flush()
651 sys.stderr.flush()
647 sys.stdout.flush()
652 sys.stdout.flush()
648
653
649 # Send the input request.
654 # Send the input request.
650 content = json_clean(dict(prompt=prompt))
655 content = json_clean(dict(prompt=prompt))
651 self.session.send(self.stdin_socket, u'input_request', content, parent,
656 self.session.send(self.stdin_socket, u'input_request', content, parent,
652 ident=ident)
657 ident=ident)
653
658
654 # Await a response.
659 # Await a response.
655 while True:
660 while True:
656 try:
661 try:
657 ident, reply = self.session.recv(self.stdin_socket, 0)
662 ident, reply = self.session.recv(self.stdin_socket, 0)
658 except Exception:
663 except Exception:
659 self.log.warn("Invalid Message:", exc_info=True)
664 self.log.warn("Invalid Message:", exc_info=True)
660 else:
665 else:
661 break
666 break
662 try:
667 try:
663 value = reply['content']['value']
668 value = reply['content']['value']
664 except:
669 except:
665 self.log.error("Got bad raw_input reply: ")
670 self.log.error("Got bad raw_input reply: ")
666 self.log.error("%s", parent)
671 self.log.error("%s", parent)
667 value = ''
672 value = ''
668 if value == '\x04':
673 if value == '\x04':
669 # EOF
674 # EOF
670 raise EOFError
675 raise EOFError
671 return value
676 return value
672
677
673 def _complete(self, msg):
678 def _complete(self, msg):
674 c = msg['content']
679 c = msg['content']
675 try:
680 try:
676 cpos = int(c['cursor_pos'])
681 cpos = int(c['cursor_pos'])
677 except:
682 except:
678 # If we don't get something that we can convert to an integer, at
683 # If we don't get something that we can convert to an integer, at
679 # least attempt the completion guessing the cursor is at the end of
684 # least attempt the completion guessing the cursor is at the end of
680 # the text, if there's any, and otherwise of the line
685 # the text, if there's any, and otherwise of the line
681 cpos = len(c['text'])
686 cpos = len(c['text'])
682 if cpos==0:
687 if cpos==0:
683 cpos = len(c['line'])
688 cpos = len(c['line'])
684 return self.shell.complete(c['text'], c['line'], cpos)
689 return self.shell.complete(c['text'], c['line'], cpos)
685
690
686 def _object_info(self, context):
691 def _object_info(self, context):
687 symbol, leftover = self._symbol_from_context(context)
692 symbol, leftover = self._symbol_from_context(context)
688 if symbol is not None and not leftover:
693 if symbol is not None and not leftover:
689 doc = getattr(symbol, '__doc__', '')
694 doc = getattr(symbol, '__doc__', '')
690 else:
695 else:
691 doc = ''
696 doc = ''
692 object_info = dict(docstring = doc)
697 object_info = dict(docstring = doc)
693 return object_info
698 return object_info
694
699
695 def _symbol_from_context(self, context):
700 def _symbol_from_context(self, context):
696 if not context:
701 if not context:
697 return None, context
702 return None, context
698
703
699 base_symbol_string = context[0]
704 base_symbol_string = context[0]
700 symbol = self.shell.user_ns.get(base_symbol_string, None)
705 symbol = self.shell.user_ns.get(base_symbol_string, None)
701 if symbol is None:
706 if symbol is None:
702 symbol = __builtin__.__dict__.get(base_symbol_string, None)
707 symbol = __builtin__.__dict__.get(base_symbol_string, None)
703 if symbol is None:
708 if symbol is None:
704 return None, context
709 return None, context
705
710
706 context = context[1:]
711 context = context[1:]
707 for i, name in enumerate(context):
712 for i, name in enumerate(context):
708 new_symbol = getattr(symbol, name, None)
713 new_symbol = getattr(symbol, name, None)
709 if new_symbol is None:
714 if new_symbol is None:
710 return symbol, context[i:]
715 return symbol, context[i:]
711 else:
716 else:
712 symbol = new_symbol
717 symbol = new_symbol
713
718
714 return symbol, []
719 return symbol, []
715
720
716 def _at_shutdown(self):
721 def _at_shutdown(self):
717 """Actions taken at shutdown by the kernel, called by python's atexit.
722 """Actions taken at shutdown by the kernel, called by python's atexit.
718 """
723 """
719 # io.rprint("Kernel at_shutdown") # dbg
724 # io.rprint("Kernel at_shutdown") # dbg
720 if self._shutdown_message is not None:
725 if self._shutdown_message is not None:
721 self.session.send(self.iopub_stream, self._shutdown_message, ident=self._topic('shutdown'))
726 self.session.send(self.iopub_stream, self._shutdown_message, ident=self._topic('shutdown'))
722 self.log.debug("%s", self._shutdown_message)
727 self.log.debug("%s", self._shutdown_message)
723 [ s.flush(zmq.POLLOUT) for s in self.shell_streams + [self.iopub_stream] ]
728 [ s.flush(zmq.POLLOUT) for s in self.shell_streams + [self.iopub_stream] ]
724
729
725 #-----------------------------------------------------------------------------
730 #-----------------------------------------------------------------------------
726 # Aliases and Flags for the IPKernelApp
731 # Aliases and Flags for the IPKernelApp
727 #-----------------------------------------------------------------------------
732 #-----------------------------------------------------------------------------
728
733
729 flags = dict(kernel_flags)
734 flags = dict(kernel_flags)
730 flags.update(shell_flags)
735 flags.update(shell_flags)
731
736
732 addflag = lambda *args: flags.update(boolean_flag(*args))
737 addflag = lambda *args: flags.update(boolean_flag(*args))
733
738
734 flags['pylab'] = (
739 flags['pylab'] = (
735 {'IPKernelApp' : {'pylab' : 'auto'}},
740 {'IPKernelApp' : {'pylab' : 'auto'}},
736 """Pre-load matplotlib and numpy for interactive use with
741 """Pre-load matplotlib and numpy for interactive use with
737 the default matplotlib backend."""
742 the default matplotlib backend."""
738 )
743 )
739
744
740 aliases = dict(kernel_aliases)
745 aliases = dict(kernel_aliases)
741 aliases.update(shell_aliases)
746 aliases.update(shell_aliases)
742
747
743 # it's possible we don't want short aliases for *all* of these:
748 # it's possible we don't want short aliases for *all* of these:
744 aliases.update(dict(
749 aliases.update(dict(
745 pylab='IPKernelApp.pylab',
750 pylab='IPKernelApp.pylab',
746 ))
751 ))
747
752
748 #-----------------------------------------------------------------------------
753 #-----------------------------------------------------------------------------
749 # The IPKernelApp class
754 # The IPKernelApp class
750 #-----------------------------------------------------------------------------
755 #-----------------------------------------------------------------------------
751
756
752 class IPKernelApp(KernelApp, InteractiveShellApp):
757 class IPKernelApp(KernelApp, InteractiveShellApp):
753 name = 'ipkernel'
758 name = 'ipkernel'
754
759
755 aliases = Dict(aliases)
760 aliases = Dict(aliases)
756 flags = Dict(flags)
761 flags = Dict(flags)
757 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
762 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
758
763
759 # configurables
764 # configurables
760 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
765 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
761 config=True,
766 config=True,
762 help="""Pre-load matplotlib and numpy for interactive use,
767 help="""Pre-load matplotlib and numpy for interactive use,
763 selecting a particular matplotlib backend and loop integration.
768 selecting a particular matplotlib backend and loop integration.
764 """
769 """
765 )
770 )
766
771
767 @catch_config_error
772 @catch_config_error
768 def initialize(self, argv=None):
773 def initialize(self, argv=None):
769 super(IPKernelApp, self).initialize(argv)
774 super(IPKernelApp, self).initialize(argv)
770 self.init_path()
775 self.init_path()
771 self.init_shell()
776 self.init_shell()
772 self.init_extensions()
777 self.init_extensions()
773 self.init_code()
778 self.init_code()
774
779
775 def init_kernel(self):
780 def init_kernel(self):
776
781
777 shell_stream = ZMQStream(self.shell_socket)
782 shell_stream = ZMQStream(self.shell_socket)
778 iopub_stream = ZMQStream(self.iopub_socket)
783 iopub_stream = ZMQStream(self.iopub_socket)
779
784
780 kernel = Kernel(config=self.config, session=self.session,
785 kernel = Kernel(config=self.config, session=self.session,
781 shell_streams=[shell_stream],
786 shell_streams=[shell_stream],
782 iopub_stream=iopub_stream,
787 iopub_stream=iopub_stream,
783 stdin_socket=self.stdin_socket,
788 stdin_socket=self.stdin_socket,
784 log=self.log,
789 log=self.log,
785 profile_dir=self.profile_dir,
790 profile_dir=self.profile_dir,
786 )
791 )
787 self.kernel = kernel
792 self.kernel = kernel
788 kernel.record_ports(self.ports)
793 kernel.record_ports(self.ports)
789 shell = kernel.shell
794 shell = kernel.shell
790 if self.pylab:
795 if self.pylab:
791 try:
796 try:
792 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
797 gui, backend = pylabtools.find_gui_and_backend(self.pylab)
793 shell.enable_pylab(gui, import_all=self.pylab_import_all)
798 shell.enable_pylab(gui, import_all=self.pylab_import_all)
794 except Exception:
799 except Exception:
795 self.log.error("Pylab initialization failed", exc_info=True)
800 self.log.error("Pylab initialization failed", exc_info=True)
796 # print exception straight to stdout, because normally
801 # print exception straight to stdout, because normally
797 # _showtraceback associates the reply with an execution,
802 # _showtraceback associates the reply with an execution,
798 # which means frontends will never draw it, as this exception
803 # which means frontends will never draw it, as this exception
799 # is not associated with any execute request.
804 # is not associated with any execute request.
800
805
801 # replace pyerr-sending traceback with stdout
806 # replace pyerr-sending traceback with stdout
802 _showtraceback = shell._showtraceback
807 _showtraceback = shell._showtraceback
803 def print_tb(etype, evalue, stb):
808 def print_tb(etype, evalue, stb):
804 print ("Error initializing pylab, pylab mode will not "
809 print ("Error initializing pylab, pylab mode will not "
805 "be active", file=io.stderr)
810 "be active", file=io.stderr)
806 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
811 print (shell.InteractiveTB.stb2text(stb), file=io.stdout)
807 shell._showtraceback = print_tb
812 shell._showtraceback = print_tb
808
813
809 # send the traceback over stdout
814 # send the traceback over stdout
810 shell.showtraceback(tb_offset=0)
815 shell.showtraceback(tb_offset=0)
811
816
812 # restore proper _showtraceback method
817 # restore proper _showtraceback method
813 shell._showtraceback = _showtraceback
818 shell._showtraceback = _showtraceback
814
819
815
820
816 def init_shell(self):
821 def init_shell(self):
817 self.shell = self.kernel.shell
822 self.shell = self.kernel.shell
818 self.shell.configurables.append(self)
823 self.shell.configurables.append(self)
819
824
820
825
821 #-----------------------------------------------------------------------------
826 #-----------------------------------------------------------------------------
822 # Kernel main and launch functions
827 # Kernel main and launch functions
823 #-----------------------------------------------------------------------------
828 #-----------------------------------------------------------------------------
824
829
825 def launch_kernel(*args, **kwargs):
830 def launch_kernel(*args, **kwargs):
826 """Launches a localhost IPython kernel, binding to the specified ports.
831 """Launches a localhost IPython kernel, binding to the specified ports.
827
832
828 This function simply calls entry_point.base_launch_kernel with the right
833 This function simply calls entry_point.base_launch_kernel with the right
829 first command to start an ipkernel. See base_launch_kernel for arguments.
834 first command to start an ipkernel. See base_launch_kernel for arguments.
830
835
831 Returns
836 Returns
832 -------
837 -------
833 A tuple of form:
838 A tuple of form:
834 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
839 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
835 where kernel_process is a Popen object and the ports are integers.
840 where kernel_process is a Popen object and the ports are integers.
836 """
841 """
837 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
842 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
838 *args, **kwargs)
843 *args, **kwargs)
839
844
840
845
841 def embed_kernel(module=None, local_ns=None, **kwargs):
846 def embed_kernel(module=None, local_ns=None, **kwargs):
842 """Embed and start an IPython kernel in a given scope.
847 """Embed and start an IPython kernel in a given scope.
843
848
844 Parameters
849 Parameters
845 ----------
850 ----------
846 module : ModuleType, optional
851 module : ModuleType, optional
847 The module to load into IPython globals (default: caller)
852 The module to load into IPython globals (default: caller)
848 local_ns : dict, optional
853 local_ns : dict, optional
849 The namespace to load into IPython user namespace (default: caller)
854 The namespace to load into IPython user namespace (default: caller)
850
855
851 kwargs : various, optional
856 kwargs : various, optional
852 Further keyword args are relayed to the KernelApp constructor,
857 Further keyword args are relayed to the KernelApp constructor,
853 allowing configuration of the Kernel. Will only have an effect
858 allowing configuration of the Kernel. Will only have an effect
854 on the first embed_kernel call for a given process.
859 on the first embed_kernel call for a given process.
855
860
856 """
861 """
857 # get the app if it exists, or set it up if it doesn't
862 # get the app if it exists, or set it up if it doesn't
858 if IPKernelApp.initialized():
863 if IPKernelApp.initialized():
859 app = IPKernelApp.instance()
864 app = IPKernelApp.instance()
860 else:
865 else:
861 app = IPKernelApp.instance(**kwargs)
866 app = IPKernelApp.instance(**kwargs)
862 app.initialize([])
867 app.initialize([])
863
868
864 # load the calling scope if not given
869 # load the calling scope if not given
865 (caller_module, caller_locals) = extract_module_locals(1)
870 (caller_module, caller_locals) = extract_module_locals(1)
866 if module is None:
871 if module is None:
867 module = caller_module
872 module = caller_module
868 if local_ns is None:
873 if local_ns is None:
869 local_ns = caller_locals
874 local_ns = caller_locals
870
875
871 app.kernel.user_module = module
876 app.kernel.user_module = module
872 app.kernel.user_ns = local_ns
877 app.kernel.user_ns = local_ns
873 app.start()
878 app.start()
874
879
875 def main():
880 def main():
876 """Run an IPKernel as an application"""
881 """Run an IPKernel as an application"""
877 app = IPKernelApp.instance()
882 app = IPKernelApp.instance()
878 app.initialize()
883 app.initialize()
879 app.start()
884 app.start()
880
885
881
886
882 if __name__ == '__main__':
887 if __name__ == '__main__':
883 main()
888 main()
@@ -1,525 +1,536 b''
1 """A ZMQ-based subclass of InteractiveShell.
1 """A ZMQ-based subclass of InteractiveShell.
2
2
3 This code is meant to ease the refactoring of the base InteractiveShell into
3 This code is meant to ease the refactoring of the base InteractiveShell into
4 something with a cleaner architecture for 2-process use, without actually
4 something with a cleaner architecture for 2-process use, without actually
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 we subclass and override what we want to fix. Once this is working well, we
6 we subclass and override what we want to fix. Once this is working well, we
7 can go back to the base class and refactor the code for a cleaner inheritance
7 can go back to the base class and refactor the code for a cleaner inheritance
8 implementation that doesn't rely on so much monkeypatching.
8 implementation that doesn't rely on so much monkeypatching.
9
9
10 But this lets us maintain a fully working IPython as we develop the new
10 But this lets us maintain a fully working IPython as we develop the new
11 machinery. This should thus be thought of as scaffolding.
11 machinery. This should thus be thought of as scaffolding.
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 # Stdlib
18 # Stdlib
19 import inspect
19 import inspect
20 import os
20 import os
21 import sys
21 import sys
22 import time
22 from subprocess import Popen, PIPE
23 from subprocess import Popen, PIPE
23
24
25 # System library imports
26 from zmq.eventloop import ioloop
27
24 # Our own
28 # Our own
25 from IPython.core.interactiveshell import (
29 from IPython.core.interactiveshell import (
26 InteractiveShell, InteractiveShellABC
30 InteractiveShell, InteractiveShellABC
27 )
31 )
28 from IPython.core import page, pylabtools
32 from IPython.core import page, pylabtools
29 from IPython.core.autocall import ZMQExitAutocall
33 from IPython.core.autocall import ZMQExitAutocall
30 from IPython.core.displaypub import DisplayPublisher
34 from IPython.core.displaypub import DisplayPublisher
31 from IPython.core.macro import Macro
35 from IPython.core.macro import Macro
32 from IPython.core.magic import MacroToEdit
36 from IPython.core.magic import MacroToEdit
33 from IPython.core.payloadpage import install_payload_page
37 from IPython.core.payloadpage import install_payload_page
34 from IPython.lib.kernel import (
38 from IPython.lib.kernel import (
35 get_connection_file, get_connection_info, connect_qtconsole
39 get_connection_file, get_connection_info, connect_qtconsole
36 )
40 )
37 from IPython.testing.skipdoctest import skip_doctest
41 from IPython.testing.skipdoctest import skip_doctest
38 from IPython.utils import io
42 from IPython.utils import io
39 from IPython.utils.jsonutil import json_clean
43 from IPython.utils.jsonutil import json_clean
40 from IPython.utils.path import get_py_filename
44 from IPython.utils.path import get_py_filename
41 from IPython.utils.process import arg_split
45 from IPython.utils.process import arg_split
42 from IPython.utils.traitlets import Instance, Type, Dict, CBool
46 from IPython.utils.traitlets import Instance, Type, Dict, CBool
43 from IPython.utils.warn import warn, error
47 from IPython.utils.warn import warn, error
44 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
48 from IPython.zmq.displayhook import ZMQShellDisplayHook, _encode_binary
45 from IPython.zmq.session import extract_header
49 from IPython.zmq.session import extract_header
46 from session import Session
50 from session import Session
47
51
48
52
49 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
50 # Functions and classes
54 # Functions and classes
51 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
52
56
53 class ZMQDisplayPublisher(DisplayPublisher):
57 class ZMQDisplayPublisher(DisplayPublisher):
54 """A display publisher that publishes data using a ZeroMQ PUB socket."""
58 """A display publisher that publishes data using a ZeroMQ PUB socket."""
55
59
56 session = Instance(Session)
60 session = Instance(Session)
57 pub_socket = Instance('zmq.Socket')
61 pub_socket = Instance('zmq.Socket')
58 parent_header = Dict({})
62 parent_header = Dict({})
59
63
60 def set_parent(self, parent):
64 def set_parent(self, parent):
61 """Set the parent for outbound messages."""
65 """Set the parent for outbound messages."""
62 self.parent_header = extract_header(parent)
66 self.parent_header = extract_header(parent)
63
67
64 def _flush_streams(self):
68 def _flush_streams(self):
65 """flush IO Streams prior to display"""
69 """flush IO Streams prior to display"""
66 sys.stdout.flush()
70 sys.stdout.flush()
67 sys.stderr.flush()
71 sys.stderr.flush()
68
72
69 def publish(self, source, data, metadata=None):
73 def publish(self, source, data, metadata=None):
70 self._flush_streams()
74 self._flush_streams()
71 if metadata is None:
75 if metadata is None:
72 metadata = {}
76 metadata = {}
73 self._validate_data(source, data, metadata)
77 self._validate_data(source, data, metadata)
74 content = {}
78 content = {}
75 content['source'] = source
79 content['source'] = source
76 _encode_binary(data)
80 _encode_binary(data)
77 content['data'] = data
81 content['data'] = data
78 content['metadata'] = metadata
82 content['metadata'] = metadata
79 self.session.send(
83 self.session.send(
80 self.pub_socket, u'display_data', json_clean(content),
84 self.pub_socket, u'display_data', json_clean(content),
81 parent=self.parent_header
85 parent=self.parent_header
82 )
86 )
83
87
84 def clear_output(self, stdout=True, stderr=True, other=True):
88 def clear_output(self, stdout=True, stderr=True, other=True):
85 content = dict(stdout=stdout, stderr=stderr, other=other)
89 content = dict(stdout=stdout, stderr=stderr, other=other)
86
90
87 if stdout:
91 if stdout:
88 print('\r', file=sys.stdout, end='')
92 print('\r', file=sys.stdout, end='')
89 if stderr:
93 if stderr:
90 print('\r', file=sys.stderr, end='')
94 print('\r', file=sys.stderr, end='')
91
95
92 self._flush_streams()
96 self._flush_streams()
93
97
94 self.session.send(
98 self.session.send(
95 self.pub_socket, u'clear_output', content,
99 self.pub_socket, u'clear_output', content,
96 parent=self.parent_header
100 parent=self.parent_header
97 )
101 )
98
102
99 class ZMQInteractiveShell(InteractiveShell):
103 class ZMQInteractiveShell(InteractiveShell):
100 """A subclass of InteractiveShell for ZMQ."""
104 """A subclass of InteractiveShell for ZMQ."""
101
105
102 displayhook_class = Type(ZMQShellDisplayHook)
106 displayhook_class = Type(ZMQShellDisplayHook)
103 display_pub_class = Type(ZMQDisplayPublisher)
107 display_pub_class = Type(ZMQDisplayPublisher)
104
108
105 # Override the traitlet in the parent class, because there's no point using
109 # Override the traitlet in the parent class, because there's no point using
106 # readline for the kernel. Can be removed when the readline code is moved
110 # readline for the kernel. Can be removed when the readline code is moved
107 # to the terminal frontend.
111 # to the terminal frontend.
108 colors_force = CBool(True)
112 colors_force = CBool(True)
109 readline_use = CBool(False)
113 readline_use = CBool(False)
110 # autoindent has no meaning in a zmqshell, and attempting to enable it
114 # autoindent has no meaning in a zmqshell, and attempting to enable it
111 # will print a warning in the absence of readline.
115 # will print a warning in the absence of readline.
112 autoindent = CBool(False)
116 autoindent = CBool(False)
113
117
114 exiter = Instance(ZMQExitAutocall)
118 exiter = Instance(ZMQExitAutocall)
115 def _exiter_default(self):
119 def _exiter_default(self):
116 return ZMQExitAutocall(self)
120 return ZMQExitAutocall(self)
121
122 def _exit_now_changed(self, name, old, new):
123 """stop eventloop when exit_now fires"""
124 if new:
125 loop = ioloop.IOLoop.instance()
126 loop.add_timeout(time.time()+0.1, loop.stop)
117
127
118 keepkernel_on_exit = None
128 keepkernel_on_exit = None
119
129
120 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
130 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
121 # interactive input being read; we provide event loop support in ipkernel
131 # interactive input being read; we provide event loop support in ipkernel
122 from .eventloops import enable_gui
132 from .eventloops import enable_gui
123 enable_gui = staticmethod(enable_gui)
133 enable_gui = staticmethod(enable_gui)
124
134
125 def init_environment(self):
135 def init_environment(self):
126 """Configure the user's environment.
136 """Configure the user's environment.
127
137
128 """
138 """
129 env = os.environ
139 env = os.environ
130 # These two ensure 'ls' produces nice coloring on BSD-derived systems
140 # These two ensure 'ls' produces nice coloring on BSD-derived systems
131 env['TERM'] = 'xterm-color'
141 env['TERM'] = 'xterm-color'
132 env['CLICOLOR'] = '1'
142 env['CLICOLOR'] = '1'
133 # Since normal pagers don't work at all (over pexpect we don't have
143 # Since normal pagers don't work at all (over pexpect we don't have
134 # single-key control of the subprocess), try to disable paging in
144 # single-key control of the subprocess), try to disable paging in
135 # subprocesses as much as possible.
145 # subprocesses as much as possible.
136 env['PAGER'] = 'cat'
146 env['PAGER'] = 'cat'
137 env['GIT_PAGER'] = 'cat'
147 env['GIT_PAGER'] = 'cat'
138
148
139 # And install the payload version of page.
149 # And install the payload version of page.
140 install_payload_page()
150 install_payload_page()
141
151
142 def auto_rewrite_input(self, cmd):
152 def auto_rewrite_input(self, cmd):
143 """Called to show the auto-rewritten input for autocall and friends.
153 """Called to show the auto-rewritten input for autocall and friends.
144
154
145 FIXME: this payload is currently not correctly processed by the
155 FIXME: this payload is currently not correctly processed by the
146 frontend.
156 frontend.
147 """
157 """
148 new = self.prompt_manager.render('rewrite') + cmd
158 new = self.prompt_manager.render('rewrite') + cmd
149 payload = dict(
159 payload = dict(
150 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
160 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
151 transformed_input=new,
161 transformed_input=new,
152 )
162 )
153 self.payload_manager.write_payload(payload)
163 self.payload_manager.write_payload(payload)
154
164
155 def ask_exit(self):
165 def ask_exit(self):
156 """Engage the exit actions."""
166 """Engage the exit actions."""
167 self.exit_now = True
157 payload = dict(
168 payload = dict(
158 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
169 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
159 exit=True,
170 exit=True,
160 keepkernel=self.keepkernel_on_exit,
171 keepkernel=self.keepkernel_on_exit,
161 )
172 )
162 self.payload_manager.write_payload(payload)
173 self.payload_manager.write_payload(payload)
163
174
164 def _showtraceback(self, etype, evalue, stb):
175 def _showtraceback(self, etype, evalue, stb):
165
176
166 exc_content = {
177 exc_content = {
167 u'traceback' : stb,
178 u'traceback' : stb,
168 u'ename' : unicode(etype.__name__),
179 u'ename' : unicode(etype.__name__),
169 u'evalue' : unicode(evalue)
180 u'evalue' : unicode(evalue)
170 }
181 }
171
182
172 dh = self.displayhook
183 dh = self.displayhook
173 # Send exception info over pub socket for other clients than the caller
184 # Send exception info over pub socket for other clients than the caller
174 # to pick up
185 # to pick up
175 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
186 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header)
176
187
177 # FIXME - Hack: store exception info in shell object. Right now, the
188 # FIXME - Hack: store exception info in shell object. Right now, the
178 # caller is reading this info after the fact, we need to fix this logic
189 # caller is reading this info after the fact, we need to fix this logic
179 # to remove this hack. Even uglier, we need to store the error status
190 # to remove this hack. Even uglier, we need to store the error status
180 # here, because in the main loop, the logic that sets it is being
191 # here, because in the main loop, the logic that sets it is being
181 # skipped because runlines swallows the exceptions.
192 # skipped because runlines swallows the exceptions.
182 exc_content[u'status'] = u'error'
193 exc_content[u'status'] = u'error'
183 self._reply_content = exc_content
194 self._reply_content = exc_content
184 # /FIXME
195 # /FIXME
185
196
186 return exc_content
197 return exc_content
187
198
188 #------------------------------------------------------------------------
199 #------------------------------------------------------------------------
189 # Magic overrides
200 # Magic overrides
190 #------------------------------------------------------------------------
201 #------------------------------------------------------------------------
191 # Once the base class stops inheriting from magic, this code needs to be
202 # Once the base class stops inheriting from magic, this code needs to be
192 # moved into a separate machinery as well. For now, at least isolate here
203 # moved into a separate machinery as well. For now, at least isolate here
193 # the magics which this class needs to implement differently from the base
204 # the magics which this class needs to implement differently from the base
194 # class, or that are unique to it.
205 # class, or that are unique to it.
195
206
196 def magic_doctest_mode(self,parameter_s=''):
207 def magic_doctest_mode(self,parameter_s=''):
197 """Toggle doctest mode on and off.
208 """Toggle doctest mode on and off.
198
209
199 This mode is intended to make IPython behave as much as possible like a
210 This mode is intended to make IPython behave as much as possible like a
200 plain Python shell, from the perspective of how its prompts, exceptions
211 plain Python shell, from the perspective of how its prompts, exceptions
201 and output look. This makes it easy to copy and paste parts of a
212 and output look. This makes it easy to copy and paste parts of a
202 session into doctests. It does so by:
213 session into doctests. It does so by:
203
214
204 - Changing the prompts to the classic ``>>>`` ones.
215 - Changing the prompts to the classic ``>>>`` ones.
205 - Changing the exception reporting mode to 'Plain'.
216 - Changing the exception reporting mode to 'Plain'.
206 - Disabling pretty-printing of output.
217 - Disabling pretty-printing of output.
207
218
208 Note that IPython also supports the pasting of code snippets that have
219 Note that IPython also supports the pasting of code snippets that have
209 leading '>>>' and '...' prompts in them. This means that you can paste
220 leading '>>>' and '...' prompts in them. This means that you can paste
210 doctests from files or docstrings (even if they have leading
221 doctests from files or docstrings (even if they have leading
211 whitespace), and the code will execute correctly. You can then use
222 whitespace), and the code will execute correctly. You can then use
212 '%history -t' to see the translated history; this will give you the
223 '%history -t' to see the translated history; this will give you the
213 input after removal of all the leading prompts and whitespace, which
224 input after removal of all the leading prompts and whitespace, which
214 can be pasted back into an editor.
225 can be pasted back into an editor.
215
226
216 With these features, you can switch into this mode easily whenever you
227 With these features, you can switch into this mode easily whenever you
217 need to do testing and changes to doctests, without having to leave
228 need to do testing and changes to doctests, without having to leave
218 your existing IPython session.
229 your existing IPython session.
219 """
230 """
220
231
221 from IPython.utils.ipstruct import Struct
232 from IPython.utils.ipstruct import Struct
222
233
223 # Shorthands
234 # Shorthands
224 shell = self.shell
235 shell = self.shell
225 disp_formatter = self.shell.display_formatter
236 disp_formatter = self.shell.display_formatter
226 ptformatter = disp_formatter.formatters['text/plain']
237 ptformatter = disp_formatter.formatters['text/plain']
227 # dstore is a data store kept in the instance metadata bag to track any
238 # dstore is a data store kept in the instance metadata bag to track any
228 # changes we make, so we can undo them later.
239 # changes we make, so we can undo them later.
229 dstore = shell.meta.setdefault('doctest_mode', Struct())
240 dstore = shell.meta.setdefault('doctest_mode', Struct())
230 save_dstore = dstore.setdefault
241 save_dstore = dstore.setdefault
231
242
232 # save a few values we'll need to recover later
243 # save a few values we'll need to recover later
233 mode = save_dstore('mode', False)
244 mode = save_dstore('mode', False)
234 save_dstore('rc_pprint', ptformatter.pprint)
245 save_dstore('rc_pprint', ptformatter.pprint)
235 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
246 save_dstore('rc_plain_text_only',disp_formatter.plain_text_only)
236 save_dstore('xmode', shell.InteractiveTB.mode)
247 save_dstore('xmode', shell.InteractiveTB.mode)
237
248
238 if mode == False:
249 if mode == False:
239 # turn on
250 # turn on
240 ptformatter.pprint = False
251 ptformatter.pprint = False
241 disp_formatter.plain_text_only = True
252 disp_formatter.plain_text_only = True
242 shell.magic_xmode('Plain')
253 shell.magic_xmode('Plain')
243 else:
254 else:
244 # turn off
255 # turn off
245 ptformatter.pprint = dstore.rc_pprint
256 ptformatter.pprint = dstore.rc_pprint
246 disp_formatter.plain_text_only = dstore.rc_plain_text_only
257 disp_formatter.plain_text_only = dstore.rc_plain_text_only
247 shell.magic_xmode(dstore.xmode)
258 shell.magic_xmode(dstore.xmode)
248
259
249 # Store new mode and inform on console
260 # Store new mode and inform on console
250 dstore.mode = bool(1-int(mode))
261 dstore.mode = bool(1-int(mode))
251 mode_label = ['OFF','ON'][dstore.mode]
262 mode_label = ['OFF','ON'][dstore.mode]
252 print('Doctest mode is:', mode_label)
263 print('Doctest mode is:', mode_label)
253
264
254 # Send the payload back so that clients can modify their prompt display
265 # Send the payload back so that clients can modify their prompt display
255 payload = dict(
266 payload = dict(
256 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
267 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
257 mode=dstore.mode)
268 mode=dstore.mode)
258 self.payload_manager.write_payload(payload)
269 self.payload_manager.write_payload(payload)
259
270
260 @skip_doctest
271 @skip_doctest
261 def magic_edit(self,parameter_s='',last_call=['','']):
272 def magic_edit(self,parameter_s='',last_call=['','']):
262 """Bring up an editor and execute the resulting code.
273 """Bring up an editor and execute the resulting code.
263
274
264 Usage:
275 Usage:
265 %edit [options] [args]
276 %edit [options] [args]
266
277
267 %edit runs an external text editor. You will need to set the command for
278 %edit runs an external text editor. You will need to set the command for
268 this editor via the ``TerminalInteractiveShell.editor`` option in your
279 this editor via the ``TerminalInteractiveShell.editor`` option in your
269 configuration file before it will work.
280 configuration file before it will work.
270
281
271 This command allows you to conveniently edit multi-line code right in
282 This command allows you to conveniently edit multi-line code right in
272 your IPython session.
283 your IPython session.
273
284
274 If called without arguments, %edit opens up an empty editor with a
285 If called without arguments, %edit opens up an empty editor with a
275 temporary file and will execute the contents of this file when you
286 temporary file and will execute the contents of this file when you
276 close it (don't forget to save it!).
287 close it (don't forget to save it!).
277
288
278
289
279 Options:
290 Options:
280
291
281 -n <number>: open the editor at a specified line number. By default,
292 -n <number>: open the editor at a specified line number. By default,
282 the IPython editor hook uses the unix syntax 'editor +N filename', but
293 the IPython editor hook uses the unix syntax 'editor +N filename', but
283 you can configure this by providing your own modified hook if your
294 you can configure this by providing your own modified hook if your
284 favorite editor supports line-number specifications with a different
295 favorite editor supports line-number specifications with a different
285 syntax.
296 syntax.
286
297
287 -p: this will call the editor with the same data as the previous time
298 -p: this will call the editor with the same data as the previous time
288 it was used, regardless of how long ago (in your current session) it
299 it was used, regardless of how long ago (in your current session) it
289 was.
300 was.
290
301
291 -r: use 'raw' input. This option only applies to input taken from the
302 -r: use 'raw' input. This option only applies to input taken from the
292 user's history. By default, the 'processed' history is used, so that
303 user's history. By default, the 'processed' history is used, so that
293 magics are loaded in their transformed version to valid Python. If
304 magics are loaded in their transformed version to valid Python. If
294 this option is given, the raw input as typed as the command line is
305 this option is given, the raw input as typed as the command line is
295 used instead. When you exit the editor, it will be executed by
306 used instead. When you exit the editor, it will be executed by
296 IPython's own processor.
307 IPython's own processor.
297
308
298 -x: do not execute the edited code immediately upon exit. This is
309 -x: do not execute the edited code immediately upon exit. This is
299 mainly useful if you are editing programs which need to be called with
310 mainly useful if you are editing programs which need to be called with
300 command line arguments, which you can then do using %run.
311 command line arguments, which you can then do using %run.
301
312
302
313
303 Arguments:
314 Arguments:
304
315
305 If arguments are given, the following possibilites exist:
316 If arguments are given, the following possibilites exist:
306
317
307 - The arguments are numbers or pairs of colon-separated numbers (like
318 - The arguments are numbers or pairs of colon-separated numbers (like
308 1 4:8 9). These are interpreted as lines of previous input to be
319 1 4:8 9). These are interpreted as lines of previous input to be
309 loaded into the editor. The syntax is the same of the %macro command.
320 loaded into the editor. The syntax is the same of the %macro command.
310
321
311 - If the argument doesn't start with a number, it is evaluated as a
322 - If the argument doesn't start with a number, it is evaluated as a
312 variable and its contents loaded into the editor. You can thus edit
323 variable and its contents loaded into the editor. You can thus edit
313 any string which contains python code (including the result of
324 any string which contains python code (including the result of
314 previous edits).
325 previous edits).
315
326
316 - If the argument is the name of an object (other than a string),
327 - If the argument is the name of an object (other than a string),
317 IPython will try to locate the file where it was defined and open the
328 IPython will try to locate the file where it was defined and open the
318 editor at the point where it is defined. You can use `%edit function`
329 editor at the point where it is defined. You can use `%edit function`
319 to load an editor exactly at the point where 'function' is defined,
330 to load an editor exactly at the point where 'function' is defined,
320 edit it and have the file be executed automatically.
331 edit it and have the file be executed automatically.
321
332
322 If the object is a macro (see %macro for details), this opens up your
333 If the object is a macro (see %macro for details), this opens up your
323 specified editor with a temporary file containing the macro's data.
334 specified editor with a temporary file containing the macro's data.
324 Upon exit, the macro is reloaded with the contents of the file.
335 Upon exit, the macro is reloaded with the contents of the file.
325
336
326 Note: opening at an exact line is only supported under Unix, and some
337 Note: opening at an exact line is only supported under Unix, and some
327 editors (like kedit and gedit up to Gnome 2.8) do not understand the
338 editors (like kedit and gedit up to Gnome 2.8) do not understand the
328 '+NUMBER' parameter necessary for this feature. Good editors like
339 '+NUMBER' parameter necessary for this feature. Good editors like
329 (X)Emacs, vi, jed, pico and joe all do.
340 (X)Emacs, vi, jed, pico and joe all do.
330
341
331 - If the argument is not found as a variable, IPython will look for a
342 - If the argument is not found as a variable, IPython will look for a
332 file with that name (adding .py if necessary) and load it into the
343 file with that name (adding .py if necessary) and load it into the
333 editor. It will execute its contents with execfile() when you exit,
344 editor. It will execute its contents with execfile() when you exit,
334 loading any code in the file into your interactive namespace.
345 loading any code in the file into your interactive namespace.
335
346
336 After executing your code, %edit will return as output the code you
347 After executing your code, %edit will return as output the code you
337 typed in the editor (except when it was an existing file). This way
348 typed in the editor (except when it was an existing file). This way
338 you can reload the code in further invocations of %edit as a variable,
349 you can reload the code in further invocations of %edit as a variable,
339 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
350 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
340 the output.
351 the output.
341
352
342 Note that %edit is also available through the alias %ed.
353 Note that %edit is also available through the alias %ed.
343
354
344 This is an example of creating a simple function inside the editor and
355 This is an example of creating a simple function inside the editor and
345 then modifying it. First, start up the editor:
356 then modifying it. First, start up the editor:
346
357
347 In [1]: ed
358 In [1]: ed
348 Editing... done. Executing edited code...
359 Editing... done. Executing edited code...
349 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
360 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
350
361
351 We can then call the function foo():
362 We can then call the function foo():
352
363
353 In [2]: foo()
364 In [2]: foo()
354 foo() was defined in an editing session
365 foo() was defined in an editing session
355
366
356 Now we edit foo. IPython automatically loads the editor with the
367 Now we edit foo. IPython automatically loads the editor with the
357 (temporary) file where foo() was previously defined:
368 (temporary) file where foo() was previously defined:
358
369
359 In [3]: ed foo
370 In [3]: ed foo
360 Editing... done. Executing edited code...
371 Editing... done. Executing edited code...
361
372
362 And if we call foo() again we get the modified version:
373 And if we call foo() again we get the modified version:
363
374
364 In [4]: foo()
375 In [4]: foo()
365 foo() has now been changed!
376 foo() has now been changed!
366
377
367 Here is an example of how to edit a code snippet successive
378 Here is an example of how to edit a code snippet successive
368 times. First we call the editor:
379 times. First we call the editor:
369
380
370 In [5]: ed
381 In [5]: ed
371 Editing... done. Executing edited code...
382 Editing... done. Executing edited code...
372 hello
383 hello
373 Out[5]: "print 'hello'n"
384 Out[5]: "print 'hello'n"
374
385
375 Now we call it again with the previous output (stored in _):
386 Now we call it again with the previous output (stored in _):
376
387
377 In [6]: ed _
388 In [6]: ed _
378 Editing... done. Executing edited code...
389 Editing... done. Executing edited code...
379 hello world
390 hello world
380 Out[6]: "print 'hello world'n"
391 Out[6]: "print 'hello world'n"
381
392
382 Now we call it with the output #8 (stored in _8, also as Out[8]):
393 Now we call it with the output #8 (stored in _8, also as Out[8]):
383
394
384 In [7]: ed _8
395 In [7]: ed _8
385 Editing... done. Executing edited code...
396 Editing... done. Executing edited code...
386 hello again
397 hello again
387 Out[7]: "print 'hello again'n"
398 Out[7]: "print 'hello again'n"
388 """
399 """
389
400
390 opts,args = self.parse_options(parameter_s,'prn:')
401 opts,args = self.parse_options(parameter_s,'prn:')
391
402
392 try:
403 try:
393 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
404 filename, lineno, _ = self._find_edit_target(args, opts, last_call)
394 except MacroToEdit as e:
405 except MacroToEdit as e:
395 # TODO: Implement macro editing over 2 processes.
406 # TODO: Implement macro editing over 2 processes.
396 print("Macro editing not yet implemented in 2-process model.")
407 print("Macro editing not yet implemented in 2-process model.")
397 return
408 return
398
409
399 # Make sure we send to the client an absolute path, in case the working
410 # Make sure we send to the client an absolute path, in case the working
400 # directory of client and kernel don't match
411 # directory of client and kernel don't match
401 filename = os.path.abspath(filename)
412 filename = os.path.abspath(filename)
402
413
403 payload = {
414 payload = {
404 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
415 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
405 'filename' : filename,
416 'filename' : filename,
406 'line_number' : lineno
417 'line_number' : lineno
407 }
418 }
408 self.payload_manager.write_payload(payload)
419 self.payload_manager.write_payload(payload)
409
420
410 # A few magics that are adapted to the specifics of using pexpect and a
421 # A few magics that are adapted to the specifics of using pexpect and a
411 # remote terminal
422 # remote terminal
412
423
413 def magic_clear(self, arg_s):
424 def magic_clear(self, arg_s):
414 """Clear the terminal."""
425 """Clear the terminal."""
415 if os.name == 'posix':
426 if os.name == 'posix':
416 self.shell.system("clear")
427 self.shell.system("clear")
417 else:
428 else:
418 self.shell.system("cls")
429 self.shell.system("cls")
419
430
420 if os.name == 'nt':
431 if os.name == 'nt':
421 # This is the usual name in windows
432 # This is the usual name in windows
422 magic_cls = magic_clear
433 magic_cls = magic_clear
423
434
424 # Terminal pagers won't work over pexpect, but we do have our own pager
435 # Terminal pagers won't work over pexpect, but we do have our own pager
425
436
426 def magic_less(self, arg_s):
437 def magic_less(self, arg_s):
427 """Show a file through the pager.
438 """Show a file through the pager.
428
439
429 Files ending in .py are syntax-highlighted."""
440 Files ending in .py are syntax-highlighted."""
430 cont = open(arg_s).read()
441 cont = open(arg_s).read()
431 if arg_s.endswith('.py'):
442 if arg_s.endswith('.py'):
432 cont = self.shell.pycolorize(cont)
443 cont = self.shell.pycolorize(cont)
433 page.page(cont)
444 page.page(cont)
434
445
435 magic_more = magic_less
446 magic_more = magic_less
436
447
437 # Man calls a pager, so we also need to redefine it
448 # Man calls a pager, so we also need to redefine it
438 if os.name == 'posix':
449 if os.name == 'posix':
439 def magic_man(self, arg_s):
450 def magic_man(self, arg_s):
440 """Find the man page for the given command and display in pager."""
451 """Find the man page for the given command and display in pager."""
441 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
452 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
442 split=False))
453 split=False))
443
454
444 # FIXME: this is specific to the GUI, so we should let the gui app load
455 # FIXME: this is specific to the GUI, so we should let the gui app load
445 # magics at startup that are only for the gui. Once the gui app has proper
456 # magics at startup that are only for the gui. Once the gui app has proper
446 # profile and configuration management, we can have it initialize a kernel
457 # profile and configuration management, we can have it initialize a kernel
447 # with a special config file that provides these.
458 # with a special config file that provides these.
448 def magic_guiref(self, arg_s):
459 def magic_guiref(self, arg_s):
449 """Show a basic reference about the GUI console."""
460 """Show a basic reference about the GUI console."""
450 from IPython.core.usage import gui_reference
461 from IPython.core.usage import gui_reference
451 page.page(gui_reference, auto_html=True)
462 page.page(gui_reference, auto_html=True)
452
463
453 def magic_connect_info(self, arg_s):
464 def magic_connect_info(self, arg_s):
454 """Print information for connecting other clients to this kernel
465 """Print information for connecting other clients to this kernel
455
466
456 It will print the contents of this session's connection file, as well as
467 It will print the contents of this session's connection file, as well as
457 shortcuts for local clients.
468 shortcuts for local clients.
458
469
459 In the simplest case, when called from the most recently launched kernel,
470 In the simplest case, when called from the most recently launched kernel,
460 secondary clients can be connected, simply with:
471 secondary clients can be connected, simply with:
461
472
462 $> ipython <app> --existing
473 $> ipython <app> --existing
463
474
464 """
475 """
465
476
466 from IPython.core.application import BaseIPythonApplication as BaseIPApp
477 from IPython.core.application import BaseIPythonApplication as BaseIPApp
467
478
468 if BaseIPApp.initialized():
479 if BaseIPApp.initialized():
469 app = BaseIPApp.instance()
480 app = BaseIPApp.instance()
470 security_dir = app.profile_dir.security_dir
481 security_dir = app.profile_dir.security_dir
471 profile = app.profile
482 profile = app.profile
472 else:
483 else:
473 profile = 'default'
484 profile = 'default'
474 security_dir = ''
485 security_dir = ''
475
486
476 try:
487 try:
477 connection_file = get_connection_file()
488 connection_file = get_connection_file()
478 info = get_connection_info(unpack=False)
489 info = get_connection_info(unpack=False)
479 except Exception as e:
490 except Exception as e:
480 error("Could not get connection info: %r" % e)
491 error("Could not get connection info: %r" % e)
481 return
492 return
482
493
483 # add profile flag for non-default profile
494 # add profile flag for non-default profile
484 profile_flag = "--profile %s" % profile if profile != 'default' else ""
495 profile_flag = "--profile %s" % profile if profile != 'default' else ""
485
496
486 # if it's in the security dir, truncate to basename
497 # if it's in the security dir, truncate to basename
487 if security_dir == os.path.dirname(connection_file):
498 if security_dir == os.path.dirname(connection_file):
488 connection_file = os.path.basename(connection_file)
499 connection_file = os.path.basename(connection_file)
489
500
490
501
491 print (info + '\n')
502 print (info + '\n')
492 print ("Paste the above JSON into a file, and connect with:\n"
503 print ("Paste the above JSON into a file, and connect with:\n"
493 " $> ipython <app> --existing <file>\n"
504 " $> ipython <app> --existing <file>\n"
494 "or, if you are local, you can connect with just:\n"
505 "or, if you are local, you can connect with just:\n"
495 " $> ipython <app> --existing {0} {1}\n"
506 " $> ipython <app> --existing {0} {1}\n"
496 "or even just:\n"
507 "or even just:\n"
497 " $> ipython <app> --existing {1}\n"
508 " $> ipython <app> --existing {1}\n"
498 "if this is the most recent IPython session you have started.".format(
509 "if this is the most recent IPython session you have started.".format(
499 connection_file, profile_flag
510 connection_file, profile_flag
500 )
511 )
501 )
512 )
502
513
503 def magic_qtconsole(self, arg_s):
514 def magic_qtconsole(self, arg_s):
504 """Open a qtconsole connected to this kernel.
515 """Open a qtconsole connected to this kernel.
505
516
506 Useful for connecting a qtconsole to running notebooks, for better
517 Useful for connecting a qtconsole to running notebooks, for better
507 debugging.
518 debugging.
508 """
519 """
509 try:
520 try:
510 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
521 p = connect_qtconsole(argv=arg_split(arg_s, os.name=='posix'))
511 except Exception as e:
522 except Exception as e:
512 error("Could not start qtconsole: %r" % e)
523 error("Could not start qtconsole: %r" % e)
513 return
524 return
514
525
515 def set_next_input(self, text):
526 def set_next_input(self, text):
516 """Send the specified text to the frontend to be presented at the next
527 """Send the specified text to the frontend to be presented at the next
517 input cell."""
528 input cell."""
518 payload = dict(
529 payload = dict(
519 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
530 source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input',
520 text=text
531 text=text
521 )
532 )
522 self.payload_manager.write_payload(payload)
533 self.payload_manager.write_payload(payload)
523
534
524
535
525 InteractiveShellABC.register(ZMQInteractiveShell)
536 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now