##// END OF EJS Templates
make parent_header available from the Shell object
MinRK -
Show More
@@ -1,798 +1,789 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """An interactive kernel that talks to frontends over 0MQ."""
3
4 Things to do:
5
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.
8 * Implement random port and security key logic.
9 * Implement control messages.
10 * Implement event loop and poll version.
11 """
12
3
13 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
14 # Imports
5 # Imports
15 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
16 from __future__ import print_function
7 from __future__ import print_function
17
8
18 # Standard library imports
9 # Standard library imports
19 import __builtin__
10 import __builtin__
20 import sys
11 import sys
21 import time
12 import time
22 import traceback
13 import traceback
23 import logging
14 import logging
24 import uuid
15 import uuid
25
16
26 from datetime import datetime
17 from datetime import datetime
27 from signal import (
18 from signal import (
28 signal, default_int_handler, SIGINT
19 signal, default_int_handler, SIGINT
29 )
20 )
30
21
31 # System library imports
22 # System library imports
32 import zmq
23 import zmq
33 from zmq.eventloop import ioloop
24 from zmq.eventloop import ioloop
34 from zmq.eventloop.zmqstream import ZMQStream
25 from zmq.eventloop.zmqstream import ZMQStream
35
26
36 # Local imports
27 # Local imports
37 from IPython.config.configurable import Configurable
28 from IPython.config.configurable import Configurable
38 from IPython.core.error import StdinNotImplementedError
29 from IPython.core.error import StdinNotImplementedError
39 from IPython.core import release
30 from IPython.core import release
40 from IPython.utils import py3compat
31 from IPython.utils import py3compat
41 from IPython.utils.jsonutil import json_clean
32 from IPython.utils.jsonutil import json_clean
42 from IPython.utils.traitlets import (
33 from IPython.utils.traitlets import (
43 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
34 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
44 Type
35 Type
45 )
36 )
46
37
47 from serialize import serialize_object, unpack_apply_message
38 from serialize import serialize_object, unpack_apply_message
48 from session import Session
39 from session import Session
49 from zmqshell import ZMQInteractiveShell
40 from zmqshell import ZMQInteractiveShell
50
41
51
42
52 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
53 # Main kernel class
44 # Main kernel class
54 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
55
46
56 protocol_version = list(release.kernel_protocol_version_info)
47 protocol_version = list(release.kernel_protocol_version_info)
57 ipython_version = list(release.version_info)
48 ipython_version = list(release.version_info)
58 language_version = list(sys.version_info[:3])
49 language_version = list(sys.version_info[:3])
59
50
60
51
61 class Kernel(Configurable):
52 class Kernel(Configurable):
62
53
63 #---------------------------------------------------------------------------
54 #---------------------------------------------------------------------------
64 # Kernel interface
55 # Kernel interface
65 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
66
57
67 # attribute to override with a GUI
58 # attribute to override with a GUI
68 eventloop = Any(None)
59 eventloop = Any(None)
69 def _eventloop_changed(self, name, old, new):
60 def _eventloop_changed(self, name, old, new):
70 """schedule call to eventloop from IOLoop"""
61 """schedule call to eventloop from IOLoop"""
71 loop = ioloop.IOLoop.instance()
62 loop = ioloop.IOLoop.instance()
72 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
63 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
73
64
74 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
75 shell_class = Type(ZMQInteractiveShell)
66 shell_class = Type(ZMQInteractiveShell)
76
67
77 session = Instance(Session)
68 session = Instance(Session)
78 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
69 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
79 shell_streams = List()
70 shell_streams = List()
80 control_stream = Instance(ZMQStream)
71 control_stream = Instance(ZMQStream)
81 iopub_socket = Instance(zmq.Socket)
72 iopub_socket = Instance(zmq.Socket)
82 stdin_socket = Instance(zmq.Socket)
73 stdin_socket = Instance(zmq.Socket)
83 log = Instance(logging.Logger)
74 log = Instance(logging.Logger)
84
75
85 user_module = Any()
76 user_module = Any()
86 def _user_module_changed(self, name, old, new):
77 def _user_module_changed(self, name, old, new):
87 if self.shell is not None:
78 if self.shell is not None:
88 self.shell.user_module = new
79 self.shell.user_module = new
89
80
90 user_ns = Instance(dict, args=None, allow_none=True)
81 user_ns = Instance(dict, args=None, allow_none=True)
91 def _user_ns_changed(self, name, old, new):
82 def _user_ns_changed(self, name, old, new):
92 if self.shell is not None:
83 if self.shell is not None:
93 self.shell.user_ns = new
84 self.shell.user_ns = new
94 self.shell.init_user_ns()
85 self.shell.init_user_ns()
95
86
96 # identities:
87 # identities:
97 int_id = Integer(-1)
88 int_id = Integer(-1)
98 ident = Unicode()
89 ident = Unicode()
99
90
100 def _ident_default(self):
91 def _ident_default(self):
101 return unicode(uuid.uuid4())
92 return unicode(uuid.uuid4())
102
93
103
94
104 # Private interface
95 # Private interface
105
96
106 # Time to sleep after flushing the stdout/err buffers in each execute
97 # 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
98 # cycle. While this introduces a hard limit on the minimal latency of the
108 # execute cycle, it helps prevent output synchronization problems for
99 # execute cycle, it helps prevent output synchronization problems for
109 # clients.
100 # clients.
110 # Units are in seconds. The minimum zmq latency on local host is probably
101 # 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
102 # ~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.
103 # a little if it's not enough after more interactive testing.
113 _execute_sleep = Float(0.0005, config=True)
104 _execute_sleep = Float(0.0005, config=True)
114
105
115 # Frequency of the kernel's event loop.
106 # Frequency of the kernel's event loop.
116 # Units are in seconds, kernel subclasses for GUI toolkits may need to
107 # Units are in seconds, kernel subclasses for GUI toolkits may need to
117 # adapt to milliseconds.
108 # adapt to milliseconds.
118 _poll_interval = Float(0.05, config=True)
109 _poll_interval = Float(0.05, config=True)
119
110
120 # If the shutdown was requested over the network, we leave here the
111 # 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
112 # 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
113 # 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
114 # the end of our shutdown process (which happens after the underlying
124 # IPython shell's own shutdown).
115 # IPython shell's own shutdown).
125 _shutdown_message = None
116 _shutdown_message = None
126
117
127 # This is a dict of port number that the kernel is listening on. It is set
118 # 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.
119 # by record_ports and used by connect_request.
129 _recorded_ports = Dict()
120 _recorded_ports = Dict()
130
121
131 # A reference to the Python builtin 'raw_input' function.
122 # A reference to the Python builtin 'raw_input' function.
132 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
123 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
133 _sys_raw_input = Any()
124 _sys_raw_input = Any()
134 _sys_eval_input = Any()
125 _sys_eval_input = Any()
135
126
136 # set of aborted msg_ids
127 # set of aborted msg_ids
137 aborted = Set()
128 aborted = Set()
138
129
139
130
140 def __init__(self, **kwargs):
131 def __init__(self, **kwargs):
141 super(Kernel, self).__init__(**kwargs)
132 super(Kernel, self).__init__(**kwargs)
142
133
143 # Initialize the InteractiveShell subclass
134 # Initialize the InteractiveShell subclass
144 self.shell = self.shell_class.instance(parent=self,
135 self.shell = self.shell_class.instance(parent=self,
145 profile_dir = self.profile_dir,
136 profile_dir = self.profile_dir,
146 user_module = self.user_module,
137 user_module = self.user_module,
147 user_ns = self.user_ns,
138 user_ns = self.user_ns,
148 kernel = self,
139 kernel = self,
149 )
140 )
150 self.shell.displayhook.session = self.session
141 self.shell.displayhook.session = self.session
151 self.shell.displayhook.pub_socket = self.iopub_socket
142 self.shell.displayhook.pub_socket = self.iopub_socket
152 self.shell.displayhook.topic = self._topic('pyout')
143 self.shell.displayhook.topic = self._topic('pyout')
153 self.shell.display_pub.session = self.session
144 self.shell.display_pub.session = self.session
154 self.shell.display_pub.pub_socket = self.iopub_socket
145 self.shell.display_pub.pub_socket = self.iopub_socket
155 self.shell.data_pub.session = self.session
146 self.shell.data_pub.session = self.session
156 self.shell.data_pub.pub_socket = self.iopub_socket
147 self.shell.data_pub.pub_socket = self.iopub_socket
157
148
158 # TMP - hack while developing
149 # TMP - hack while developing
159 self.shell._reply_content = None
150 self.shell._reply_content = None
160
151
161 # Build dict of handlers for message types
152 # Build dict of handlers for message types
162 msg_types = [ 'execute_request', 'complete_request',
153 msg_types = [ 'execute_request', 'complete_request',
163 'object_info_request', 'history_request',
154 'object_info_request', 'history_request',
164 'kernel_info_request',
155 'kernel_info_request',
165 'connect_request', 'shutdown_request',
156 'connect_request', 'shutdown_request',
166 'apply_request',
157 'apply_request',
167 ]
158 ]
168 self.shell_handlers = {}
159 self.shell_handlers = {}
169 for msg_type in msg_types:
160 for msg_type in msg_types:
170 self.shell_handlers[msg_type] = getattr(self, msg_type)
161 self.shell_handlers[msg_type] = getattr(self, msg_type)
171
162
172 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
163 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
173 comm_manager = self.shell.comm_manager
164 comm_manager = self.shell.comm_manager
174 for msg_type in comm_msg_types:
165 for msg_type in comm_msg_types:
175 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
166 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
176
167
177 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
168 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
178 self.control_handlers = {}
169 self.control_handlers = {}
179 for msg_type in control_msg_types:
170 for msg_type in control_msg_types:
180 self.control_handlers[msg_type] = getattr(self, msg_type)
171 self.control_handlers[msg_type] = getattr(self, msg_type)
181
172
182
173
183 def dispatch_control(self, msg):
174 def dispatch_control(self, msg):
184 """dispatch control requests"""
175 """dispatch control requests"""
185 idents,msg = self.session.feed_identities(msg, copy=False)
176 idents,msg = self.session.feed_identities(msg, copy=False)
186 try:
177 try:
187 msg = self.session.unserialize(msg, content=True, copy=False)
178 msg = self.session.unserialize(msg, content=True, copy=False)
188 except:
179 except:
189 self.log.error("Invalid Control Message", exc_info=True)
180 self.log.error("Invalid Control Message", exc_info=True)
190 return
181 return
191
182
192 self.log.debug("Control received: %s", msg)
183 self.log.debug("Control received: %s", msg)
193
184
194 header = msg['header']
185 header = msg['header']
195 msg_id = header['msg_id']
186 msg_id = header['msg_id']
196 msg_type = header['msg_type']
187 msg_type = header['msg_type']
197
188
198 handler = self.control_handlers.get(msg_type, None)
189 handler = self.control_handlers.get(msg_type, None)
199 if handler is None:
190 if handler is None:
200 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
191 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
201 else:
192 else:
202 try:
193 try:
203 handler(self.control_stream, idents, msg)
194 handler(self.control_stream, idents, msg)
204 except Exception:
195 except Exception:
205 self.log.error("Exception in control handler:", exc_info=True)
196 self.log.error("Exception in control handler:", exc_info=True)
206
197
207 def dispatch_shell(self, stream, msg):
198 def dispatch_shell(self, stream, msg):
208 """dispatch shell requests"""
199 """dispatch shell requests"""
209 # flush control requests first
200 # flush control requests first
210 if self.control_stream:
201 if self.control_stream:
211 self.control_stream.flush()
202 self.control_stream.flush()
212
203
213 idents,msg = self.session.feed_identities(msg, copy=False)
204 idents,msg = self.session.feed_identities(msg, copy=False)
214 try:
205 try:
215 msg = self.session.unserialize(msg, content=True, copy=False)
206 msg = self.session.unserialize(msg, content=True, copy=False)
216 except:
207 except:
217 self.log.error("Invalid Message", exc_info=True)
208 self.log.error("Invalid Message", exc_info=True)
218 return
209 return
219
210
220 header = msg['header']
211 header = msg['header']
221 msg_id = header['msg_id']
212 msg_id = header['msg_id']
222 msg_type = msg['header']['msg_type']
213 msg_type = msg['header']['msg_type']
223
214
224 # Print some info about this message and leave a '--->' marker, so it's
215 # Print some info about this message and leave a '--->' marker, so it's
225 # easier to trace visually the message chain when debugging. Each
216 # easier to trace visually the message chain when debugging. Each
226 # handler prints its message at the end.
217 # handler prints its message at the end.
227 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
218 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
228 self.log.debug(' Content: %s\n --->\n ', msg['content'])
219 self.log.debug(' Content: %s\n --->\n ', msg['content'])
229
220
230 if msg_id in self.aborted:
221 if msg_id in self.aborted:
231 self.aborted.remove(msg_id)
222 self.aborted.remove(msg_id)
232 # is it safe to assume a msg_id will not be resubmitted?
223 # is it safe to assume a msg_id will not be resubmitted?
233 reply_type = msg_type.split('_')[0] + '_reply'
224 reply_type = msg_type.split('_')[0] + '_reply'
234 status = {'status' : 'aborted'}
225 status = {'status' : 'aborted'}
235 md = {'engine' : self.ident}
226 md = {'engine' : self.ident}
236 md.update(status)
227 md.update(status)
237 reply_msg = self.session.send(stream, reply_type, metadata=md,
228 reply_msg = self.session.send(stream, reply_type, metadata=md,
238 content=status, parent=msg, ident=idents)
229 content=status, parent=msg, ident=idents)
239 return
230 return
240
231
241 handler = self.shell_handlers.get(msg_type, None)
232 handler = self.shell_handlers.get(msg_type, None)
242 if handler is None:
233 if handler is None:
243 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
234 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
244 else:
235 else:
245 # ensure default_int_handler during handler call
236 # ensure default_int_handler during handler call
246 sig = signal(SIGINT, default_int_handler)
237 sig = signal(SIGINT, default_int_handler)
247 try:
238 try:
248 handler(stream, idents, msg)
239 handler(stream, idents, msg)
249 except Exception:
240 except Exception:
250 self.log.error("Exception in message handler:", exc_info=True)
241 self.log.error("Exception in message handler:", exc_info=True)
251 finally:
242 finally:
252 signal(SIGINT, sig)
243 signal(SIGINT, sig)
253
244
254 def enter_eventloop(self):
245 def enter_eventloop(self):
255 """enter eventloop"""
246 """enter eventloop"""
256 self.log.info("entering eventloop")
247 self.log.info("entering eventloop")
257 # restore default_int_handler
248 # restore default_int_handler
258 signal(SIGINT, default_int_handler)
249 signal(SIGINT, default_int_handler)
259 while self.eventloop is not None:
250 while self.eventloop is not None:
260 try:
251 try:
261 self.eventloop(self)
252 self.eventloop(self)
262 except KeyboardInterrupt:
253 except KeyboardInterrupt:
263 # Ctrl-C shouldn't crash the kernel
254 # Ctrl-C shouldn't crash the kernel
264 self.log.error("KeyboardInterrupt caught in kernel")
255 self.log.error("KeyboardInterrupt caught in kernel")
265 continue
256 continue
266 else:
257 else:
267 # eventloop exited cleanly, this means we should stop (right?)
258 # eventloop exited cleanly, this means we should stop (right?)
268 self.eventloop = None
259 self.eventloop = None
269 break
260 break
270 self.log.info("exiting eventloop")
261 self.log.info("exiting eventloop")
271
262
272 def start(self):
263 def start(self):
273 """register dispatchers for streams"""
264 """register dispatchers for streams"""
274 self.shell.exit_now = False
265 self.shell.exit_now = False
275 if self.control_stream:
266 if self.control_stream:
276 self.control_stream.on_recv(self.dispatch_control, copy=False)
267 self.control_stream.on_recv(self.dispatch_control, copy=False)
277
268
278 def make_dispatcher(stream):
269 def make_dispatcher(stream):
279 def dispatcher(msg):
270 def dispatcher(msg):
280 return self.dispatch_shell(stream, msg)
271 return self.dispatch_shell(stream, msg)
281 return dispatcher
272 return dispatcher
282
273
283 for s in self.shell_streams:
274 for s in self.shell_streams:
284 s.on_recv(make_dispatcher(s), copy=False)
275 s.on_recv(make_dispatcher(s), copy=False)
285
276
286 # publish idle status
277 # publish idle status
287 self._publish_status('starting')
278 self._publish_status('starting')
288
279
289 def do_one_iteration(self):
280 def do_one_iteration(self):
290 """step eventloop just once"""
281 """step eventloop just once"""
291 if self.control_stream:
282 if self.control_stream:
292 self.control_stream.flush()
283 self.control_stream.flush()
293 for stream in self.shell_streams:
284 for stream in self.shell_streams:
294 # handle at most one request per iteration
285 # handle at most one request per iteration
295 stream.flush(zmq.POLLIN, 1)
286 stream.flush(zmq.POLLIN, 1)
296 stream.flush(zmq.POLLOUT)
287 stream.flush(zmq.POLLOUT)
297
288
298
289
299 def record_ports(self, ports):
290 def record_ports(self, ports):
300 """Record the ports that this kernel is using.
291 """Record the ports that this kernel is using.
301
292
302 The creator of the Kernel instance must call this methods if they
293 The creator of the Kernel instance must call this methods if they
303 want the :meth:`connect_request` method to return the port numbers.
294 want the :meth:`connect_request` method to return the port numbers.
304 """
295 """
305 self._recorded_ports = ports
296 self._recorded_ports = ports
306
297
307 #---------------------------------------------------------------------------
298 #---------------------------------------------------------------------------
308 # Kernel request handlers
299 # Kernel request handlers
309 #---------------------------------------------------------------------------
300 #---------------------------------------------------------------------------
310
301
311 def _make_metadata(self, other=None):
302 def _make_metadata(self, other=None):
312 """init metadata dict, for execute/apply_reply"""
303 """init metadata dict, for execute/apply_reply"""
313 new_md = {
304 new_md = {
314 'dependencies_met' : True,
305 'dependencies_met' : True,
315 'engine' : self.ident,
306 'engine' : self.ident,
316 'started': datetime.now(),
307 'started': datetime.now(),
317 }
308 }
318 if other:
309 if other:
319 new_md.update(other)
310 new_md.update(other)
320 return new_md
311 return new_md
321
312
322 def _publish_pyin(self, code, parent, execution_count):
313 def _publish_pyin(self, code, parent, execution_count):
323 """Publish the code request on the pyin stream."""
314 """Publish the code request on the pyin stream."""
324
315
325 self.session.send(self.iopub_socket, u'pyin',
316 self.session.send(self.iopub_socket, u'pyin',
326 {u'code':code, u'execution_count': execution_count},
317 {u'code':code, u'execution_count': execution_count},
327 parent=parent, ident=self._topic('pyin')
318 parent=parent, ident=self._topic('pyin')
328 )
319 )
329
320
330 def _publish_status(self, status, parent=None):
321 def _publish_status(self, status, parent=None):
331 """send status (busy/idle) on IOPub"""
322 """send status (busy/idle) on IOPub"""
332 self.session.send(self.iopub_socket,
323 self.session.send(self.iopub_socket,
333 u'status',
324 u'status',
334 {u'execution_state': status},
325 {u'execution_state': status},
335 parent=parent,
326 parent=parent,
336 ident=self._topic('status'),
327 ident=self._topic('status'),
337 )
328 )
338
329
339
330
340 def execute_request(self, stream, ident, parent):
331 def execute_request(self, stream, ident, parent):
341 """handle an execute_request"""
332 """handle an execute_request"""
342
333
343 self._publish_status(u'busy', parent)
334 self._publish_status(u'busy', parent)
344
335
345 try:
336 try:
346 content = parent[u'content']
337 content = parent[u'content']
347 code = content[u'code']
338 code = content[u'code']
348 silent = content[u'silent']
339 silent = content[u'silent']
349 store_history = content.get(u'store_history', not silent)
340 store_history = content.get(u'store_history', not silent)
350 except:
341 except:
351 self.log.error("Got bad msg: ")
342 self.log.error("Got bad msg: ")
352 self.log.error("%s", parent)
343 self.log.error("%s", parent)
353 return
344 return
354
345
355 md = self._make_metadata(parent['metadata'])
346 md = self._make_metadata(parent['metadata'])
356
347
357 shell = self.shell # we'll need this a lot here
348 shell = self.shell # we'll need this a lot here
358
349
359 # Replace raw_input. Note that is not sufficient to replace
350 # Replace raw_input. Note that is not sufficient to replace
360 # raw_input in the user namespace.
351 # raw_input in the user namespace.
361 if content.get('allow_stdin', False):
352 if content.get('allow_stdin', False):
362 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
353 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
363 input = lambda prompt='': eval(raw_input(prompt))
354 input = lambda prompt='': eval(raw_input(prompt))
364 else:
355 else:
365 raw_input = input = lambda prompt='' : self._no_raw_input()
356 raw_input = input = lambda prompt='' : self._no_raw_input()
366
357
367 if py3compat.PY3:
358 if py3compat.PY3:
368 self._sys_raw_input = __builtin__.input
359 self._sys_raw_input = __builtin__.input
369 __builtin__.input = raw_input
360 __builtin__.input = raw_input
370 else:
361 else:
371 self._sys_raw_input = __builtin__.raw_input
362 self._sys_raw_input = __builtin__.raw_input
372 self._sys_eval_input = __builtin__.input
363 self._sys_eval_input = __builtin__.input
373 __builtin__.raw_input = raw_input
364 __builtin__.raw_input = raw_input
374 __builtin__.input = input
365 __builtin__.input = input
375
366
376 # Set the parent message of the display hook and out streams.
367 # Set the parent message of the display hook and out streams.
377 shell.set_parent(parent)
368 shell.set_parent(parent)
378
369
379 # Re-broadcast our input for the benefit of listening clients, and
370 # Re-broadcast our input for the benefit of listening clients, and
380 # start computing output
371 # start computing output
381 if not silent:
372 if not silent:
382 self._publish_pyin(code, parent, shell.execution_count)
373 self._publish_pyin(code, parent, shell.execution_count)
383
374
384 reply_content = {}
375 reply_content = {}
385 try:
376 try:
386 # FIXME: the shell calls the exception handler itself.
377 # FIXME: the shell calls the exception handler itself.
387 shell.run_cell(code, store_history=store_history, silent=silent)
378 shell.run_cell(code, store_history=store_history, silent=silent)
388 except:
379 except:
389 status = u'error'
380 status = u'error'
390 # FIXME: this code right now isn't being used yet by default,
381 # FIXME: this code right now isn't being used yet by default,
391 # because the run_cell() call above directly fires off exception
382 # because the run_cell() call above directly fires off exception
392 # reporting. This code, therefore, is only active in the scenario
383 # reporting. This code, therefore, is only active in the scenario
393 # where runlines itself has an unhandled exception. We need to
384 # where runlines itself has an unhandled exception. We need to
394 # uniformize this, for all exception construction to come from a
385 # uniformize this, for all exception construction to come from a
395 # single location in the codbase.
386 # single location in the codbase.
396 etype, evalue, tb = sys.exc_info()
387 etype, evalue, tb = sys.exc_info()
397 tb_list = traceback.format_exception(etype, evalue, tb)
388 tb_list = traceback.format_exception(etype, evalue, tb)
398 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
389 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
399 else:
390 else:
400 status = u'ok'
391 status = u'ok'
401 finally:
392 finally:
402 # Restore raw_input.
393 # Restore raw_input.
403 if py3compat.PY3:
394 if py3compat.PY3:
404 __builtin__.input = self._sys_raw_input
395 __builtin__.input = self._sys_raw_input
405 else:
396 else:
406 __builtin__.raw_input = self._sys_raw_input
397 __builtin__.raw_input = self._sys_raw_input
407 __builtin__.input = self._sys_eval_input
398 __builtin__.input = self._sys_eval_input
408
399
409 reply_content[u'status'] = status
400 reply_content[u'status'] = status
410
401
411 # Return the execution counter so clients can display prompts
402 # Return the execution counter so clients can display prompts
412 reply_content['execution_count'] = shell.execution_count - 1
403 reply_content['execution_count'] = shell.execution_count - 1
413
404
414 # FIXME - fish exception info out of shell, possibly left there by
405 # FIXME - fish exception info out of shell, possibly left there by
415 # runlines. We'll need to clean up this logic later.
406 # runlines. We'll need to clean up this logic later.
416 if shell._reply_content is not None:
407 if shell._reply_content is not None:
417 reply_content.update(shell._reply_content)
408 reply_content.update(shell._reply_content)
418 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
409 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
419 reply_content['engine_info'] = e_info
410 reply_content['engine_info'] = e_info
420 # reset after use
411 # reset after use
421 shell._reply_content = None
412 shell._reply_content = None
422
413
423 if 'traceback' in reply_content:
414 if 'traceback' in reply_content:
424 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
415 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
425
416
426
417
427 # At this point, we can tell whether the main code execution succeeded
418 # At this point, we can tell whether the main code execution succeeded
428 # or not. If it did, we proceed to evaluate user_variables/expressions
419 # or not. If it did, we proceed to evaluate user_variables/expressions
429 if reply_content['status'] == 'ok':
420 if reply_content['status'] == 'ok':
430 reply_content[u'user_variables'] = \
421 reply_content[u'user_variables'] = \
431 shell.user_variables(content.get(u'user_variables', []))
422 shell.user_variables(content.get(u'user_variables', []))
432 reply_content[u'user_expressions'] = \
423 reply_content[u'user_expressions'] = \
433 shell.user_expressions(content.get(u'user_expressions', {}))
424 shell.user_expressions(content.get(u'user_expressions', {}))
434 else:
425 else:
435 # If there was an error, don't even try to compute variables or
426 # If there was an error, don't even try to compute variables or
436 # expressions
427 # expressions
437 reply_content[u'user_variables'] = {}
428 reply_content[u'user_variables'] = {}
438 reply_content[u'user_expressions'] = {}
429 reply_content[u'user_expressions'] = {}
439
430
440 # Payloads should be retrieved regardless of outcome, so we can both
431 # Payloads should be retrieved regardless of outcome, so we can both
441 # recover partial output (that could have been generated early in a
432 # recover partial output (that could have been generated early in a
442 # block, before an error) and clear the payload system always.
433 # block, before an error) and clear the payload system always.
443 reply_content[u'payload'] = shell.payload_manager.read_payload()
434 reply_content[u'payload'] = shell.payload_manager.read_payload()
444 # Be agressive about clearing the payload because we don't want
435 # Be agressive about clearing the payload because we don't want
445 # it to sit in memory until the next execute_request comes in.
436 # it to sit in memory until the next execute_request comes in.
446 shell.payload_manager.clear_payload()
437 shell.payload_manager.clear_payload()
447
438
448 # Flush output before sending the reply.
439 # Flush output before sending the reply.
449 sys.stdout.flush()
440 sys.stdout.flush()
450 sys.stderr.flush()
441 sys.stderr.flush()
451 # FIXME: on rare occasions, the flush doesn't seem to make it to the
442 # FIXME: on rare occasions, the flush doesn't seem to make it to the
452 # clients... This seems to mitigate the problem, but we definitely need
443 # clients... This seems to mitigate the problem, but we definitely need
453 # to better understand what's going on.
444 # to better understand what's going on.
454 if self._execute_sleep:
445 if self._execute_sleep:
455 time.sleep(self._execute_sleep)
446 time.sleep(self._execute_sleep)
456
447
457 # Send the reply.
448 # Send the reply.
458 reply_content = json_clean(reply_content)
449 reply_content = json_clean(reply_content)
459
450
460 md['status'] = reply_content['status']
451 md['status'] = reply_content['status']
461 if reply_content['status'] == 'error' and \
452 if reply_content['status'] == 'error' and \
462 reply_content['ename'] == 'UnmetDependency':
453 reply_content['ename'] == 'UnmetDependency':
463 md['dependencies_met'] = False
454 md['dependencies_met'] = False
464
455
465 reply_msg = self.session.send(stream, u'execute_reply',
456 reply_msg = self.session.send(stream, u'execute_reply',
466 reply_content, parent, metadata=md,
457 reply_content, parent, metadata=md,
467 ident=ident)
458 ident=ident)
468
459
469 self.log.debug("%s", reply_msg)
460 self.log.debug("%s", reply_msg)
470
461
471 if not silent and reply_msg['content']['status'] == u'error':
462 if not silent and reply_msg['content']['status'] == u'error':
472 self._abort_queues()
463 self._abort_queues()
473
464
474 self._publish_status(u'idle', parent)
465 self._publish_status(u'idle', parent)
475
466
476 def complete_request(self, stream, ident, parent):
467 def complete_request(self, stream, ident, parent):
477 txt, matches = self._complete(parent)
468 txt, matches = self._complete(parent)
478 matches = {'matches' : matches,
469 matches = {'matches' : matches,
479 'matched_text' : txt,
470 'matched_text' : txt,
480 'status' : 'ok'}
471 'status' : 'ok'}
481 matches = json_clean(matches)
472 matches = json_clean(matches)
482 completion_msg = self.session.send(stream, 'complete_reply',
473 completion_msg = self.session.send(stream, 'complete_reply',
483 matches, parent, ident)
474 matches, parent, ident)
484 self.log.debug("%s", completion_msg)
475 self.log.debug("%s", completion_msg)
485
476
486 def object_info_request(self, stream, ident, parent):
477 def object_info_request(self, stream, ident, parent):
487 content = parent['content']
478 content = parent['content']
488 object_info = self.shell.object_inspect(content['oname'],
479 object_info = self.shell.object_inspect(content['oname'],
489 detail_level = content.get('detail_level', 0)
480 detail_level = content.get('detail_level', 0)
490 )
481 )
491 # Before we send this object over, we scrub it for JSON usage
482 # Before we send this object over, we scrub it for JSON usage
492 oinfo = json_clean(object_info)
483 oinfo = json_clean(object_info)
493 msg = self.session.send(stream, 'object_info_reply',
484 msg = self.session.send(stream, 'object_info_reply',
494 oinfo, parent, ident)
485 oinfo, parent, ident)
495 self.log.debug("%s", msg)
486 self.log.debug("%s", msg)
496
487
497 def history_request(self, stream, ident, parent):
488 def history_request(self, stream, ident, parent):
498 # We need to pull these out, as passing **kwargs doesn't work with
489 # We need to pull these out, as passing **kwargs doesn't work with
499 # unicode keys before Python 2.6.5.
490 # unicode keys before Python 2.6.5.
500 hist_access_type = parent['content']['hist_access_type']
491 hist_access_type = parent['content']['hist_access_type']
501 raw = parent['content']['raw']
492 raw = parent['content']['raw']
502 output = parent['content']['output']
493 output = parent['content']['output']
503 if hist_access_type == 'tail':
494 if hist_access_type == 'tail':
504 n = parent['content']['n']
495 n = parent['content']['n']
505 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
496 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
506 include_latest=True)
497 include_latest=True)
507
498
508 elif hist_access_type == 'range':
499 elif hist_access_type == 'range':
509 session = parent['content']['session']
500 session = parent['content']['session']
510 start = parent['content']['start']
501 start = parent['content']['start']
511 stop = parent['content']['stop']
502 stop = parent['content']['stop']
512 hist = self.shell.history_manager.get_range(session, start, stop,
503 hist = self.shell.history_manager.get_range(session, start, stop,
513 raw=raw, output=output)
504 raw=raw, output=output)
514
505
515 elif hist_access_type == 'search':
506 elif hist_access_type == 'search':
516 n = parent['content'].get('n')
507 n = parent['content'].get('n')
517 unique = parent['content'].get('unique', False)
508 unique = parent['content'].get('unique', False)
518 pattern = parent['content']['pattern']
509 pattern = parent['content']['pattern']
519 hist = self.shell.history_manager.search(
510 hist = self.shell.history_manager.search(
520 pattern, raw=raw, output=output, n=n, unique=unique)
511 pattern, raw=raw, output=output, n=n, unique=unique)
521
512
522 else:
513 else:
523 hist = []
514 hist = []
524 hist = list(hist)
515 hist = list(hist)
525 content = {'history' : hist}
516 content = {'history' : hist}
526 content = json_clean(content)
517 content = json_clean(content)
527 msg = self.session.send(stream, 'history_reply',
518 msg = self.session.send(stream, 'history_reply',
528 content, parent, ident)
519 content, parent, ident)
529 self.log.debug("Sending history reply with %i entries", len(hist))
520 self.log.debug("Sending history reply with %i entries", len(hist))
530
521
531 def connect_request(self, stream, ident, parent):
522 def connect_request(self, stream, ident, parent):
532 if self._recorded_ports is not None:
523 if self._recorded_ports is not None:
533 content = self._recorded_ports.copy()
524 content = self._recorded_ports.copy()
534 else:
525 else:
535 content = {}
526 content = {}
536 msg = self.session.send(stream, 'connect_reply',
527 msg = self.session.send(stream, 'connect_reply',
537 content, parent, ident)
528 content, parent, ident)
538 self.log.debug("%s", msg)
529 self.log.debug("%s", msg)
539
530
540 def kernel_info_request(self, stream, ident, parent):
531 def kernel_info_request(self, stream, ident, parent):
541 vinfo = {
532 vinfo = {
542 'protocol_version': protocol_version,
533 'protocol_version': protocol_version,
543 'ipython_version': ipython_version,
534 'ipython_version': ipython_version,
544 'language_version': language_version,
535 'language_version': language_version,
545 'language': 'python',
536 'language': 'python',
546 }
537 }
547 msg = self.session.send(stream, 'kernel_info_reply',
538 msg = self.session.send(stream, 'kernel_info_reply',
548 vinfo, parent, ident)
539 vinfo, parent, ident)
549 self.log.debug("%s", msg)
540 self.log.debug("%s", msg)
550
541
551 def shutdown_request(self, stream, ident, parent):
542 def shutdown_request(self, stream, ident, parent):
552 self.shell.exit_now = True
543 self.shell.exit_now = True
553 content = dict(status='ok')
544 content = dict(status='ok')
554 content.update(parent['content'])
545 content.update(parent['content'])
555 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
546 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
556 # same content, but different msg_id for broadcasting on IOPub
547 # same content, but different msg_id for broadcasting on IOPub
557 self._shutdown_message = self.session.msg(u'shutdown_reply',
548 self._shutdown_message = self.session.msg(u'shutdown_reply',
558 content, parent
549 content, parent
559 )
550 )
560
551
561 self._at_shutdown()
552 self._at_shutdown()
562 # call sys.exit after a short delay
553 # call sys.exit after a short delay
563 loop = ioloop.IOLoop.instance()
554 loop = ioloop.IOLoop.instance()
564 loop.add_timeout(time.time()+0.1, loop.stop)
555 loop.add_timeout(time.time()+0.1, loop.stop)
565
556
566 #---------------------------------------------------------------------------
557 #---------------------------------------------------------------------------
567 # Engine methods
558 # Engine methods
568 #---------------------------------------------------------------------------
559 #---------------------------------------------------------------------------
569
560
570 def apply_request(self, stream, ident, parent):
561 def apply_request(self, stream, ident, parent):
571 try:
562 try:
572 content = parent[u'content']
563 content = parent[u'content']
573 bufs = parent[u'buffers']
564 bufs = parent[u'buffers']
574 msg_id = parent['header']['msg_id']
565 msg_id = parent['header']['msg_id']
575 except:
566 except:
576 self.log.error("Got bad msg: %s", parent, exc_info=True)
567 self.log.error("Got bad msg: %s", parent, exc_info=True)
577 return
568 return
578
569
579 self._publish_status(u'busy', parent)
570 self._publish_status(u'busy', parent)
580
571
581 # Set the parent message of the display hook and out streams.
572 # Set the parent message of the display hook and out streams.
582 shell = self.shell
573 shell = self.shell
583 shell.set_parent(parent)
574 shell.set_parent(parent)
584
575
585 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
576 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
586 # self.iopub_socket.send(pyin_msg)
577 # self.iopub_socket.send(pyin_msg)
587 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
578 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
588 md = self._make_metadata(parent['metadata'])
579 md = self._make_metadata(parent['metadata'])
589 try:
580 try:
590 working = shell.user_ns
581 working = shell.user_ns
591
582
592 prefix = "_"+str(msg_id).replace("-","")+"_"
583 prefix = "_"+str(msg_id).replace("-","")+"_"
593
584
594 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
585 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
595
586
596 fname = getattr(f, '__name__', 'f')
587 fname = getattr(f, '__name__', 'f')
597
588
598 fname = prefix+"f"
589 fname = prefix+"f"
599 argname = prefix+"args"
590 argname = prefix+"args"
600 kwargname = prefix+"kwargs"
591 kwargname = prefix+"kwargs"
601 resultname = prefix+"result"
592 resultname = prefix+"result"
602
593
603 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
594 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
604 # print ns
595 # print ns
605 working.update(ns)
596 working.update(ns)
606 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
597 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
607 try:
598 try:
608 exec code in shell.user_global_ns, shell.user_ns
599 exec code in shell.user_global_ns, shell.user_ns
609 result = working.get(resultname)
600 result = working.get(resultname)
610 finally:
601 finally:
611 for key in ns.iterkeys():
602 for key in ns.iterkeys():
612 working.pop(key)
603 working.pop(key)
613
604
614 result_buf = serialize_object(result,
605 result_buf = serialize_object(result,
615 buffer_threshold=self.session.buffer_threshold,
606 buffer_threshold=self.session.buffer_threshold,
616 item_threshold=self.session.item_threshold,
607 item_threshold=self.session.item_threshold,
617 )
608 )
618
609
619 except:
610 except:
620 # invoke IPython traceback formatting
611 # invoke IPython traceback formatting
621 shell.showtraceback()
612 shell.showtraceback()
622 # FIXME - fish exception info out of shell, possibly left there by
613 # FIXME - fish exception info out of shell, possibly left there by
623 # run_code. We'll need to clean up this logic later.
614 # run_code. We'll need to clean up this logic later.
624 reply_content = {}
615 reply_content = {}
625 if shell._reply_content is not None:
616 if shell._reply_content is not None:
626 reply_content.update(shell._reply_content)
617 reply_content.update(shell._reply_content)
627 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
618 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
628 reply_content['engine_info'] = e_info
619 reply_content['engine_info'] = e_info
629 # reset after use
620 # reset after use
630 shell._reply_content = None
621 shell._reply_content = None
631
622
632 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
623 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
633 ident=self._topic('pyerr'))
624 ident=self._topic('pyerr'))
634 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
625 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
635 result_buf = []
626 result_buf = []
636
627
637 if reply_content['ename'] == 'UnmetDependency':
628 if reply_content['ename'] == 'UnmetDependency':
638 md['dependencies_met'] = False
629 md['dependencies_met'] = False
639 else:
630 else:
640 reply_content = {'status' : 'ok'}
631 reply_content = {'status' : 'ok'}
641
632
642 # put 'ok'/'error' status in header, for scheduler introspection:
633 # put 'ok'/'error' status in header, for scheduler introspection:
643 md['status'] = reply_content['status']
634 md['status'] = reply_content['status']
644
635
645 # flush i/o
636 # flush i/o
646 sys.stdout.flush()
637 sys.stdout.flush()
647 sys.stderr.flush()
638 sys.stderr.flush()
648
639
649 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
640 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
650 parent=parent, ident=ident,buffers=result_buf, metadata=md)
641 parent=parent, ident=ident,buffers=result_buf, metadata=md)
651
642
652 self._publish_status(u'idle', parent)
643 self._publish_status(u'idle', parent)
653
644
654 #---------------------------------------------------------------------------
645 #---------------------------------------------------------------------------
655 # Control messages
646 # Control messages
656 #---------------------------------------------------------------------------
647 #---------------------------------------------------------------------------
657
648
658 def abort_request(self, stream, ident, parent):
649 def abort_request(self, stream, ident, parent):
659 """abort a specifig msg by id"""
650 """abort a specifig msg by id"""
660 msg_ids = parent['content'].get('msg_ids', None)
651 msg_ids = parent['content'].get('msg_ids', None)
661 if isinstance(msg_ids, basestring):
652 if isinstance(msg_ids, basestring):
662 msg_ids = [msg_ids]
653 msg_ids = [msg_ids]
663 if not msg_ids:
654 if not msg_ids:
664 self.abort_queues()
655 self.abort_queues()
665 for mid in msg_ids:
656 for mid in msg_ids:
666 self.aborted.add(str(mid))
657 self.aborted.add(str(mid))
667
658
668 content = dict(status='ok')
659 content = dict(status='ok')
669 reply_msg = self.session.send(stream, 'abort_reply', content=content,
660 reply_msg = self.session.send(stream, 'abort_reply', content=content,
670 parent=parent, ident=ident)
661 parent=parent, ident=ident)
671 self.log.debug("%s", reply_msg)
662 self.log.debug("%s", reply_msg)
672
663
673 def clear_request(self, stream, idents, parent):
664 def clear_request(self, stream, idents, parent):
674 """Clear our namespace."""
665 """Clear our namespace."""
675 self.shell.reset(False)
666 self.shell.reset(False)
676 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
667 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
677 content = dict(status='ok'))
668 content = dict(status='ok'))
678
669
679
670
680 #---------------------------------------------------------------------------
671 #---------------------------------------------------------------------------
681 # Protected interface
672 # Protected interface
682 #---------------------------------------------------------------------------
673 #---------------------------------------------------------------------------
683
674
684 def _wrap_exception(self, method=None):
675 def _wrap_exception(self, method=None):
685 # import here, because _wrap_exception is only used in parallel,
676 # import here, because _wrap_exception is only used in parallel,
686 # and parallel has higher min pyzmq version
677 # and parallel has higher min pyzmq version
687 from IPython.parallel.error import wrap_exception
678 from IPython.parallel.error import wrap_exception
688 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
679 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
689 content = wrap_exception(e_info)
680 content = wrap_exception(e_info)
690 return content
681 return content
691
682
692 def _topic(self, topic):
683 def _topic(self, topic):
693 """prefixed topic for IOPub messages"""
684 """prefixed topic for IOPub messages"""
694 if self.int_id >= 0:
685 if self.int_id >= 0:
695 base = "engine.%i" % self.int_id
686 base = "engine.%i" % self.int_id
696 else:
687 else:
697 base = "kernel.%s" % self.ident
688 base = "kernel.%s" % self.ident
698
689
699 return py3compat.cast_bytes("%s.%s" % (base, topic))
690 return py3compat.cast_bytes("%s.%s" % (base, topic))
700
691
701 def _abort_queues(self):
692 def _abort_queues(self):
702 for stream in self.shell_streams:
693 for stream in self.shell_streams:
703 if stream:
694 if stream:
704 self._abort_queue(stream)
695 self._abort_queue(stream)
705
696
706 def _abort_queue(self, stream):
697 def _abort_queue(self, stream):
707 poller = zmq.Poller()
698 poller = zmq.Poller()
708 poller.register(stream.socket, zmq.POLLIN)
699 poller.register(stream.socket, zmq.POLLIN)
709 while True:
700 while True:
710 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
701 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
711 if msg is None:
702 if msg is None:
712 return
703 return
713
704
714 self.log.info("Aborting:")
705 self.log.info("Aborting:")
715 self.log.info("%s", msg)
706 self.log.info("%s", msg)
716 msg_type = msg['header']['msg_type']
707 msg_type = msg['header']['msg_type']
717 reply_type = msg_type.split('_')[0] + '_reply'
708 reply_type = msg_type.split('_')[0] + '_reply'
718
709
719 status = {'status' : 'aborted'}
710 status = {'status' : 'aborted'}
720 md = {'engine' : self.ident}
711 md = {'engine' : self.ident}
721 md.update(status)
712 md.update(status)
722 reply_msg = self.session.send(stream, reply_type, metadata=md,
713 reply_msg = self.session.send(stream, reply_type, metadata=md,
723 content=status, parent=msg, ident=idents)
714 content=status, parent=msg, ident=idents)
724 self.log.debug("%s", reply_msg)
715 self.log.debug("%s", reply_msg)
725 # We need to wait a bit for requests to come in. This can probably
716 # We need to wait a bit for requests to come in. This can probably
726 # be set shorter for true asynchronous clients.
717 # be set shorter for true asynchronous clients.
727 poller.poll(50)
718 poller.poll(50)
728
719
729
720
730 def _no_raw_input(self):
721 def _no_raw_input(self):
731 """Raise StdinNotImplentedError if active frontend doesn't support
722 """Raise StdinNotImplentedError if active frontend doesn't support
732 stdin."""
723 stdin."""
733 raise StdinNotImplementedError("raw_input was called, but this "
724 raise StdinNotImplementedError("raw_input was called, but this "
734 "frontend does not support stdin.")
725 "frontend does not support stdin.")
735
726
736 def _raw_input(self, prompt, ident, parent):
727 def _raw_input(self, prompt, ident, parent):
737 # Flush output before making the request.
728 # Flush output before making the request.
738 sys.stderr.flush()
729 sys.stderr.flush()
739 sys.stdout.flush()
730 sys.stdout.flush()
740 # flush the stdin socket, to purge stale replies
731 # flush the stdin socket, to purge stale replies
741 while True:
732 while True:
742 try:
733 try:
743 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
734 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
744 except zmq.ZMQError as e:
735 except zmq.ZMQError as e:
745 if e.errno == zmq.EAGAIN:
736 if e.errno == zmq.EAGAIN:
746 break
737 break
747 else:
738 else:
748 raise
739 raise
749
740
750 # Send the input request.
741 # Send the input request.
751 content = json_clean(dict(prompt=prompt))
742 content = json_clean(dict(prompt=prompt))
752 self.session.send(self.stdin_socket, u'input_request', content, parent,
743 self.session.send(self.stdin_socket, u'input_request', content, parent,
753 ident=ident)
744 ident=ident)
754
745
755 # Await a response.
746 # Await a response.
756 while True:
747 while True:
757 try:
748 try:
758 ident, reply = self.session.recv(self.stdin_socket, 0)
749 ident, reply = self.session.recv(self.stdin_socket, 0)
759 except Exception:
750 except Exception:
760 self.log.warn("Invalid Message:", exc_info=True)
751 self.log.warn("Invalid Message:", exc_info=True)
761 except KeyboardInterrupt:
752 except KeyboardInterrupt:
762 # re-raise KeyboardInterrupt, to truncate traceback
753 # re-raise KeyboardInterrupt, to truncate traceback
763 raise KeyboardInterrupt
754 raise KeyboardInterrupt
764 else:
755 else:
765 break
756 break
766 try:
757 try:
767 value = py3compat.unicode_to_str(reply['content']['value'])
758 value = py3compat.unicode_to_str(reply['content']['value'])
768 except:
759 except:
769 self.log.error("Got bad raw_input reply: ")
760 self.log.error("Got bad raw_input reply: ")
770 self.log.error("%s", parent)
761 self.log.error("%s", parent)
771 value = ''
762 value = ''
772 if value == '\x04':
763 if value == '\x04':
773 # EOF
764 # EOF
774 raise EOFError
765 raise EOFError
775 return value
766 return value
776
767
777 def _complete(self, msg):
768 def _complete(self, msg):
778 c = msg['content']
769 c = msg['content']
779 try:
770 try:
780 cpos = int(c['cursor_pos'])
771 cpos = int(c['cursor_pos'])
781 except:
772 except:
782 # If we don't get something that we can convert to an integer, at
773 # If we don't get something that we can convert to an integer, at
783 # least attempt the completion guessing the cursor is at the end of
774 # least attempt the completion guessing the cursor is at the end of
784 # the text, if there's any, and otherwise of the line
775 # the text, if there's any, and otherwise of the line
785 cpos = len(c['text'])
776 cpos = len(c['text'])
786 if cpos==0:
777 if cpos==0:
787 cpos = len(c['line'])
778 cpos = len(c['line'])
788 return self.shell.complete(c['text'], c['line'], cpos)
779 return self.shell.complete(c['text'], c['line'], cpos)
789
780
790 def _at_shutdown(self):
781 def _at_shutdown(self):
791 """Actions taken at shutdown by the kernel, called by python's atexit.
782 """Actions taken at shutdown by the kernel, called by python's atexit.
792 """
783 """
793 # io.rprint("Kernel at_shutdown") # dbg
784 # io.rprint("Kernel at_shutdown") # dbg
794 if self._shutdown_message is not None:
785 if self._shutdown_message is not None:
795 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
786 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
796 self.log.debug("%s", self._shutdown_message)
787 self.log.debug("%s", self._shutdown_message)
797 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
788 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
798
789
@@ -1,618 +1,623 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, Any
47 from IPython.utils.traitlets import Instance, Type, Dict, CBool, CBytes, Any
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 parent_header = Any()
481
482
482 # Override the traitlet in the parent class, because there's no point using
483 # 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
484 # readline for the kernel. Can be removed when the readline code is moved
484 # to the terminal frontend.
485 # to the terminal frontend.
485 colors_force = CBool(True)
486 colors_force = CBool(True)
486 readline_use = CBool(False)
487 readline_use = CBool(False)
487 # autoindent has no meaning in a zmqshell, and attempting to enable it
488 # autoindent has no meaning in a zmqshell, and attempting to enable it
488 # will print a warning in the absence of readline.
489 # will print a warning in the absence of readline.
489 autoindent = CBool(False)
490 autoindent = CBool(False)
490
491
491 exiter = Instance(ZMQExitAutocall)
492 exiter = Instance(ZMQExitAutocall)
492 def _exiter_default(self):
493 def _exiter_default(self):
493 return ZMQExitAutocall(self)
494 return ZMQExitAutocall(self)
494
495
495 def _exit_now_changed(self, name, old, new):
496 def _exit_now_changed(self, name, old, new):
496 """stop eventloop when exit_now fires"""
497 """stop eventloop when exit_now fires"""
497 if new:
498 if new:
498 loop = ioloop.IOLoop.instance()
499 loop = ioloop.IOLoop.instance()
499 loop.add_timeout(time.time()+0.1, loop.stop)
500 loop.add_timeout(time.time()+0.1, loop.stop)
500
501
501 keepkernel_on_exit = None
502 keepkernel_on_exit = None
502
503
503 # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no
504 # 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
505 # interactive input being read; we provide event loop support in ipkernel
505 @staticmethod
506 @staticmethod
506 def enable_gui(gui):
507 def enable_gui(gui):
507 from .eventloops import enable_gui as real_enable_gui
508 from .eventloops import enable_gui as real_enable_gui
508 try:
509 try:
509 real_enable_gui(gui)
510 real_enable_gui(gui)
510 except ValueError as e:
511 except ValueError as e:
511 raise UsageError("%s" % e)
512 raise UsageError("%s" % e)
512
513
513 def init_environment(self):
514 def init_environment(self):
514 """Configure the user's environment.
515 """Configure the user's environment.
515
516
516 """
517 """
517 env = os.environ
518 env = os.environ
518 # These two ensure 'ls' produces nice coloring on BSD-derived systems
519 # These two ensure 'ls' produces nice coloring on BSD-derived systems
519 env['TERM'] = 'xterm-color'
520 env['TERM'] = 'xterm-color'
520 env['CLICOLOR'] = '1'
521 env['CLICOLOR'] = '1'
521 # Since normal pagers don't work at all (over pexpect we don't have
522 # 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
523 # single-key control of the subprocess), try to disable paging in
523 # subprocesses as much as possible.
524 # subprocesses as much as possible.
524 env['PAGER'] = 'cat'
525 env['PAGER'] = 'cat'
525 env['GIT_PAGER'] = 'cat'
526 env['GIT_PAGER'] = 'cat'
526
527
527 # And install the payload version of page.
528 # And install the payload version of page.
528 install_payload_page()
529 install_payload_page()
529
530
530 def auto_rewrite_input(self, cmd):
531 def auto_rewrite_input(self, cmd):
531 """Called to show the auto-rewritten input for autocall and friends.
532 """Called to show the auto-rewritten input for autocall and friends.
532
533
533 FIXME: this payload is currently not correctly processed by the
534 FIXME: this payload is currently not correctly processed by the
534 frontend.
535 frontend.
535 """
536 """
536 new = self.prompt_manager.render('rewrite') + cmd
537 new = self.prompt_manager.render('rewrite') + cmd
537 payload = dict(
538 payload = dict(
538 source='auto_rewrite_input',
539 source='auto_rewrite_input',
539 transformed_input=new,
540 transformed_input=new,
540 )
541 )
541 self.payload_manager.write_payload(payload)
542 self.payload_manager.write_payload(payload)
542
543
543 def ask_exit(self):
544 def ask_exit(self):
544 """Engage the exit actions."""
545 """Engage the exit actions."""
545 self.exit_now = True
546 self.exit_now = True
546 payload = dict(
547 payload = dict(
547 source='ask_exit',
548 source='ask_exit',
548 exit=True,
549 exit=True,
549 keepkernel=self.keepkernel_on_exit,
550 keepkernel=self.keepkernel_on_exit,
550 )
551 )
551 self.payload_manager.write_payload(payload)
552 self.payload_manager.write_payload(payload)
552
553
553 def _showtraceback(self, etype, evalue, stb):
554 def _showtraceback(self, etype, evalue, stb):
554
555
555 exc_content = {
556 exc_content = {
556 u'traceback' : stb,
557 u'traceback' : stb,
557 u'ename' : unicode(etype.__name__),
558 u'ename' : unicode(etype.__name__),
558 u'evalue' : py3compat.safe_unicode(evalue),
559 u'evalue' : py3compat.safe_unicode(evalue),
559 }
560 }
560
561
561 dh = self.displayhook
562 dh = self.displayhook
562 # Send exception info over pub socket for other clients than the caller
563 # Send exception info over pub socket for other clients than the caller
563 # to pick up
564 # to pick up
564 topic = None
565 topic = None
565 if dh.topic:
566 if dh.topic:
566 topic = dh.topic.replace(b'pyout', b'pyerr')
567 topic = dh.topic.replace(b'pyout', b'pyerr')
567
568
568 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
569 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic)
569
570
570 # FIXME - Hack: store exception info in shell object. Right now, the
571 # 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
572 # 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
573 # 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
574 # here, because in the main loop, the logic that sets it is being
574 # skipped because runlines swallows the exceptions.
575 # skipped because runlines swallows the exceptions.
575 exc_content[u'status'] = u'error'
576 exc_content[u'status'] = u'error'
576 self._reply_content = exc_content
577 self._reply_content = exc_content
577 # /FIXME
578 # /FIXME
578
579
579 return exc_content
580 return exc_content
580
581
581 def set_next_input(self, text):
582 def set_next_input(self, text):
582 """Send the specified text to the frontend to be presented at the next
583 """Send the specified text to the frontend to be presented at the next
583 input cell."""
584 input cell."""
584 payload = dict(
585 payload = dict(
585 source='set_next_input',
586 source='set_next_input',
586 text=text
587 text=text
587 )
588 )
588 self.payload_manager.write_payload(payload)
589 self.payload_manager.write_payload(payload)
589
590
590 def set_parent(self, parent):
591 def set_parent(self, parent):
591 """Set the parent header for associating output with its triggering input"""
592 """Set the parent header for associating output with its triggering input"""
593 self.parent_header = parent
592 self.displayhook.set_parent(parent)
594 self.displayhook.set_parent(parent)
593 self.display_pub.set_parent(parent)
595 self.display_pub.set_parent(parent)
594 self.data_pub.set_parent(parent)
596 self.data_pub.set_parent(parent)
595 try:
597 try:
596 sys.stdout.set_parent(parent)
598 sys.stdout.set_parent(parent)
597 except AttributeError:
599 except AttributeError:
598 pass
600 pass
599 try:
601 try:
600 sys.stderr.set_parent(parent)
602 sys.stderr.set_parent(parent)
601 except AttributeError:
603 except AttributeError:
602 pass
604 pass
603
605
606 def get_parent(self):
607 return self.parent_header
608
604 #-------------------------------------------------------------------------
609 #-------------------------------------------------------------------------
605 # Things related to magics
610 # Things related to magics
606 #-------------------------------------------------------------------------
611 #-------------------------------------------------------------------------
607
612
608 def init_magics(self):
613 def init_magics(self):
609 super(ZMQInteractiveShell, self).init_magics()
614 super(ZMQInteractiveShell, self).init_magics()
610 self.register_magics(KernelMagics)
615 self.register_magics(KernelMagics)
611 self.magics_manager.register_alias('ed', 'edit')
616 self.magics_manager.register_alias('ed', 'edit')
612
617
613 def init_comms(self):
618 def init_comms(self):
614 self.comm_manager = CommManager(shell=self, parent=self)
619 self.comm_manager = CommManager(shell=self, parent=self)
615 self.configurables.append(self.comm_manager)
620 self.configurables.append(self.comm_manager)
616
621
617
622
618 InteractiveShellABC.register(ZMQInteractiveShell)
623 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now