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