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