##// END OF EJS Templates
use QSocketNotifier, not poll...
MinRK -
Show More
@@ -1,227 +1,255 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Event loop integration for the ZeroMQ-based kernels.
2 """Event loop integration for the ZeroMQ-based kernels.
3 """
3 """
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2011 The IPython Development Team
6 # Copyright (C) 2011 The IPython Development Team
7
7
8 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING, distributed as part of this software.
9 # the file COPYING, distributed as part of this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 import sys
17 import sys
18
18
19 # System library imports.
19 # System library imports
20 import zmq
20 import zmq
21
21
22 # Local imports.
22 # Local imports
23 from IPython.config.application import Application
23 from IPython.config.application import Application
24 from IPython.utils import io
24 from IPython.utils import io
25
25
26
26
27 #------------------------------------------------------------------------------
27 #------------------------------------------------------------------------------
28 # Eventloops for integrating the Kernel into different GUIs
28 # Eventloops for integrating the Kernel into different GUIs
29 #------------------------------------------------------------------------------
29 #------------------------------------------------------------------------------
30
30
31 def _on_os_x_10_9():
32 import platform
33 from distutils.version import LooseVersion as V
34 return sys.platform == 'darwin' and V(platform.mac_ver()[0]) >= V('10.9')
35
36 def _notify_stream_qt(kernel, stream):
37
38 from IPython.external.qt_for_kernel import QtCore
39
40 if _on_os_x_10_9() and kernel._darwin_app_nap:
41 from IPython.external.appnope import nope_scope as context
42 else:
43 from IPython.core.interactiveshell import no_op_context as context
44
45 def process_stream_events():
46 while stream.getsockopt(zmq.EVENTS) & zmq.POLLIN:
47 with context():
48 kernel.do_one_iteration()
49
50 fd = stream.getsockopt(zmq.FD)
51 notifier = QtCore.QSocketNotifier(fd, QtCore.QSocketNotifier.Read, kernel.app)
52 notifier.activated.connect(process_stream_events)
53
31 def loop_qt4(kernel):
54 def loop_qt4(kernel):
32 """Start a kernel with PyQt4 event loop integration."""
55 """Start a kernel with PyQt4 event loop integration."""
33
56
34 from IPython.external.qt_for_kernel import QtCore
35 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
57 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
36
58
37 kernel.app = get_app_qt4([" "])
59 kernel.app = get_app_qt4([" "])
38 kernel.app.setQuitOnLastWindowClosed(False)
60 kernel.app.setQuitOnLastWindowClosed(False)
39 kernel.timer = QtCore.QTimer()
61
40 kernel.timer.timeout.connect(kernel.do_one_iteration)
62 for s in kernel.shell_streams:
41 # Units for the timer are in milliseconds
63 _notify_stream_qt(kernel, s)
42 kernel.timer.start(1000*kernel._poll_interval)
64
43 start_event_loop_qt4(kernel.app)
65 start_event_loop_qt4(kernel.app)
44
66
45
67
46 def loop_wx(kernel):
68 def loop_wx(kernel):
47 """Start a kernel with wx event loop support."""
69 """Start a kernel with wx event loop support."""
48
70
49 import wx
71 import wx
50 from IPython.lib.guisupport import start_event_loop_wx
72 from IPython.lib.guisupport import start_event_loop_wx
73
74 if _on_os_x_10_9() and kernel._darwin_app_nap:
75 # we don't hook up App Nap contexts for Wx,
76 # just disable it outright.
77 from IPython.external.appnope import nope
78 nope()
51
79
52 doi = kernel.do_one_iteration
80 doi = kernel.do_one_iteration
53 # Wx uses milliseconds
81 # Wx uses milliseconds
54 poll_interval = int(1000*kernel._poll_interval)
82 poll_interval = int(1000*kernel._poll_interval)
55
83
56 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
84 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
57 # We make the Frame hidden when we create it in the main app below.
85 # We make the Frame hidden when we create it in the main app below.
58 class TimerFrame(wx.Frame):
86 class TimerFrame(wx.Frame):
59 def __init__(self, func):
87 def __init__(self, func):
60 wx.Frame.__init__(self, None, -1)
88 wx.Frame.__init__(self, None, -1)
61 self.timer = wx.Timer(self)
89 self.timer = wx.Timer(self)
62 # Units for the timer are in milliseconds
90 # Units for the timer are in milliseconds
63 self.timer.Start(poll_interval)
91 self.timer.Start(poll_interval)
64 self.Bind(wx.EVT_TIMER, self.on_timer)
92 self.Bind(wx.EVT_TIMER, self.on_timer)
65 self.func = func
93 self.func = func
66
94
67 def on_timer(self, event):
95 def on_timer(self, event):
68 self.func()
96 self.func()
69
97
70 # We need a custom wx.App to create our Frame subclass that has the
98 # We need a custom wx.App to create our Frame subclass that has the
71 # wx.Timer to drive the ZMQ event loop.
99 # wx.Timer to drive the ZMQ event loop.
72 class IPWxApp(wx.App):
100 class IPWxApp(wx.App):
73 def OnInit(self):
101 def OnInit(self):
74 self.frame = TimerFrame(doi)
102 self.frame = TimerFrame(doi)
75 self.frame.Show(False)
103 self.frame.Show(False)
76 return True
104 return True
77
105
78 # The redirect=False here makes sure that wx doesn't replace
106 # The redirect=False here makes sure that wx doesn't replace
79 # sys.stdout/stderr with its own classes.
107 # sys.stdout/stderr with its own classes.
80 kernel.app = IPWxApp(redirect=False)
108 kernel.app = IPWxApp(redirect=False)
81
109
82 # The import of wx on Linux sets the handler for signal.SIGINT
110 # The import of wx on Linux sets the handler for signal.SIGINT
83 # to 0. This is a bug in wx or gtk. We fix by just setting it
111 # to 0. This is a bug in wx or gtk. We fix by just setting it
84 # back to the Python default.
112 # back to the Python default.
85 import signal
113 import signal
86 if not callable(signal.getsignal(signal.SIGINT)):
114 if not callable(signal.getsignal(signal.SIGINT)):
87 signal.signal(signal.SIGINT, signal.default_int_handler)
115 signal.signal(signal.SIGINT, signal.default_int_handler)
88
116
89 start_event_loop_wx(kernel.app)
117 start_event_loop_wx(kernel.app)
90
118
91
119
92 def loop_tk(kernel):
120 def loop_tk(kernel):
93 """Start a kernel with the Tk event loop."""
121 """Start a kernel with the Tk event loop."""
94
122
95 try:
123 try:
96 from tkinter import Tk # Py 3
124 from tkinter import Tk # Py 3
97 except ImportError:
125 except ImportError:
98 from Tkinter import Tk # Py 2
126 from Tkinter import Tk # Py 2
99 doi = kernel.do_one_iteration
127 doi = kernel.do_one_iteration
100 # Tk uses milliseconds
128 # Tk uses milliseconds
101 poll_interval = int(1000*kernel._poll_interval)
129 poll_interval = int(1000*kernel._poll_interval)
102 # For Tkinter, we create a Tk object and call its withdraw method.
130 # For Tkinter, we create a Tk object and call its withdraw method.
103 class Timer(object):
131 class Timer(object):
104 def __init__(self, func):
132 def __init__(self, func):
105 self.app = Tk()
133 self.app = Tk()
106 self.app.withdraw()
134 self.app.withdraw()
107 self.func = func
135 self.func = func
108
136
109 def on_timer(self):
137 def on_timer(self):
110 self.func()
138 self.func()
111 self.app.after(poll_interval, self.on_timer)
139 self.app.after(poll_interval, self.on_timer)
112
140
113 def start(self):
141 def start(self):
114 self.on_timer() # Call it once to get things going.
142 self.on_timer() # Call it once to get things going.
115 self.app.mainloop()
143 self.app.mainloop()
116
144
117 kernel.timer = Timer(doi)
145 kernel.timer = Timer(doi)
118 kernel.timer.start()
146 kernel.timer.start()
119
147
120
148
121 def loop_gtk(kernel):
149 def loop_gtk(kernel):
122 """Start the kernel, coordinating with the GTK event loop"""
150 """Start the kernel, coordinating with the GTK event loop"""
123 from .gui.gtkembed import GTKEmbed
151 from .gui.gtkembed import GTKEmbed
124
152
125 gtk_kernel = GTKEmbed(kernel)
153 gtk_kernel = GTKEmbed(kernel)
126 gtk_kernel.start()
154 gtk_kernel.start()
127
155
128
156
129 def loop_cocoa(kernel):
157 def loop_cocoa(kernel):
130 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
158 """Start the kernel, coordinating with the Cocoa CFRunLoop event loop
131 via the matplotlib MacOSX backend.
159 via the matplotlib MacOSX backend.
132 """
160 """
133 import matplotlib
161 import matplotlib
134 if matplotlib.__version__ < '1.1.0':
162 if matplotlib.__version__ < '1.1.0':
135 kernel.log.warn(
163 kernel.log.warn(
136 "MacOSX backend in matplotlib %s doesn't have a Timer, "
164 "MacOSX backend in matplotlib %s doesn't have a Timer, "
137 "falling back on Tk for CFRunLoop integration. Note that "
165 "falling back on Tk for CFRunLoop integration. Note that "
138 "even this won't work if Tk is linked against X11 instead of "
166 "even this won't work if Tk is linked against X11 instead of "
139 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
167 "Cocoa (e.g. EPD). To use the MacOSX backend in the kernel, "
140 "you must use matplotlib >= 1.1.0, or a native libtk."
168 "you must use matplotlib >= 1.1.0, or a native libtk."
141 )
169 )
142 return loop_tk(kernel)
170 return loop_tk(kernel)
143
171
144 from matplotlib.backends.backend_macosx import TimerMac, show
172 from matplotlib.backends.backend_macosx import TimerMac, show
145
173
146 # scale interval for sec->ms
174 # scale interval for sec->ms
147 poll_interval = int(1000*kernel._poll_interval)
175 poll_interval = int(1000*kernel._poll_interval)
148
176
149 real_excepthook = sys.excepthook
177 real_excepthook = sys.excepthook
150 def handle_int(etype, value, tb):
178 def handle_int(etype, value, tb):
151 """don't let KeyboardInterrupts look like crashes"""
179 """don't let KeyboardInterrupts look like crashes"""
152 if etype is KeyboardInterrupt:
180 if etype is KeyboardInterrupt:
153 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
181 io.raw_print("KeyboardInterrupt caught in CFRunLoop")
154 else:
182 else:
155 real_excepthook(etype, value, tb)
183 real_excepthook(etype, value, tb)
156
184
157 # add doi() as a Timer to the CFRunLoop
185 # add doi() as a Timer to the CFRunLoop
158 def doi():
186 def doi():
159 # restore excepthook during IPython code
187 # restore excepthook during IPython code
160 sys.excepthook = real_excepthook
188 sys.excepthook = real_excepthook
161 kernel.do_one_iteration()
189 kernel.do_one_iteration()
162 # and back:
190 # and back:
163 sys.excepthook = handle_int
191 sys.excepthook = handle_int
164
192
165 t = TimerMac(poll_interval)
193 t = TimerMac(poll_interval)
166 t.add_callback(doi)
194 t.add_callback(doi)
167 t.start()
195 t.start()
168
196
169 # but still need a Poller for when there are no active windows,
197 # but still need a Poller for when there are no active windows,
170 # during which time mainloop() returns immediately
198 # during which time mainloop() returns immediately
171 poller = zmq.Poller()
199 poller = zmq.Poller()
172 if kernel.control_stream:
200 if kernel.control_stream:
173 poller.register(kernel.control_stream.socket, zmq.POLLIN)
201 poller.register(kernel.control_stream.socket, zmq.POLLIN)
174 for stream in kernel.shell_streams:
202 for stream in kernel.shell_streams:
175 poller.register(stream.socket, zmq.POLLIN)
203 poller.register(stream.socket, zmq.POLLIN)
176
204
177 while True:
205 while True:
178 try:
206 try:
179 # double nested try/except, to properly catch KeyboardInterrupt
207 # double nested try/except, to properly catch KeyboardInterrupt
180 # due to pyzmq Issue #130
208 # due to pyzmq Issue #130
181 try:
209 try:
182 # don't let interrupts during mainloop invoke crash_handler:
210 # don't let interrupts during mainloop invoke crash_handler:
183 sys.excepthook = handle_int
211 sys.excepthook = handle_int
184 show.mainloop()
212 show.mainloop()
185 sys.excepthook = real_excepthook
213 sys.excepthook = real_excepthook
186 # use poller if mainloop returned (no windows)
214 # use poller if mainloop returned (no windows)
187 # scale by extra factor of 10, since it's a real poll
215 # scale by extra factor of 10, since it's a real poll
188 poller.poll(10*poll_interval)
216 poller.poll(10*poll_interval)
189 kernel.do_one_iteration()
217 kernel.do_one_iteration()
190 except:
218 except:
191 raise
219 raise
192 except KeyboardInterrupt:
220 except KeyboardInterrupt:
193 # Ctrl-C shouldn't crash the kernel
221 # Ctrl-C shouldn't crash the kernel
194 io.raw_print("KeyboardInterrupt caught in kernel")
222 io.raw_print("KeyboardInterrupt caught in kernel")
195 finally:
223 finally:
196 # ensure excepthook is restored
224 # ensure excepthook is restored
197 sys.excepthook = real_excepthook
225 sys.excepthook = real_excepthook
198
226
199 # mapping of keys to loop functions
227 # mapping of keys to loop functions
200 loop_map = {
228 loop_map = {
201 'qt' : loop_qt4,
229 'qt' : loop_qt4,
202 'qt4': loop_qt4,
230 'qt4': loop_qt4,
203 'inline': None,
231 'inline': None,
204 'osx': loop_cocoa,
232 'osx': loop_cocoa,
205 'wx' : loop_wx,
233 'wx' : loop_wx,
206 'tk' : loop_tk,
234 'tk' : loop_tk,
207 'gtk': loop_gtk,
235 'gtk': loop_gtk,
208 None : None,
236 None : None,
209 }
237 }
210
238
211
239
212 def enable_gui(gui, kernel=None):
240 def enable_gui(gui, kernel=None):
213 """Enable integration with a given GUI"""
241 """Enable integration with a given GUI"""
214 if gui not in loop_map:
242 if gui not in loop_map:
215 e = "Invalid GUI request %r, valid ones are:%s" % (gui, loop_map.keys())
243 e = "Invalid GUI request %r, valid ones are:%s" % (gui, loop_map.keys())
216 raise ValueError(e)
244 raise ValueError(e)
217 if kernel is None:
245 if kernel is None:
218 if Application.initialized():
246 if Application.initialized():
219 kernel = getattr(Application.instance(), 'kernel', None)
247 kernel = getattr(Application.instance(), 'kernel', None)
220 if kernel is None:
248 if kernel is None:
221 raise RuntimeError("You didn't specify a kernel,"
249 raise RuntimeError("You didn't specify a kernel,"
222 " and no IPython Application with a kernel appears to be running."
250 " and no IPython Application with a kernel appears to be running."
223 )
251 )
224 loop = loop_map[gui]
252 loop = loop_map[gui]
225 if loop and kernel.eventloop is not None and kernel.eventloop is not loop:
253 if loop and kernel.eventloop is not None and kernel.eventloop is not loop:
226 raise RuntimeError("Cannot activate multiple GUI eventloops")
254 raise RuntimeError("Cannot activate multiple GUI eventloops")
227 kernel.eventloop = loop
255 kernel.eventloop = loop
@@ -1,789 +1,795 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """An interactive kernel that talks to frontends over 0MQ."""
2 """An interactive kernel that talks to frontends over 0MQ."""
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 from __future__ import print_function
7 from __future__ import print_function
8
8
9 # Standard library imports
9 # Standard library imports
10 import sys
10 import sys
11 import time
11 import time
12 import traceback
12 import traceback
13 import logging
13 import logging
14 import uuid
14 import uuid
15
15
16 from datetime import datetime
16 from datetime import datetime
17 from signal import (
17 from signal import (
18 signal, default_int_handler, SIGINT
18 signal, default_int_handler, SIGINT
19 )
19 )
20
20
21 # System library imports
21 # System library imports
22 import zmq
22 import zmq
23 from zmq.eventloop import ioloop
23 from zmq.eventloop import ioloop
24 from zmq.eventloop.zmqstream import ZMQStream
24 from zmq.eventloop.zmqstream import ZMQStream
25
25
26 # Local imports
26 # Local imports
27 from IPython.config.configurable import Configurable
27 from IPython.config.configurable import Configurable
28 from IPython.core.error import StdinNotImplementedError
28 from IPython.core.error import StdinNotImplementedError
29 from IPython.core import release
29 from IPython.core import release
30 from IPython.utils import py3compat
30 from IPython.utils import py3compat
31 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
31 from IPython.utils.py3compat import builtin_mod, unicode_type, string_types
32 from IPython.utils.jsonutil import json_clean
32 from IPython.utils.jsonutil import json_clean
33 from IPython.utils.traitlets import (
33 from IPython.utils.traitlets import (
34 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
34 Any, Instance, Float, Dict, List, Set, Integer, Unicode,
35 Type
35 Type, Bool,
36 )
36 )
37
37
38 from .serialize import serialize_object, unpack_apply_message
38 from .serialize import serialize_object, unpack_apply_message
39 from .session import Session
39 from .session import Session
40 from .zmqshell import ZMQInteractiveShell
40 from .zmqshell import ZMQInteractiveShell
41
41
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Main kernel class
44 # Main kernel class
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 protocol_version = list(release.kernel_protocol_version_info)
47 protocol_version = list(release.kernel_protocol_version_info)
48 ipython_version = list(release.version_info)
48 ipython_version = list(release.version_info)
49 language_version = list(sys.version_info[:3])
49 language_version = list(sys.version_info[:3])
50
50
51
51
52 class Kernel(Configurable):
52 class Kernel(Configurable):
53
53
54 #---------------------------------------------------------------------------
54 #---------------------------------------------------------------------------
55 # Kernel interface
55 # Kernel interface
56 #---------------------------------------------------------------------------
56 #---------------------------------------------------------------------------
57
57
58 # attribute to override with a GUI
58 # attribute to override with a GUI
59 eventloop = Any(None)
59 eventloop = Any(None)
60 def _eventloop_changed(self, name, old, new):
60 def _eventloop_changed(self, name, old, new):
61 """schedule call to eventloop from IOLoop"""
61 """schedule call to eventloop from IOLoop"""
62 loop = ioloop.IOLoop.instance()
62 loop = ioloop.IOLoop.instance()
63 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
63 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
64
64
65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
65 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
66 shell_class = Type(ZMQInteractiveShell)
66 shell_class = Type(ZMQInteractiveShell)
67
67
68 session = Instance(Session)
68 session = Instance(Session)
69 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
69 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
70 shell_streams = List()
70 shell_streams = List()
71 control_stream = Instance(ZMQStream)
71 control_stream = Instance(ZMQStream)
72 iopub_socket = Instance(zmq.Socket)
72 iopub_socket = Instance(zmq.Socket)
73 stdin_socket = Instance(zmq.Socket)
73 stdin_socket = Instance(zmq.Socket)
74 log = Instance(logging.Logger)
74 log = Instance(logging.Logger)
75
75
76 user_module = Any()
76 user_module = Any()
77 def _user_module_changed(self, name, old, new):
77 def _user_module_changed(self, name, old, new):
78 if self.shell is not None:
78 if self.shell is not None:
79 self.shell.user_module = new
79 self.shell.user_module = new
80
80
81 user_ns = Instance(dict, args=None, allow_none=True)
81 user_ns = Instance(dict, args=None, allow_none=True)
82 def _user_ns_changed(self, name, old, new):
82 def _user_ns_changed(self, name, old, new):
83 if self.shell is not None:
83 if self.shell is not None:
84 self.shell.user_ns = new
84 self.shell.user_ns = new
85 self.shell.init_user_ns()
85 self.shell.init_user_ns()
86
86
87 # identities:
87 # identities:
88 int_id = Integer(-1)
88 int_id = Integer(-1)
89 ident = Unicode()
89 ident = Unicode()
90
90
91 def _ident_default(self):
91 def _ident_default(self):
92 return unicode_type(uuid.uuid4())
92 return unicode_type(uuid.uuid4())
93
93
94
95 # Private interface
94 # Private interface
96
95
96 _darwin_app_nap = Bool(True, config=True,
97 help="""Whether to use appnope for compatiblity with OS X App Nap.
98
99 Only affects OS X >= 10.9.
100 """
101 )
102
97 # Time to sleep after flushing the stdout/err buffers in each execute
103 # Time to sleep after flushing the stdout/err buffers in each execute
98 # cycle. While this introduces a hard limit on the minimal latency of the
104 # cycle. While this introduces a hard limit on the minimal latency of the
99 # execute cycle, it helps prevent output synchronization problems for
105 # execute cycle, it helps prevent output synchronization problems for
100 # clients.
106 # clients.
101 # Units are in seconds. The minimum zmq latency on local host is probably
107 # Units are in seconds. The minimum zmq latency on local host is probably
102 # ~150 microseconds, set this to 500us for now. We may need to increase it
108 # ~150 microseconds, set this to 500us for now. We may need to increase it
103 # a little if it's not enough after more interactive testing.
109 # a little if it's not enough after more interactive testing.
104 _execute_sleep = Float(0.0005, config=True)
110 _execute_sleep = Float(0.0005, config=True)
105
111
106 # Frequency of the kernel's event loop.
112 # Frequency of the kernel's event loop.
107 # Units are in seconds, kernel subclasses for GUI toolkits may need to
113 # Units are in seconds, kernel subclasses for GUI toolkits may need to
108 # adapt to milliseconds.
114 # adapt to milliseconds.
109 _poll_interval = Float(0.05, config=True)
115 _poll_interval = Float(0.05, config=True)
110
116
111 # If the shutdown was requested over the network, we leave here the
117 # If the shutdown was requested over the network, we leave here the
112 # necessary reply message so it can be sent by our registered atexit
118 # necessary reply message so it can be sent by our registered atexit
113 # handler. This ensures that the reply is only sent to clients truly at
119 # handler. This ensures that the reply is only sent to clients truly at
114 # the end of our shutdown process (which happens after the underlying
120 # the end of our shutdown process (which happens after the underlying
115 # IPython shell's own shutdown).
121 # IPython shell's own shutdown).
116 _shutdown_message = None
122 _shutdown_message = None
117
123
118 # This is a dict of port number that the kernel is listening on. It is set
124 # This is a dict of port number that the kernel is listening on. It is set
119 # by record_ports and used by connect_request.
125 # by record_ports and used by connect_request.
120 _recorded_ports = Dict()
126 _recorded_ports = Dict()
121
127
122 # A reference to the Python builtin 'raw_input' function.
128 # A reference to the Python builtin 'raw_input' function.
123 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
129 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
124 _sys_raw_input = Any()
130 _sys_raw_input = Any()
125 _sys_eval_input = Any()
131 _sys_eval_input = Any()
126
132
127 # set of aborted msg_ids
133 # set of aborted msg_ids
128 aborted = Set()
134 aborted = Set()
129
135
130
136
131 def __init__(self, **kwargs):
137 def __init__(self, **kwargs):
132 super(Kernel, self).__init__(**kwargs)
138 super(Kernel, self).__init__(**kwargs)
133
139
134 # Initialize the InteractiveShell subclass
140 # Initialize the InteractiveShell subclass
135 self.shell = self.shell_class.instance(parent=self,
141 self.shell = self.shell_class.instance(parent=self,
136 profile_dir = self.profile_dir,
142 profile_dir = self.profile_dir,
137 user_module = self.user_module,
143 user_module = self.user_module,
138 user_ns = self.user_ns,
144 user_ns = self.user_ns,
139 kernel = self,
145 kernel = self,
140 )
146 )
141 self.shell.displayhook.session = self.session
147 self.shell.displayhook.session = self.session
142 self.shell.displayhook.pub_socket = self.iopub_socket
148 self.shell.displayhook.pub_socket = self.iopub_socket
143 self.shell.displayhook.topic = self._topic('pyout')
149 self.shell.displayhook.topic = self._topic('pyout')
144 self.shell.display_pub.session = self.session
150 self.shell.display_pub.session = self.session
145 self.shell.display_pub.pub_socket = self.iopub_socket
151 self.shell.display_pub.pub_socket = self.iopub_socket
146 self.shell.data_pub.session = self.session
152 self.shell.data_pub.session = self.session
147 self.shell.data_pub.pub_socket = self.iopub_socket
153 self.shell.data_pub.pub_socket = self.iopub_socket
148
154
149 # TMP - hack while developing
155 # TMP - hack while developing
150 self.shell._reply_content = None
156 self.shell._reply_content = None
151
157
152 # Build dict of handlers for message types
158 # Build dict of handlers for message types
153 msg_types = [ 'execute_request', 'complete_request',
159 msg_types = [ 'execute_request', 'complete_request',
154 'object_info_request', 'history_request',
160 'object_info_request', 'history_request',
155 'kernel_info_request',
161 'kernel_info_request',
156 'connect_request', 'shutdown_request',
162 'connect_request', 'shutdown_request',
157 'apply_request',
163 'apply_request',
158 ]
164 ]
159 self.shell_handlers = {}
165 self.shell_handlers = {}
160 for msg_type in msg_types:
166 for msg_type in msg_types:
161 self.shell_handlers[msg_type] = getattr(self, msg_type)
167 self.shell_handlers[msg_type] = getattr(self, msg_type)
162
168
163 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
169 comm_msg_types = [ 'comm_open', 'comm_msg', 'comm_close' ]
164 comm_manager = self.shell.comm_manager
170 comm_manager = self.shell.comm_manager
165 for msg_type in comm_msg_types:
171 for msg_type in comm_msg_types:
166 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
172 self.shell_handlers[msg_type] = getattr(comm_manager, msg_type)
167
173
168 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
174 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
169 self.control_handlers = {}
175 self.control_handlers = {}
170 for msg_type in control_msg_types:
176 for msg_type in control_msg_types:
171 self.control_handlers[msg_type] = getattr(self, msg_type)
177 self.control_handlers[msg_type] = getattr(self, msg_type)
172
178
173
179
174 def dispatch_control(self, msg):
180 def dispatch_control(self, msg):
175 """dispatch control requests"""
181 """dispatch control requests"""
176 idents,msg = self.session.feed_identities(msg, copy=False)
182 idents,msg = self.session.feed_identities(msg, copy=False)
177 try:
183 try:
178 msg = self.session.unserialize(msg, content=True, copy=False)
184 msg = self.session.unserialize(msg, content=True, copy=False)
179 except:
185 except:
180 self.log.error("Invalid Control Message", exc_info=True)
186 self.log.error("Invalid Control Message", exc_info=True)
181 return
187 return
182
188
183 self.log.debug("Control received: %s", msg)
189 self.log.debug("Control received: %s", msg)
184
190
185 header = msg['header']
191 header = msg['header']
186 msg_id = header['msg_id']
192 msg_id = header['msg_id']
187 msg_type = header['msg_type']
193 msg_type = header['msg_type']
188
194
189 handler = self.control_handlers.get(msg_type, None)
195 handler = self.control_handlers.get(msg_type, None)
190 if handler is None:
196 if handler is None:
191 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
197 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
192 else:
198 else:
193 try:
199 try:
194 handler(self.control_stream, idents, msg)
200 handler(self.control_stream, idents, msg)
195 except Exception:
201 except Exception:
196 self.log.error("Exception in control handler:", exc_info=True)
202 self.log.error("Exception in control handler:", exc_info=True)
197
203
198 def dispatch_shell(self, stream, msg):
204 def dispatch_shell(self, stream, msg):
199 """dispatch shell requests"""
205 """dispatch shell requests"""
200 # flush control requests first
206 # flush control requests first
201 if self.control_stream:
207 if self.control_stream:
202 self.control_stream.flush()
208 self.control_stream.flush()
203
209
204 idents,msg = self.session.feed_identities(msg, copy=False)
210 idents,msg = self.session.feed_identities(msg, copy=False)
205 try:
211 try:
206 msg = self.session.unserialize(msg, content=True, copy=False)
212 msg = self.session.unserialize(msg, content=True, copy=False)
207 except:
213 except:
208 self.log.error("Invalid Message", exc_info=True)
214 self.log.error("Invalid Message", exc_info=True)
209 return
215 return
210
216
211 header = msg['header']
217 header = msg['header']
212 msg_id = header['msg_id']
218 msg_id = header['msg_id']
213 msg_type = msg['header']['msg_type']
219 msg_type = msg['header']['msg_type']
214
220
215 # Print some info about this message and leave a '--->' marker, so it's
221 # Print some info about this message and leave a '--->' marker, so it's
216 # easier to trace visually the message chain when debugging. Each
222 # easier to trace visually the message chain when debugging. Each
217 # handler prints its message at the end.
223 # handler prints its message at the end.
218 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
224 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
219 self.log.debug(' Content: %s\n --->\n ', msg['content'])
225 self.log.debug(' Content: %s\n --->\n ', msg['content'])
220
226
221 if msg_id in self.aborted:
227 if msg_id in self.aborted:
222 self.aborted.remove(msg_id)
228 self.aborted.remove(msg_id)
223 # is it safe to assume a msg_id will not be resubmitted?
229 # is it safe to assume a msg_id will not be resubmitted?
224 reply_type = msg_type.split('_')[0] + '_reply'
230 reply_type = msg_type.split('_')[0] + '_reply'
225 status = {'status' : 'aborted'}
231 status = {'status' : 'aborted'}
226 md = {'engine' : self.ident}
232 md = {'engine' : self.ident}
227 md.update(status)
233 md.update(status)
228 reply_msg = self.session.send(stream, reply_type, metadata=md,
234 reply_msg = self.session.send(stream, reply_type, metadata=md,
229 content=status, parent=msg, ident=idents)
235 content=status, parent=msg, ident=idents)
230 return
236 return
231
237
232 handler = self.shell_handlers.get(msg_type, None)
238 handler = self.shell_handlers.get(msg_type, None)
233 if handler is None:
239 if handler is None:
234 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
240 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
235 else:
241 else:
236 # ensure default_int_handler during handler call
242 # ensure default_int_handler during handler call
237 sig = signal(SIGINT, default_int_handler)
243 sig = signal(SIGINT, default_int_handler)
238 try:
244 try:
239 handler(stream, idents, msg)
245 handler(stream, idents, msg)
240 except Exception:
246 except Exception:
241 self.log.error("Exception in message handler:", exc_info=True)
247 self.log.error("Exception in message handler:", exc_info=True)
242 finally:
248 finally:
243 signal(SIGINT, sig)
249 signal(SIGINT, sig)
244
250
245 def enter_eventloop(self):
251 def enter_eventloop(self):
246 """enter eventloop"""
252 """enter eventloop"""
247 self.log.info("entering eventloop")
253 self.log.info("entering eventloop")
248 # restore default_int_handler
254 # restore default_int_handler
249 signal(SIGINT, default_int_handler)
255 signal(SIGINT, default_int_handler)
250 while self.eventloop is not None:
256 while self.eventloop is not None:
251 try:
257 try:
252 self.eventloop(self)
258 self.eventloop(self)
253 except KeyboardInterrupt:
259 except KeyboardInterrupt:
254 # Ctrl-C shouldn't crash the kernel
260 # Ctrl-C shouldn't crash the kernel
255 self.log.error("KeyboardInterrupt caught in kernel")
261 self.log.error("KeyboardInterrupt caught in kernel")
256 continue
262 continue
257 else:
263 else:
258 # eventloop exited cleanly, this means we should stop (right?)
264 # eventloop exited cleanly, this means we should stop (right?)
259 self.eventloop = None
265 self.eventloop = None
260 break
266 break
261 self.log.info("exiting eventloop")
267 self.log.info("exiting eventloop")
262
268
263 def start(self):
269 def start(self):
264 """register dispatchers for streams"""
270 """register dispatchers for streams"""
265 self.shell.exit_now = False
271 self.shell.exit_now = False
266 if self.control_stream:
272 if self.control_stream:
267 self.control_stream.on_recv(self.dispatch_control, copy=False)
273 self.control_stream.on_recv(self.dispatch_control, copy=False)
268
274
269 def make_dispatcher(stream):
275 def make_dispatcher(stream):
270 def dispatcher(msg):
276 def dispatcher(msg):
271 return self.dispatch_shell(stream, msg)
277 return self.dispatch_shell(stream, msg)
272 return dispatcher
278 return dispatcher
273
279
274 for s in self.shell_streams:
280 for s in self.shell_streams:
275 s.on_recv(make_dispatcher(s), copy=False)
281 s.on_recv(make_dispatcher(s), copy=False)
276
282
277 # publish idle status
283 # publish idle status
278 self._publish_status('starting')
284 self._publish_status('starting')
279
285
280 def do_one_iteration(self):
286 def do_one_iteration(self):
281 """step eventloop just once"""
287 """step eventloop just once"""
282 if self.control_stream:
288 if self.control_stream:
283 self.control_stream.flush()
289 self.control_stream.flush()
284 for stream in self.shell_streams:
290 for stream in self.shell_streams:
285 # handle at most one request per iteration
291 # handle at most one request per iteration
286 stream.flush(zmq.POLLIN, 1)
292 stream.flush(zmq.POLLIN, 1)
287 stream.flush(zmq.POLLOUT)
293 stream.flush(zmq.POLLOUT)
288
294
289
295
290 def record_ports(self, ports):
296 def record_ports(self, ports):
291 """Record the ports that this kernel is using.
297 """Record the ports that this kernel is using.
292
298
293 The creator of the Kernel instance must call this methods if they
299 The creator of the Kernel instance must call this methods if they
294 want the :meth:`connect_request` method to return the port numbers.
300 want the :meth:`connect_request` method to return the port numbers.
295 """
301 """
296 self._recorded_ports = ports
302 self._recorded_ports = ports
297
303
298 #---------------------------------------------------------------------------
304 #---------------------------------------------------------------------------
299 # Kernel request handlers
305 # Kernel request handlers
300 #---------------------------------------------------------------------------
306 #---------------------------------------------------------------------------
301
307
302 def _make_metadata(self, other=None):
308 def _make_metadata(self, other=None):
303 """init metadata dict, for execute/apply_reply"""
309 """init metadata dict, for execute/apply_reply"""
304 new_md = {
310 new_md = {
305 'dependencies_met' : True,
311 'dependencies_met' : True,
306 'engine' : self.ident,
312 'engine' : self.ident,
307 'started': datetime.now(),
313 'started': datetime.now(),
308 }
314 }
309 if other:
315 if other:
310 new_md.update(other)
316 new_md.update(other)
311 return new_md
317 return new_md
312
318
313 def _publish_pyin(self, code, parent, execution_count):
319 def _publish_pyin(self, code, parent, execution_count):
314 """Publish the code request on the pyin stream."""
320 """Publish the code request on the pyin stream."""
315
321
316 self.session.send(self.iopub_socket, u'pyin',
322 self.session.send(self.iopub_socket, u'pyin',
317 {u'code':code, u'execution_count': execution_count},
323 {u'code':code, u'execution_count': execution_count},
318 parent=parent, ident=self._topic('pyin')
324 parent=parent, ident=self._topic('pyin')
319 )
325 )
320
326
321 def _publish_status(self, status, parent=None):
327 def _publish_status(self, status, parent=None):
322 """send status (busy/idle) on IOPub"""
328 """send status (busy/idle) on IOPub"""
323 self.session.send(self.iopub_socket,
329 self.session.send(self.iopub_socket,
324 u'status',
330 u'status',
325 {u'execution_state': status},
331 {u'execution_state': status},
326 parent=parent,
332 parent=parent,
327 ident=self._topic('status'),
333 ident=self._topic('status'),
328 )
334 )
329
335
330
336
331 def execute_request(self, stream, ident, parent):
337 def execute_request(self, stream, ident, parent):
332 """handle an execute_request"""
338 """handle an execute_request"""
333
339
334 self._publish_status(u'busy', parent)
340 self._publish_status(u'busy', parent)
335
341
336 try:
342 try:
337 content = parent[u'content']
343 content = parent[u'content']
338 code = content[u'code']
344 code = content[u'code']
339 silent = content[u'silent']
345 silent = content[u'silent']
340 store_history = content.get(u'store_history', not silent)
346 store_history = content.get(u'store_history', not silent)
341 except:
347 except:
342 self.log.error("Got bad msg: ")
348 self.log.error("Got bad msg: ")
343 self.log.error("%s", parent)
349 self.log.error("%s", parent)
344 return
350 return
345
351
346 md = self._make_metadata(parent['metadata'])
352 md = self._make_metadata(parent['metadata'])
347
353
348 shell = self.shell # we'll need this a lot here
354 shell = self.shell # we'll need this a lot here
349
355
350 # Replace raw_input. Note that is not sufficient to replace
356 # Replace raw_input. Note that is not sufficient to replace
351 # raw_input in the user namespace.
357 # raw_input in the user namespace.
352 if content.get('allow_stdin', False):
358 if content.get('allow_stdin', False):
353 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
359 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
354 input = lambda prompt='': eval(raw_input(prompt))
360 input = lambda prompt='': eval(raw_input(prompt))
355 else:
361 else:
356 raw_input = input = lambda prompt='' : self._no_raw_input()
362 raw_input = input = lambda prompt='' : self._no_raw_input()
357
363
358 if py3compat.PY3:
364 if py3compat.PY3:
359 self._sys_raw_input = builtin_mod.input
365 self._sys_raw_input = builtin_mod.input
360 builtin_mod.input = raw_input
366 builtin_mod.input = raw_input
361 else:
367 else:
362 self._sys_raw_input = builtin_mod.raw_input
368 self._sys_raw_input = builtin_mod.raw_input
363 self._sys_eval_input = builtin_mod.input
369 self._sys_eval_input = builtin_mod.input
364 builtin_mod.raw_input = raw_input
370 builtin_mod.raw_input = raw_input
365 builtin_mod.input = input
371 builtin_mod.input = input
366
372
367 # Set the parent message of the display hook and out streams.
373 # Set the parent message of the display hook and out streams.
368 shell.set_parent(parent)
374 shell.set_parent(parent)
369
375
370 # Re-broadcast our input for the benefit of listening clients, and
376 # Re-broadcast our input for the benefit of listening clients, and
371 # start computing output
377 # start computing output
372 if not silent:
378 if not silent:
373 self._publish_pyin(code, parent, shell.execution_count)
379 self._publish_pyin(code, parent, shell.execution_count)
374
380
375 reply_content = {}
381 reply_content = {}
376 try:
382 try:
377 # FIXME: the shell calls the exception handler itself.
383 # FIXME: the shell calls the exception handler itself.
378 shell.run_cell(code, store_history=store_history, silent=silent)
384 shell.run_cell(code, store_history=store_history, silent=silent)
379 except:
385 except:
380 status = u'error'
386 status = u'error'
381 # FIXME: this code right now isn't being used yet by default,
387 # FIXME: this code right now isn't being used yet by default,
382 # because the run_cell() call above directly fires off exception
388 # because the run_cell() call above directly fires off exception
383 # reporting. This code, therefore, is only active in the scenario
389 # reporting. This code, therefore, is only active in the scenario
384 # where runlines itself has an unhandled exception. We need to
390 # where runlines itself has an unhandled exception. We need to
385 # uniformize this, for all exception construction to come from a
391 # uniformize this, for all exception construction to come from a
386 # single location in the codbase.
392 # single location in the codbase.
387 etype, evalue, tb = sys.exc_info()
393 etype, evalue, tb = sys.exc_info()
388 tb_list = traceback.format_exception(etype, evalue, tb)
394 tb_list = traceback.format_exception(etype, evalue, tb)
389 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
395 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
390 else:
396 else:
391 status = u'ok'
397 status = u'ok'
392 finally:
398 finally:
393 # Restore raw_input.
399 # Restore raw_input.
394 if py3compat.PY3:
400 if py3compat.PY3:
395 builtin_mod.input = self._sys_raw_input
401 builtin_mod.input = self._sys_raw_input
396 else:
402 else:
397 builtin_mod.raw_input = self._sys_raw_input
403 builtin_mod.raw_input = self._sys_raw_input
398 builtin_mod.input = self._sys_eval_input
404 builtin_mod.input = self._sys_eval_input
399
405
400 reply_content[u'status'] = status
406 reply_content[u'status'] = status
401
407
402 # Return the execution counter so clients can display prompts
408 # Return the execution counter so clients can display prompts
403 reply_content['execution_count'] = shell.execution_count - 1
409 reply_content['execution_count'] = shell.execution_count - 1
404
410
405 # FIXME - fish exception info out of shell, possibly left there by
411 # FIXME - fish exception info out of shell, possibly left there by
406 # runlines. We'll need to clean up this logic later.
412 # runlines. We'll need to clean up this logic later.
407 if shell._reply_content is not None:
413 if shell._reply_content is not None:
408 reply_content.update(shell._reply_content)
414 reply_content.update(shell._reply_content)
409 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
415 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
410 reply_content['engine_info'] = e_info
416 reply_content['engine_info'] = e_info
411 # reset after use
417 # reset after use
412 shell._reply_content = None
418 shell._reply_content = None
413
419
414 if 'traceback' in reply_content:
420 if 'traceback' in reply_content:
415 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
421 self.log.info("Exception in execute request:\n%s", '\n'.join(reply_content['traceback']))
416
422
417
423
418 # At this point, we can tell whether the main code execution succeeded
424 # At this point, we can tell whether the main code execution succeeded
419 # or not. If it did, we proceed to evaluate user_variables/expressions
425 # or not. If it did, we proceed to evaluate user_variables/expressions
420 if reply_content['status'] == 'ok':
426 if reply_content['status'] == 'ok':
421 reply_content[u'user_variables'] = \
427 reply_content[u'user_variables'] = \
422 shell.user_variables(content.get(u'user_variables', []))
428 shell.user_variables(content.get(u'user_variables', []))
423 reply_content[u'user_expressions'] = \
429 reply_content[u'user_expressions'] = \
424 shell.user_expressions(content.get(u'user_expressions', {}))
430 shell.user_expressions(content.get(u'user_expressions', {}))
425 else:
431 else:
426 # If there was an error, don't even try to compute variables or
432 # If there was an error, don't even try to compute variables or
427 # expressions
433 # expressions
428 reply_content[u'user_variables'] = {}
434 reply_content[u'user_variables'] = {}
429 reply_content[u'user_expressions'] = {}
435 reply_content[u'user_expressions'] = {}
430
436
431 # Payloads should be retrieved regardless of outcome, so we can both
437 # Payloads should be retrieved regardless of outcome, so we can both
432 # recover partial output (that could have been generated early in a
438 # recover partial output (that could have been generated early in a
433 # block, before an error) and clear the payload system always.
439 # block, before an error) and clear the payload system always.
434 reply_content[u'payload'] = shell.payload_manager.read_payload()
440 reply_content[u'payload'] = shell.payload_manager.read_payload()
435 # Be agressive about clearing the payload because we don't want
441 # Be agressive about clearing the payload because we don't want
436 # it to sit in memory until the next execute_request comes in.
442 # it to sit in memory until the next execute_request comes in.
437 shell.payload_manager.clear_payload()
443 shell.payload_manager.clear_payload()
438
444
439 # Flush output before sending the reply.
445 # Flush output before sending the reply.
440 sys.stdout.flush()
446 sys.stdout.flush()
441 sys.stderr.flush()
447 sys.stderr.flush()
442 # FIXME: on rare occasions, the flush doesn't seem to make it to the
448 # FIXME: on rare occasions, the flush doesn't seem to make it to the
443 # clients... This seems to mitigate the problem, but we definitely need
449 # clients... This seems to mitigate the problem, but we definitely need
444 # to better understand what's going on.
450 # to better understand what's going on.
445 if self._execute_sleep:
451 if self._execute_sleep:
446 time.sleep(self._execute_sleep)
452 time.sleep(self._execute_sleep)
447
453
448 # Send the reply.
454 # Send the reply.
449 reply_content = json_clean(reply_content)
455 reply_content = json_clean(reply_content)
450
456
451 md['status'] = reply_content['status']
457 md['status'] = reply_content['status']
452 if reply_content['status'] == 'error' and \
458 if reply_content['status'] == 'error' and \
453 reply_content['ename'] == 'UnmetDependency':
459 reply_content['ename'] == 'UnmetDependency':
454 md['dependencies_met'] = False
460 md['dependencies_met'] = False
455
461
456 reply_msg = self.session.send(stream, u'execute_reply',
462 reply_msg = self.session.send(stream, u'execute_reply',
457 reply_content, parent, metadata=md,
463 reply_content, parent, metadata=md,
458 ident=ident)
464 ident=ident)
459
465
460 self.log.debug("%s", reply_msg)
466 self.log.debug("%s", reply_msg)
461
467
462 if not silent and reply_msg['content']['status'] == u'error':
468 if not silent and reply_msg['content']['status'] == u'error':
463 self._abort_queues()
469 self._abort_queues()
464
470
465 self._publish_status(u'idle', parent)
471 self._publish_status(u'idle', parent)
466
472
467 def complete_request(self, stream, ident, parent):
473 def complete_request(self, stream, ident, parent):
468 txt, matches = self._complete(parent)
474 txt, matches = self._complete(parent)
469 matches = {'matches' : matches,
475 matches = {'matches' : matches,
470 'matched_text' : txt,
476 'matched_text' : txt,
471 'status' : 'ok'}
477 'status' : 'ok'}
472 matches = json_clean(matches)
478 matches = json_clean(matches)
473 completion_msg = self.session.send(stream, 'complete_reply',
479 completion_msg = self.session.send(stream, 'complete_reply',
474 matches, parent, ident)
480 matches, parent, ident)
475 self.log.debug("%s", completion_msg)
481 self.log.debug("%s", completion_msg)
476
482
477 def object_info_request(self, stream, ident, parent):
483 def object_info_request(self, stream, ident, parent):
478 content = parent['content']
484 content = parent['content']
479 object_info = self.shell.object_inspect(content['oname'],
485 object_info = self.shell.object_inspect(content['oname'],
480 detail_level = content.get('detail_level', 0)
486 detail_level = content.get('detail_level', 0)
481 )
487 )
482 # Before we send this object over, we scrub it for JSON usage
488 # Before we send this object over, we scrub it for JSON usage
483 oinfo = json_clean(object_info)
489 oinfo = json_clean(object_info)
484 msg = self.session.send(stream, 'object_info_reply',
490 msg = self.session.send(stream, 'object_info_reply',
485 oinfo, parent, ident)
491 oinfo, parent, ident)
486 self.log.debug("%s", msg)
492 self.log.debug("%s", msg)
487
493
488 def history_request(self, stream, ident, parent):
494 def history_request(self, stream, ident, parent):
489 # We need to pull these out, as passing **kwargs doesn't work with
495 # We need to pull these out, as passing **kwargs doesn't work with
490 # unicode keys before Python 2.6.5.
496 # unicode keys before Python 2.6.5.
491 hist_access_type = parent['content']['hist_access_type']
497 hist_access_type = parent['content']['hist_access_type']
492 raw = parent['content']['raw']
498 raw = parent['content']['raw']
493 output = parent['content']['output']
499 output = parent['content']['output']
494 if hist_access_type == 'tail':
500 if hist_access_type == 'tail':
495 n = parent['content']['n']
501 n = parent['content']['n']
496 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
502 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
497 include_latest=True)
503 include_latest=True)
498
504
499 elif hist_access_type == 'range':
505 elif hist_access_type == 'range':
500 session = parent['content']['session']
506 session = parent['content']['session']
501 start = parent['content']['start']
507 start = parent['content']['start']
502 stop = parent['content']['stop']
508 stop = parent['content']['stop']
503 hist = self.shell.history_manager.get_range(session, start, stop,
509 hist = self.shell.history_manager.get_range(session, start, stop,
504 raw=raw, output=output)
510 raw=raw, output=output)
505
511
506 elif hist_access_type == 'search':
512 elif hist_access_type == 'search':
507 n = parent['content'].get('n')
513 n = parent['content'].get('n')
508 unique = parent['content'].get('unique', False)
514 unique = parent['content'].get('unique', False)
509 pattern = parent['content']['pattern']
515 pattern = parent['content']['pattern']
510 hist = self.shell.history_manager.search(
516 hist = self.shell.history_manager.search(
511 pattern, raw=raw, output=output, n=n, unique=unique)
517 pattern, raw=raw, output=output, n=n, unique=unique)
512
518
513 else:
519 else:
514 hist = []
520 hist = []
515 hist = list(hist)
521 hist = list(hist)
516 content = {'history' : hist}
522 content = {'history' : hist}
517 content = json_clean(content)
523 content = json_clean(content)
518 msg = self.session.send(stream, 'history_reply',
524 msg = self.session.send(stream, 'history_reply',
519 content, parent, ident)
525 content, parent, ident)
520 self.log.debug("Sending history reply with %i entries", len(hist))
526 self.log.debug("Sending history reply with %i entries", len(hist))
521
527
522 def connect_request(self, stream, ident, parent):
528 def connect_request(self, stream, ident, parent):
523 if self._recorded_ports is not None:
529 if self._recorded_ports is not None:
524 content = self._recorded_ports.copy()
530 content = self._recorded_ports.copy()
525 else:
531 else:
526 content = {}
532 content = {}
527 msg = self.session.send(stream, 'connect_reply',
533 msg = self.session.send(stream, 'connect_reply',
528 content, parent, ident)
534 content, parent, ident)
529 self.log.debug("%s", msg)
535 self.log.debug("%s", msg)
530
536
531 def kernel_info_request(self, stream, ident, parent):
537 def kernel_info_request(self, stream, ident, parent):
532 vinfo = {
538 vinfo = {
533 'protocol_version': protocol_version,
539 'protocol_version': protocol_version,
534 'ipython_version': ipython_version,
540 'ipython_version': ipython_version,
535 'language_version': language_version,
541 'language_version': language_version,
536 'language': 'python',
542 'language': 'python',
537 }
543 }
538 msg = self.session.send(stream, 'kernel_info_reply',
544 msg = self.session.send(stream, 'kernel_info_reply',
539 vinfo, parent, ident)
545 vinfo, parent, ident)
540 self.log.debug("%s", msg)
546 self.log.debug("%s", msg)
541
547
542 def shutdown_request(self, stream, ident, parent):
548 def shutdown_request(self, stream, ident, parent):
543 self.shell.exit_now = True
549 self.shell.exit_now = True
544 content = dict(status='ok')
550 content = dict(status='ok')
545 content.update(parent['content'])
551 content.update(parent['content'])
546 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
552 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
547 # same content, but different msg_id for broadcasting on IOPub
553 # same content, but different msg_id for broadcasting on IOPub
548 self._shutdown_message = self.session.msg(u'shutdown_reply',
554 self._shutdown_message = self.session.msg(u'shutdown_reply',
549 content, parent
555 content, parent
550 )
556 )
551
557
552 self._at_shutdown()
558 self._at_shutdown()
553 # call sys.exit after a short delay
559 # call sys.exit after a short delay
554 loop = ioloop.IOLoop.instance()
560 loop = ioloop.IOLoop.instance()
555 loop.add_timeout(time.time()+0.1, loop.stop)
561 loop.add_timeout(time.time()+0.1, loop.stop)
556
562
557 #---------------------------------------------------------------------------
563 #---------------------------------------------------------------------------
558 # Engine methods
564 # Engine methods
559 #---------------------------------------------------------------------------
565 #---------------------------------------------------------------------------
560
566
561 def apply_request(self, stream, ident, parent):
567 def apply_request(self, stream, ident, parent):
562 try:
568 try:
563 content = parent[u'content']
569 content = parent[u'content']
564 bufs = parent[u'buffers']
570 bufs = parent[u'buffers']
565 msg_id = parent['header']['msg_id']
571 msg_id = parent['header']['msg_id']
566 except:
572 except:
567 self.log.error("Got bad msg: %s", parent, exc_info=True)
573 self.log.error("Got bad msg: %s", parent, exc_info=True)
568 return
574 return
569
575
570 self._publish_status(u'busy', parent)
576 self._publish_status(u'busy', parent)
571
577
572 # Set the parent message of the display hook and out streams.
578 # Set the parent message of the display hook and out streams.
573 shell = self.shell
579 shell = self.shell
574 shell.set_parent(parent)
580 shell.set_parent(parent)
575
581
576 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
582 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
577 # self.iopub_socket.send(pyin_msg)
583 # self.iopub_socket.send(pyin_msg)
578 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
584 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
579 md = self._make_metadata(parent['metadata'])
585 md = self._make_metadata(parent['metadata'])
580 try:
586 try:
581 working = shell.user_ns
587 working = shell.user_ns
582
588
583 prefix = "_"+str(msg_id).replace("-","")+"_"
589 prefix = "_"+str(msg_id).replace("-","")+"_"
584
590
585 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
591 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
586
592
587 fname = getattr(f, '__name__', 'f')
593 fname = getattr(f, '__name__', 'f')
588
594
589 fname = prefix+"f"
595 fname = prefix+"f"
590 argname = prefix+"args"
596 argname = prefix+"args"
591 kwargname = prefix+"kwargs"
597 kwargname = prefix+"kwargs"
592 resultname = prefix+"result"
598 resultname = prefix+"result"
593
599
594 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
600 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
595 # print ns
601 # print ns
596 working.update(ns)
602 working.update(ns)
597 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
603 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
598 try:
604 try:
599 exec(code, shell.user_global_ns, shell.user_ns)
605 exec(code, shell.user_global_ns, shell.user_ns)
600 result = working.get(resultname)
606 result = working.get(resultname)
601 finally:
607 finally:
602 for key in ns:
608 for key in ns:
603 working.pop(key)
609 working.pop(key)
604
610
605 result_buf = serialize_object(result,
611 result_buf = serialize_object(result,
606 buffer_threshold=self.session.buffer_threshold,
612 buffer_threshold=self.session.buffer_threshold,
607 item_threshold=self.session.item_threshold,
613 item_threshold=self.session.item_threshold,
608 )
614 )
609
615
610 except:
616 except:
611 # invoke IPython traceback formatting
617 # invoke IPython traceback formatting
612 shell.showtraceback()
618 shell.showtraceback()
613 # FIXME - fish exception info out of shell, possibly left there by
619 # FIXME - fish exception info out of shell, possibly left there by
614 # run_code. We'll need to clean up this logic later.
620 # run_code. We'll need to clean up this logic later.
615 reply_content = {}
621 reply_content = {}
616 if shell._reply_content is not None:
622 if shell._reply_content is not None:
617 reply_content.update(shell._reply_content)
623 reply_content.update(shell._reply_content)
618 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
624 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
619 reply_content['engine_info'] = e_info
625 reply_content['engine_info'] = e_info
620 # reset after use
626 # reset after use
621 shell._reply_content = None
627 shell._reply_content = None
622
628
623 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
629 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
624 ident=self._topic('pyerr'))
630 ident=self._topic('pyerr'))
625 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
631 self.log.info("Exception in apply request:\n%s", '\n'.join(reply_content['traceback']))
626 result_buf = []
632 result_buf = []
627
633
628 if reply_content['ename'] == 'UnmetDependency':
634 if reply_content['ename'] == 'UnmetDependency':
629 md['dependencies_met'] = False
635 md['dependencies_met'] = False
630 else:
636 else:
631 reply_content = {'status' : 'ok'}
637 reply_content = {'status' : 'ok'}
632
638
633 # put 'ok'/'error' status in header, for scheduler introspection:
639 # put 'ok'/'error' status in header, for scheduler introspection:
634 md['status'] = reply_content['status']
640 md['status'] = reply_content['status']
635
641
636 # flush i/o
642 # flush i/o
637 sys.stdout.flush()
643 sys.stdout.flush()
638 sys.stderr.flush()
644 sys.stderr.flush()
639
645
640 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
646 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
641 parent=parent, ident=ident,buffers=result_buf, metadata=md)
647 parent=parent, ident=ident,buffers=result_buf, metadata=md)
642
648
643 self._publish_status(u'idle', parent)
649 self._publish_status(u'idle', parent)
644
650
645 #---------------------------------------------------------------------------
651 #---------------------------------------------------------------------------
646 # Control messages
652 # Control messages
647 #---------------------------------------------------------------------------
653 #---------------------------------------------------------------------------
648
654
649 def abort_request(self, stream, ident, parent):
655 def abort_request(self, stream, ident, parent):
650 """abort a specifig msg by id"""
656 """abort a specifig msg by id"""
651 msg_ids = parent['content'].get('msg_ids', None)
657 msg_ids = parent['content'].get('msg_ids', None)
652 if isinstance(msg_ids, string_types):
658 if isinstance(msg_ids, string_types):
653 msg_ids = [msg_ids]
659 msg_ids = [msg_ids]
654 if not msg_ids:
660 if not msg_ids:
655 self.abort_queues()
661 self.abort_queues()
656 for mid in msg_ids:
662 for mid in msg_ids:
657 self.aborted.add(str(mid))
663 self.aborted.add(str(mid))
658
664
659 content = dict(status='ok')
665 content = dict(status='ok')
660 reply_msg = self.session.send(stream, 'abort_reply', content=content,
666 reply_msg = self.session.send(stream, 'abort_reply', content=content,
661 parent=parent, ident=ident)
667 parent=parent, ident=ident)
662 self.log.debug("%s", reply_msg)
668 self.log.debug("%s", reply_msg)
663
669
664 def clear_request(self, stream, idents, parent):
670 def clear_request(self, stream, idents, parent):
665 """Clear our namespace."""
671 """Clear our namespace."""
666 self.shell.reset(False)
672 self.shell.reset(False)
667 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
673 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
668 content = dict(status='ok'))
674 content = dict(status='ok'))
669
675
670
676
671 #---------------------------------------------------------------------------
677 #---------------------------------------------------------------------------
672 # Protected interface
678 # Protected interface
673 #---------------------------------------------------------------------------
679 #---------------------------------------------------------------------------
674
680
675 def _wrap_exception(self, method=None):
681 def _wrap_exception(self, method=None):
676 # import here, because _wrap_exception is only used in parallel,
682 # import here, because _wrap_exception is only used in parallel,
677 # and parallel has higher min pyzmq version
683 # and parallel has higher min pyzmq version
678 from IPython.parallel.error import wrap_exception
684 from IPython.parallel.error import wrap_exception
679 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
685 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
680 content = wrap_exception(e_info)
686 content = wrap_exception(e_info)
681 return content
687 return content
682
688
683 def _topic(self, topic):
689 def _topic(self, topic):
684 """prefixed topic for IOPub messages"""
690 """prefixed topic for IOPub messages"""
685 if self.int_id >= 0:
691 if self.int_id >= 0:
686 base = "engine.%i" % self.int_id
692 base = "engine.%i" % self.int_id
687 else:
693 else:
688 base = "kernel.%s" % self.ident
694 base = "kernel.%s" % self.ident
689
695
690 return py3compat.cast_bytes("%s.%s" % (base, topic))
696 return py3compat.cast_bytes("%s.%s" % (base, topic))
691
697
692 def _abort_queues(self):
698 def _abort_queues(self):
693 for stream in self.shell_streams:
699 for stream in self.shell_streams:
694 if stream:
700 if stream:
695 self._abort_queue(stream)
701 self._abort_queue(stream)
696
702
697 def _abort_queue(self, stream):
703 def _abort_queue(self, stream):
698 poller = zmq.Poller()
704 poller = zmq.Poller()
699 poller.register(stream.socket, zmq.POLLIN)
705 poller.register(stream.socket, zmq.POLLIN)
700 while True:
706 while True:
701 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
707 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
702 if msg is None:
708 if msg is None:
703 return
709 return
704
710
705 self.log.info("Aborting:")
711 self.log.info("Aborting:")
706 self.log.info("%s", msg)
712 self.log.info("%s", msg)
707 msg_type = msg['header']['msg_type']
713 msg_type = msg['header']['msg_type']
708 reply_type = msg_type.split('_')[0] + '_reply'
714 reply_type = msg_type.split('_')[0] + '_reply'
709
715
710 status = {'status' : 'aborted'}
716 status = {'status' : 'aborted'}
711 md = {'engine' : self.ident}
717 md = {'engine' : self.ident}
712 md.update(status)
718 md.update(status)
713 reply_msg = self.session.send(stream, reply_type, metadata=md,
719 reply_msg = self.session.send(stream, reply_type, metadata=md,
714 content=status, parent=msg, ident=idents)
720 content=status, parent=msg, ident=idents)
715 self.log.debug("%s", reply_msg)
721 self.log.debug("%s", reply_msg)
716 # We need to wait a bit for requests to come in. This can probably
722 # We need to wait a bit for requests to come in. This can probably
717 # be set shorter for true asynchronous clients.
723 # be set shorter for true asynchronous clients.
718 poller.poll(50)
724 poller.poll(50)
719
725
720
726
721 def _no_raw_input(self):
727 def _no_raw_input(self):
722 """Raise StdinNotImplentedError if active frontend doesn't support
728 """Raise StdinNotImplentedError if active frontend doesn't support
723 stdin."""
729 stdin."""
724 raise StdinNotImplementedError("raw_input was called, but this "
730 raise StdinNotImplementedError("raw_input was called, but this "
725 "frontend does not support stdin.")
731 "frontend does not support stdin.")
726
732
727 def _raw_input(self, prompt, ident, parent):
733 def _raw_input(self, prompt, ident, parent):
728 # Flush output before making the request.
734 # Flush output before making the request.
729 sys.stderr.flush()
735 sys.stderr.flush()
730 sys.stdout.flush()
736 sys.stdout.flush()
731 # flush the stdin socket, to purge stale replies
737 # flush the stdin socket, to purge stale replies
732 while True:
738 while True:
733 try:
739 try:
734 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
740 self.stdin_socket.recv_multipart(zmq.NOBLOCK)
735 except zmq.ZMQError as e:
741 except zmq.ZMQError as e:
736 if e.errno == zmq.EAGAIN:
742 if e.errno == zmq.EAGAIN:
737 break
743 break
738 else:
744 else:
739 raise
745 raise
740
746
741 # Send the input request.
747 # Send the input request.
742 content = json_clean(dict(prompt=prompt))
748 content = json_clean(dict(prompt=prompt))
743 self.session.send(self.stdin_socket, u'input_request', content, parent,
749 self.session.send(self.stdin_socket, u'input_request', content, parent,
744 ident=ident)
750 ident=ident)
745
751
746 # Await a response.
752 # Await a response.
747 while True:
753 while True:
748 try:
754 try:
749 ident, reply = self.session.recv(self.stdin_socket, 0)
755 ident, reply = self.session.recv(self.stdin_socket, 0)
750 except Exception:
756 except Exception:
751 self.log.warn("Invalid Message:", exc_info=True)
757 self.log.warn("Invalid Message:", exc_info=True)
752 except KeyboardInterrupt:
758 except KeyboardInterrupt:
753 # re-raise KeyboardInterrupt, to truncate traceback
759 # re-raise KeyboardInterrupt, to truncate traceback
754 raise KeyboardInterrupt
760 raise KeyboardInterrupt
755 else:
761 else:
756 break
762 break
757 try:
763 try:
758 value = py3compat.unicode_to_str(reply['content']['value'])
764 value = py3compat.unicode_to_str(reply['content']['value'])
759 except:
765 except:
760 self.log.error("Got bad raw_input reply: ")
766 self.log.error("Got bad raw_input reply: ")
761 self.log.error("%s", parent)
767 self.log.error("%s", parent)
762 value = ''
768 value = ''
763 if value == '\x04':
769 if value == '\x04':
764 # EOF
770 # EOF
765 raise EOFError
771 raise EOFError
766 return value
772 return value
767
773
768 def _complete(self, msg):
774 def _complete(self, msg):
769 c = msg['content']
775 c = msg['content']
770 try:
776 try:
771 cpos = int(c['cursor_pos'])
777 cpos = int(c['cursor_pos'])
772 except:
778 except:
773 # If we don't get something that we can convert to an integer, at
779 # If we don't get something that we can convert to an integer, at
774 # least attempt the completion guessing the cursor is at the end of
780 # least attempt the completion guessing the cursor is at the end of
775 # the text, if there's any, and otherwise of the line
781 # the text, if there's any, and otherwise of the line
776 cpos = len(c['text'])
782 cpos = len(c['text'])
777 if cpos==0:
783 if cpos==0:
778 cpos = len(c['line'])
784 cpos = len(c['line'])
779 return self.shell.complete(c['text'], c['line'], cpos)
785 return self.shell.complete(c['text'], c['line'], cpos)
780
786
781 def _at_shutdown(self):
787 def _at_shutdown(self):
782 """Actions taken at shutdown by the kernel, called by python's atexit.
788 """Actions taken at shutdown by the kernel, called by python's atexit.
783 """
789 """
784 # io.rprint("Kernel at_shutdown") # dbg
790 # io.rprint("Kernel at_shutdown") # dbg
785 if self._shutdown_message is not None:
791 if self._shutdown_message is not None:
786 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
792 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
787 self.log.debug("%s", self._shutdown_message)
793 self.log.debug("%s", self._shutdown_message)
788 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
794 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
789
795
@@ -1,480 +1,473 b''
1 """An Application for launching a kernel
1 """An Application for launching a kernel
2
2
3 Authors
3 Authors
4 -------
4 -------
5 * MinRK
5 * MinRK
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2011 The IPython Development Team
8 # Copyright (C) 2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING.txt, distributed as part of this software.
11 # the file COPYING.txt, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 from __future__ import print_function
18 from __future__ import print_function
19
19
20 # Standard library imports
20 # Standard library imports
21 import atexit
21 import atexit
22 import json
22 import json
23 import os
23 import os
24 import sys
24 import sys
25 import signal
25 import signal
26
26
27 # System library imports
27 # System library imports
28 import zmq
28 import zmq
29 from zmq.eventloop import ioloop
29 from zmq.eventloop import ioloop
30 from zmq.eventloop.zmqstream import ZMQStream
30 from zmq.eventloop.zmqstream import ZMQStream
31
31
32 # IPython imports
32 # IPython imports
33 from IPython.core.ultratb import FormattedTB
33 from IPython.core.ultratb import FormattedTB
34 from IPython.core.application import (
34 from IPython.core.application import (
35 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
35 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
36 )
36 )
37 from IPython.core.profiledir import ProfileDir
37 from IPython.core.profiledir import ProfileDir
38 from IPython.core.shellapp import (
38 from IPython.core.shellapp import (
39 InteractiveShellApp, shell_flags, shell_aliases
39 InteractiveShellApp, shell_flags, shell_aliases
40 )
40 )
41 from IPython.utils import io
41 from IPython.utils import io
42 from IPython.utils.localinterfaces import localhost
42 from IPython.utils.localinterfaces import localhost
43 from IPython.utils.path import filefind
43 from IPython.utils.path import filefind
44 from IPython.utils.py3compat import str_to_bytes
44 from IPython.utils.py3compat import str_to_bytes
45 from IPython.utils.traitlets import (
45 from IPython.utils.traitlets import (
46 Any, Instance, Dict, Unicode, Integer, Bool, CaselessStrEnum,
46 Any, Instance, Dict, Unicode, Integer, Bool, CaselessStrEnum,
47 DottedObjectName,
47 DottedObjectName,
48 )
48 )
49 from IPython.utils.importstring import import_item
49 from IPython.utils.importstring import import_item
50 from IPython.kernel import write_connection_file
50 from IPython.kernel import write_connection_file
51
51
52 # local imports
52 # local imports
53 from .heartbeat import Heartbeat
53 from .heartbeat import Heartbeat
54 from .ipkernel import Kernel
54 from .ipkernel import Kernel
55 from .parentpoller import ParentPollerUnix, ParentPollerWindows
55 from .parentpoller import ParentPollerUnix, ParentPollerWindows
56 from .session import (
56 from .session import (
57 Session, session_flags, session_aliases, default_secure,
57 Session, session_flags, session_aliases, default_secure,
58 )
58 )
59 from .zmqshell import ZMQInteractiveShell
59 from .zmqshell import ZMQInteractiveShell
60
60
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62 # Flags and Aliases
62 # Flags and Aliases
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64
64
65 kernel_aliases = dict(base_aliases)
65 kernel_aliases = dict(base_aliases)
66 kernel_aliases.update({
66 kernel_aliases.update({
67 'ip' : 'IPKernelApp.ip',
67 'ip' : 'IPKernelApp.ip',
68 'hb' : 'IPKernelApp.hb_port',
68 'hb' : 'IPKernelApp.hb_port',
69 'shell' : 'IPKernelApp.shell_port',
69 'shell' : 'IPKernelApp.shell_port',
70 'iopub' : 'IPKernelApp.iopub_port',
70 'iopub' : 'IPKernelApp.iopub_port',
71 'stdin' : 'IPKernelApp.stdin_port',
71 'stdin' : 'IPKernelApp.stdin_port',
72 'control' : 'IPKernelApp.control_port',
72 'control' : 'IPKernelApp.control_port',
73 'f' : 'IPKernelApp.connection_file',
73 'f' : 'IPKernelApp.connection_file',
74 'parent': 'IPKernelApp.parent_handle',
74 'parent': 'IPKernelApp.parent_handle',
75 'transport': 'IPKernelApp.transport',
75 'transport': 'IPKernelApp.transport',
76 })
76 })
77 if sys.platform.startswith('win'):
77 if sys.platform.startswith('win'):
78 kernel_aliases['interrupt'] = 'IPKernelApp.interrupt'
78 kernel_aliases['interrupt'] = 'IPKernelApp.interrupt'
79
79
80 kernel_flags = dict(base_flags)
80 kernel_flags = dict(base_flags)
81 kernel_flags.update({
81 kernel_flags.update({
82 'no-stdout' : (
82 'no-stdout' : (
83 {'IPKernelApp' : {'no_stdout' : True}},
83 {'IPKernelApp' : {'no_stdout' : True}},
84 "redirect stdout to the null device"),
84 "redirect stdout to the null device"),
85 'no-stderr' : (
85 'no-stderr' : (
86 {'IPKernelApp' : {'no_stderr' : True}},
86 {'IPKernelApp' : {'no_stderr' : True}},
87 "redirect stderr to the null device"),
87 "redirect stderr to the null device"),
88 'pylab' : (
88 'pylab' : (
89 {'IPKernelApp' : {'pylab' : 'auto'}},
89 {'IPKernelApp' : {'pylab' : 'auto'}},
90 """Pre-load matplotlib and numpy for interactive use with
90 """Pre-load matplotlib and numpy for interactive use with
91 the default matplotlib backend."""),
91 the default matplotlib backend."""),
92 })
92 })
93
93
94 # inherit flags&aliases for any IPython shell apps
94 # inherit flags&aliases for any IPython shell apps
95 kernel_aliases.update(shell_aliases)
95 kernel_aliases.update(shell_aliases)
96 kernel_flags.update(shell_flags)
96 kernel_flags.update(shell_flags)
97
97
98 # inherit flags&aliases for Sessions
98 # inherit flags&aliases for Sessions
99 kernel_aliases.update(session_aliases)
99 kernel_aliases.update(session_aliases)
100 kernel_flags.update(session_flags)
100 kernel_flags.update(session_flags)
101
101
102 _ctrl_c_message = """\
102 _ctrl_c_message = """\
103 NOTE: When using the `ipython kernel` entry point, Ctrl-C will not work.
103 NOTE: When using the `ipython kernel` entry point, Ctrl-C will not work.
104
104
105 To exit, you will have to explicitly quit this process, by either sending
105 To exit, you will have to explicitly quit this process, by either sending
106 "quit" from a client, or using Ctrl-\\ in UNIX-like environments.
106 "quit" from a client, or using Ctrl-\\ in UNIX-like environments.
107
107
108 To read more about this, see https://github.com/ipython/ipython/issues/2049
108 To read more about this, see https://github.com/ipython/ipython/issues/2049
109
109
110 """
110 """
111
111
112 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
113 # Application class for starting an IPython Kernel
113 # Application class for starting an IPython Kernel
114 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
115
115
116 class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):
116 class IPKernelApp(BaseIPythonApplication, InteractiveShellApp):
117 name='ipkernel'
117 name='ipkernel'
118 aliases = Dict(kernel_aliases)
118 aliases = Dict(kernel_aliases)
119 flags = Dict(kernel_flags)
119 flags = Dict(kernel_flags)
120 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
120 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
121 # the kernel class, as an importstring
121 # the kernel class, as an importstring
122 kernel_class = DottedObjectName('IPython.kernel.zmq.ipkernel.Kernel', config=True,
122 kernel_class = DottedObjectName('IPython.kernel.zmq.ipkernel.Kernel', config=True,
123 help="""The Kernel subclass to be used.
123 help="""The Kernel subclass to be used.
124
124
125 This should allow easy re-use of the IPKernelApp entry point
125 This should allow easy re-use of the IPKernelApp entry point
126 to configure and launch kernels other than IPython's own.
126 to configure and launch kernels other than IPython's own.
127 """)
127 """)
128 kernel = Any()
128 kernel = Any()
129 poller = Any() # don't restrict this even though current pollers are all Threads
129 poller = Any() # don't restrict this even though current pollers are all Threads
130 heartbeat = Instance(Heartbeat)
130 heartbeat = Instance(Heartbeat)
131 session = Instance('IPython.kernel.zmq.session.Session')
131 session = Instance('IPython.kernel.zmq.session.Session')
132 ports = Dict()
132 ports = Dict()
133
133
134 # ipkernel doesn't get its own config file
134 # ipkernel doesn't get its own config file
135 def _config_file_name_default(self):
135 def _config_file_name_default(self):
136 return 'ipython_config.py'
136 return 'ipython_config.py'
137
137
138 # inherit config file name from parent:
138 # inherit config file name from parent:
139 parent_appname = Unicode(config=True)
139 parent_appname = Unicode(config=True)
140 def _parent_appname_changed(self, name, old, new):
140 def _parent_appname_changed(self, name, old, new):
141 if self.config_file_specified:
141 if self.config_file_specified:
142 # it was manually specified, ignore
142 # it was manually specified, ignore
143 return
143 return
144 self.config_file_name = new.replace('-','_') + u'_config.py'
144 self.config_file_name = new.replace('-','_') + u'_config.py'
145 # don't let this count as specifying the config file
145 # don't let this count as specifying the config file
146 self.config_file_specified.remove(self.config_file_name)
146 self.config_file_specified.remove(self.config_file_name)
147
147
148 # connection info:
148 # connection info:
149 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
149 transport = CaselessStrEnum(['tcp', 'ipc'], default_value='tcp', config=True)
150 ip = Unicode(config=True,
150 ip = Unicode(config=True,
151 help="Set the IP or interface on which the kernel will listen.")
151 help="Set the IP or interface on which the kernel will listen.")
152 def _ip_default(self):
152 def _ip_default(self):
153 if self.transport == 'ipc':
153 if self.transport == 'ipc':
154 if self.connection_file:
154 if self.connection_file:
155 return os.path.splitext(self.abs_connection_file)[0] + '-ipc'
155 return os.path.splitext(self.abs_connection_file)[0] + '-ipc'
156 else:
156 else:
157 return 'kernel-ipc'
157 return 'kernel-ipc'
158 else:
158 else:
159 return localhost()
159 return localhost()
160
160
161 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
161 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
162 shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]")
162 shell_port = Integer(0, config=True, help="set the shell (ROUTER) port [default: random]")
163 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
163 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
164 stdin_port = Integer(0, config=True, help="set the stdin (ROUTER) port [default: random]")
164 stdin_port = Integer(0, config=True, help="set the stdin (ROUTER) port [default: random]")
165 control_port = Integer(0, config=True, help="set the control (ROUTER) port [default: random]")
165 control_port = Integer(0, config=True, help="set the control (ROUTER) port [default: random]")
166 connection_file = Unicode('', config=True,
166 connection_file = Unicode('', config=True,
167 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
167 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
168
168
169 This file will contain the IP, ports, and authentication key needed to connect
169 This file will contain the IP, ports, and authentication key needed to connect
170 clients to this kernel. By default, this file will be created in the security dir
170 clients to this kernel. By default, this file will be created in the security dir
171 of the current profile, but can be specified by absolute path.
171 of the current profile, but can be specified by absolute path.
172 """)
172 """)
173 @property
173 @property
174 def abs_connection_file(self):
174 def abs_connection_file(self):
175 if os.path.basename(self.connection_file) == self.connection_file:
175 if os.path.basename(self.connection_file) == self.connection_file:
176 return os.path.join(self.profile_dir.security_dir, self.connection_file)
176 return os.path.join(self.profile_dir.security_dir, self.connection_file)
177 else:
177 else:
178 return self.connection_file
178 return self.connection_file
179
179
180
180
181 # streams, etc.
181 # streams, etc.
182 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
182 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
183 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
183 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
184 outstream_class = DottedObjectName('IPython.kernel.zmq.iostream.OutStream',
184 outstream_class = DottedObjectName('IPython.kernel.zmq.iostream.OutStream',
185 config=True, help="The importstring for the OutStream factory")
185 config=True, help="The importstring for the OutStream factory")
186 displayhook_class = DottedObjectName('IPython.kernel.zmq.displayhook.ZMQDisplayHook',
186 displayhook_class = DottedObjectName('IPython.kernel.zmq.displayhook.ZMQDisplayHook',
187 config=True, help="The importstring for the DisplayHook factory")
187 config=True, help="The importstring for the DisplayHook factory")
188
188
189 # polling
189 # polling
190 parent_handle = Integer(0, config=True,
190 parent_handle = Integer(0, config=True,
191 help="""kill this process if its parent dies. On Windows, the argument
191 help="""kill this process if its parent dies. On Windows, the argument
192 specifies the HANDLE of the parent process, otherwise it is simply boolean.
192 specifies the HANDLE of the parent process, otherwise it is simply boolean.
193 """)
193 """)
194 interrupt = Integer(0, config=True,
194 interrupt = Integer(0, config=True,
195 help="""ONLY USED ON WINDOWS
195 help="""ONLY USED ON WINDOWS
196 Interrupt this process when the parent is signaled.
196 Interrupt this process when the parent is signaled.
197 """)
197 """)
198
198
199 def init_crash_handler(self):
199 def init_crash_handler(self):
200 # Install minimal exception handling
200 # Install minimal exception handling
201 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
201 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
202 ostream=sys.__stdout__)
202 ostream=sys.__stdout__)
203
203
204 def init_poller(self):
204 def init_poller(self):
205 if sys.platform == 'win32':
205 if sys.platform == 'win32':
206 if self.interrupt or self.parent_handle:
206 if self.interrupt or self.parent_handle:
207 self.poller = ParentPollerWindows(self.interrupt, self.parent_handle)
207 self.poller = ParentPollerWindows(self.interrupt, self.parent_handle)
208 elif self.parent_handle:
208 elif self.parent_handle:
209 self.poller = ParentPollerUnix()
209 self.poller = ParentPollerUnix()
210
210
211 def _bind_socket(self, s, port):
211 def _bind_socket(self, s, port):
212 iface = '%s://%s' % (self.transport, self.ip)
212 iface = '%s://%s' % (self.transport, self.ip)
213 if self.transport == 'tcp':
213 if self.transport == 'tcp':
214 if port <= 0:
214 if port <= 0:
215 port = s.bind_to_random_port(iface)
215 port = s.bind_to_random_port(iface)
216 else:
216 else:
217 s.bind("tcp://%s:%i" % (self.ip, port))
217 s.bind("tcp://%s:%i" % (self.ip, port))
218 elif self.transport == 'ipc':
218 elif self.transport == 'ipc':
219 if port <= 0:
219 if port <= 0:
220 port = 1
220 port = 1
221 path = "%s-%i" % (self.ip, port)
221 path = "%s-%i" % (self.ip, port)
222 while os.path.exists(path):
222 while os.path.exists(path):
223 port = port + 1
223 port = port + 1
224 path = "%s-%i" % (self.ip, port)
224 path = "%s-%i" % (self.ip, port)
225 else:
225 else:
226 path = "%s-%i" % (self.ip, port)
226 path = "%s-%i" % (self.ip, port)
227 s.bind("ipc://%s" % path)
227 s.bind("ipc://%s" % path)
228 return port
228 return port
229
229
230 def load_connection_file(self):
230 def load_connection_file(self):
231 """load ip/port/hmac config from JSON connection file"""
231 """load ip/port/hmac config from JSON connection file"""
232 try:
232 try:
233 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
233 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
234 except IOError:
234 except IOError:
235 self.log.debug("Connection file not found: %s", self.connection_file)
235 self.log.debug("Connection file not found: %s", self.connection_file)
236 # This means I own it, so I will clean it up:
236 # This means I own it, so I will clean it up:
237 atexit.register(self.cleanup_connection_file)
237 atexit.register(self.cleanup_connection_file)
238 return
238 return
239 self.log.debug(u"Loading connection file %s", fname)
239 self.log.debug(u"Loading connection file %s", fname)
240 with open(fname) as f:
240 with open(fname) as f:
241 s = f.read()
241 s = f.read()
242 cfg = json.loads(s)
242 cfg = json.loads(s)
243 self.transport = cfg.get('transport', self.transport)
243 self.transport = cfg.get('transport', self.transport)
244 if self.ip == self._ip_default() and 'ip' in cfg:
244 if self.ip == self._ip_default() and 'ip' in cfg:
245 # not overridden by config or cl_args
245 # not overridden by config or cl_args
246 self.ip = cfg['ip']
246 self.ip = cfg['ip']
247 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
247 for channel in ('hb', 'shell', 'iopub', 'stdin', 'control'):
248 name = channel + '_port'
248 name = channel + '_port'
249 if getattr(self, name) == 0 and name in cfg:
249 if getattr(self, name) == 0 and name in cfg:
250 # not overridden by config or cl_args
250 # not overridden by config or cl_args
251 setattr(self, name, cfg[name])
251 setattr(self, name, cfg[name])
252 if 'key' in cfg:
252 if 'key' in cfg:
253 self.config.Session.key = str_to_bytes(cfg['key'])
253 self.config.Session.key = str_to_bytes(cfg['key'])
254
254
255 def write_connection_file(self):
255 def write_connection_file(self):
256 """write connection info to JSON file"""
256 """write connection info to JSON file"""
257 cf = self.abs_connection_file
257 cf = self.abs_connection_file
258 self.log.debug("Writing connection file: %s", cf)
258 self.log.debug("Writing connection file: %s", cf)
259 write_connection_file(cf, ip=self.ip, key=self.session.key, transport=self.transport,
259 write_connection_file(cf, ip=self.ip, key=self.session.key, transport=self.transport,
260 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
260 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
261 iopub_port=self.iopub_port, control_port=self.control_port)
261 iopub_port=self.iopub_port, control_port=self.control_port)
262
262
263 def cleanup_connection_file(self):
263 def cleanup_connection_file(self):
264 cf = self.abs_connection_file
264 cf = self.abs_connection_file
265 self.log.debug("Cleaning up connection file: %s", cf)
265 self.log.debug("Cleaning up connection file: %s", cf)
266 try:
266 try:
267 os.remove(cf)
267 os.remove(cf)
268 except (IOError, OSError):
268 except (IOError, OSError):
269 pass
269 pass
270
270
271 self.cleanup_ipc_files()
271 self.cleanup_ipc_files()
272
272
273 def cleanup_ipc_files(self):
273 def cleanup_ipc_files(self):
274 """cleanup ipc files if we wrote them"""
274 """cleanup ipc files if we wrote them"""
275 if self.transport != 'ipc':
275 if self.transport != 'ipc':
276 return
276 return
277 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port, self.control_port):
277 for port in (self.shell_port, self.iopub_port, self.stdin_port, self.hb_port, self.control_port):
278 ipcfile = "%s-%i" % (self.ip, port)
278 ipcfile = "%s-%i" % (self.ip, port)
279 try:
279 try:
280 os.remove(ipcfile)
280 os.remove(ipcfile)
281 except (IOError, OSError):
281 except (IOError, OSError):
282 pass
282 pass
283
283
284 def init_connection_file(self):
284 def init_connection_file(self):
285 if not self.connection_file:
285 if not self.connection_file:
286 self.connection_file = "kernel-%s.json"%os.getpid()
286 self.connection_file = "kernel-%s.json"%os.getpid()
287 try:
287 try:
288 self.load_connection_file()
288 self.load_connection_file()
289 except Exception:
289 except Exception:
290 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
290 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
291 self.exit(1)
291 self.exit(1)
292
292
293 def init_sockets(self):
293 def init_sockets(self):
294 # Create a context, a session, and the kernel sockets.
294 # Create a context, a session, and the kernel sockets.
295 self.log.info("Starting the kernel at pid: %i", os.getpid())
295 self.log.info("Starting the kernel at pid: %i", os.getpid())
296 context = zmq.Context.instance()
296 context = zmq.Context.instance()
297 # Uncomment this to try closing the context.
297 # Uncomment this to try closing the context.
298 # atexit.register(context.term)
298 # atexit.register(context.term)
299
299
300 self.shell_socket = context.socket(zmq.ROUTER)
300 self.shell_socket = context.socket(zmq.ROUTER)
301 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
301 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
302 self.log.debug("shell ROUTER Channel on port: %i" % self.shell_port)
302 self.log.debug("shell ROUTER Channel on port: %i" % self.shell_port)
303
303
304 self.iopub_socket = context.socket(zmq.PUB)
304 self.iopub_socket = context.socket(zmq.PUB)
305 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
305 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
306 self.log.debug("iopub PUB Channel on port: %i" % self.iopub_port)
306 self.log.debug("iopub PUB Channel on port: %i" % self.iopub_port)
307
307
308 self.stdin_socket = context.socket(zmq.ROUTER)
308 self.stdin_socket = context.socket(zmq.ROUTER)
309 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
309 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
310 self.log.debug("stdin ROUTER Channel on port: %i" % self.stdin_port)
310 self.log.debug("stdin ROUTER Channel on port: %i" % self.stdin_port)
311
311
312 self.control_socket = context.socket(zmq.ROUTER)
312 self.control_socket = context.socket(zmq.ROUTER)
313 self.control_port = self._bind_socket(self.control_socket, self.control_port)
313 self.control_port = self._bind_socket(self.control_socket, self.control_port)
314 self.log.debug("control ROUTER Channel on port: %i" % self.control_port)
314 self.log.debug("control ROUTER Channel on port: %i" % self.control_port)
315
315
316 def init_heartbeat(self):
316 def init_heartbeat(self):
317 """start the heart beating"""
317 """start the heart beating"""
318 # heartbeat doesn't share context, because it mustn't be blocked
318 # heartbeat doesn't share context, because it mustn't be blocked
319 # by the GIL, which is accessed by libzmq when freeing zero-copy messages
319 # by the GIL, which is accessed by libzmq when freeing zero-copy messages
320 hb_ctx = zmq.Context()
320 hb_ctx = zmq.Context()
321 self.heartbeat = Heartbeat(hb_ctx, (self.transport, self.ip, self.hb_port))
321 self.heartbeat = Heartbeat(hb_ctx, (self.transport, self.ip, self.hb_port))
322 self.hb_port = self.heartbeat.port
322 self.hb_port = self.heartbeat.port
323 self.log.debug("Heartbeat REP Channel on port: %i" % self.hb_port)
323 self.log.debug("Heartbeat REP Channel on port: %i" % self.hb_port)
324 self.heartbeat.start()
324 self.heartbeat.start()
325
325
326 def log_connection_info(self):
326 def log_connection_info(self):
327 """display connection info, and store ports"""
327 """display connection info, and store ports"""
328 basename = os.path.basename(self.connection_file)
328 basename = os.path.basename(self.connection_file)
329 if basename == self.connection_file or \
329 if basename == self.connection_file or \
330 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
330 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
331 # use shortname
331 # use shortname
332 tail = basename
332 tail = basename
333 if self.profile != 'default':
333 if self.profile != 'default':
334 tail += " --profile %s" % self.profile
334 tail += " --profile %s" % self.profile
335 else:
335 else:
336 tail = self.connection_file
336 tail = self.connection_file
337 lines = [
337 lines = [
338 "To connect another client to this kernel, use:",
338 "To connect another client to this kernel, use:",
339 " --existing %s" % tail,
339 " --existing %s" % tail,
340 ]
340 ]
341 # log connection info
341 # log connection info
342 # info-level, so often not shown.
342 # info-level, so often not shown.
343 # frontends should use the %connect_info magic
343 # frontends should use the %connect_info magic
344 # to see the connection info
344 # to see the connection info
345 for line in lines:
345 for line in lines:
346 self.log.info(line)
346 self.log.info(line)
347 # also raw print to the terminal if no parent_handle (`ipython kernel`)
347 # also raw print to the terminal if no parent_handle (`ipython kernel`)
348 if not self.parent_handle:
348 if not self.parent_handle:
349 io.rprint(_ctrl_c_message)
349 io.rprint(_ctrl_c_message)
350 for line in lines:
350 for line in lines:
351 io.rprint(line)
351 io.rprint(line)
352
352
353 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
353 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
354 stdin=self.stdin_port, hb=self.hb_port,
354 stdin=self.stdin_port, hb=self.hb_port,
355 control=self.control_port)
355 control=self.control_port)
356
356
357 def init_session(self):
357 def init_session(self):
358 """create our session object"""
358 """create our session object"""
359 default_secure(self.config)
359 default_secure(self.config)
360 self.session = Session(parent=self, username=u'kernel')
360 self.session = Session(parent=self, username=u'kernel')
361
361
362 def init_blackhole(self):
362 def init_blackhole(self):
363 """redirects stdout/stderr to devnull if necessary"""
363 """redirects stdout/stderr to devnull if necessary"""
364 if self.no_stdout or self.no_stderr:
364 if self.no_stdout or self.no_stderr:
365 blackhole = open(os.devnull, 'w')
365 blackhole = open(os.devnull, 'w')
366 if self.no_stdout:
366 if self.no_stdout:
367 sys.stdout = sys.__stdout__ = blackhole
367 sys.stdout = sys.__stdout__ = blackhole
368 if self.no_stderr:
368 if self.no_stderr:
369 sys.stderr = sys.__stderr__ = blackhole
369 sys.stderr = sys.__stderr__ = blackhole
370
370
371 def init_io(self):
371 def init_io(self):
372 """Redirect input streams and set a display hook."""
372 """Redirect input streams and set a display hook."""
373 if self.outstream_class:
373 if self.outstream_class:
374 outstream_factory = import_item(str(self.outstream_class))
374 outstream_factory = import_item(str(self.outstream_class))
375 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
375 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
376 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
376 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
377 if self.displayhook_class:
377 if self.displayhook_class:
378 displayhook_factory = import_item(str(self.displayhook_class))
378 displayhook_factory = import_item(str(self.displayhook_class))
379 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
379 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
380
380
381 def init_signal(self):
381 def init_signal(self):
382 signal.signal(signal.SIGINT, signal.SIG_IGN)
382 signal.signal(signal.SIGINT, signal.SIG_IGN)
383
383
384 def init_kernel(self):
384 def init_kernel(self):
385 """Create the Kernel object itself"""
385 """Create the Kernel object itself"""
386 shell_stream = ZMQStream(self.shell_socket)
386 shell_stream = ZMQStream(self.shell_socket)
387 control_stream = ZMQStream(self.control_socket)
387 control_stream = ZMQStream(self.control_socket)
388
388
389 kernel_factory = import_item(str(self.kernel_class))
389 kernel_factory = import_item(str(self.kernel_class))
390
390
391 kernel = kernel_factory(parent=self, session=self.session,
391 kernel = kernel_factory(parent=self, session=self.session,
392 shell_streams=[shell_stream, control_stream],
392 shell_streams=[shell_stream, control_stream],
393 iopub_socket=self.iopub_socket,
393 iopub_socket=self.iopub_socket,
394 stdin_socket=self.stdin_socket,
394 stdin_socket=self.stdin_socket,
395 log=self.log,
395 log=self.log,
396 profile_dir=self.profile_dir,
396 profile_dir=self.profile_dir,
397 user_ns=self.user_ns,
397 user_ns=self.user_ns,
398 )
398 )
399 kernel.record_ports(self.ports)
399 kernel.record_ports(self.ports)
400 self.kernel = kernel
400 self.kernel = kernel
401
401
402 def init_gui_pylab(self):
402 def init_gui_pylab(self):
403 """Enable GUI event loop integration, taking pylab into account."""
403 """Enable GUI event loop integration, taking pylab into account."""
404
404
405 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
405 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
406 # to ensure that any exception is printed straight to stderr.
406 # to ensure that any exception is printed straight to stderr.
407 # Normally _showtraceback associates the reply with an execution,
407 # Normally _showtraceback associates the reply with an execution,
408 # which means frontends will never draw it, as this exception
408 # which means frontends will never draw it, as this exception
409 # is not associated with any execute request.
409 # is not associated with any execute request.
410
410
411 shell = self.shell
411 shell = self.shell
412 _showtraceback = shell._showtraceback
412 _showtraceback = shell._showtraceback
413 try:
413 try:
414 # replace pyerr-sending traceback with stderr
414 # replace pyerr-sending traceback with stderr
415 def print_tb(etype, evalue, stb):
415 def print_tb(etype, evalue, stb):
416 print ("GUI event loop or pylab initialization failed",
416 print ("GUI event loop or pylab initialization failed",
417 file=io.stderr)
417 file=io.stderr)
418 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
418 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
419 shell._showtraceback = print_tb
419 shell._showtraceback = print_tb
420 InteractiveShellApp.init_gui_pylab(self)
420 InteractiveShellApp.init_gui_pylab(self)
421 finally:
421 finally:
422 shell._showtraceback = _showtraceback
422 shell._showtraceback = _showtraceback
423
423
424 def init_shell(self):
424 def init_shell(self):
425 self.shell = self.kernel.shell
425 self.shell = self.kernel.shell
426 self.shell.configurables.append(self)
426 self.shell.configurables.append(self)
427
427
428 def init_osx(self):
429 if sys.platform != 'darwin':
430 return
431 from IPython.utils.darwin import disable_app_nap
432 self._activity = disable_app_nap(self.log.warn)
433
434 @catch_config_error
428 @catch_config_error
435 def initialize(self, argv=None):
429 def initialize(self, argv=None):
436 super(IPKernelApp, self).initialize(argv)
430 super(IPKernelApp, self).initialize(argv)
437 self.init_blackhole()
431 self.init_blackhole()
438 self.init_connection_file()
432 self.init_connection_file()
439 self.init_session()
433 self.init_session()
440 self.init_poller()
434 self.init_poller()
441 self.init_sockets()
435 self.init_sockets()
442 self.init_heartbeat()
436 self.init_heartbeat()
443 # writing/displaying connection info must be *after* init_sockets/heartbeat
437 # writing/displaying connection info must be *after* init_sockets/heartbeat
444 self.log_connection_info()
438 self.log_connection_info()
445 self.write_connection_file()
439 self.write_connection_file()
446 self.init_io()
440 self.init_io()
447 self.init_signal()
441 self.init_signal()
448 self.init_osx()
449 self.init_kernel()
442 self.init_kernel()
450 # shell init steps
443 # shell init steps
451 self.init_path()
444 self.init_path()
452 self.init_shell()
445 self.init_shell()
453 self.init_gui_pylab()
446 self.init_gui_pylab()
454 self.init_extensions()
447 self.init_extensions()
455 self.init_code()
448 self.init_code()
456 # flush stdout/stderr, so that anything written to these streams during
449 # flush stdout/stderr, so that anything written to these streams during
457 # initialization do not get associated with the first execution request
450 # initialization do not get associated with the first execution request
458 sys.stdout.flush()
451 sys.stdout.flush()
459 sys.stderr.flush()
452 sys.stderr.flush()
460
453
461 def start(self):
454 def start(self):
462 if self.poller is not None:
455 if self.poller is not None:
463 self.poller.start()
456 self.poller.start()
464 self.kernel.start()
457 self.kernel.start()
465 try:
458 try:
466 ioloop.IOLoop.instance().start()
459 ioloop.IOLoop.instance().start()
467 except KeyboardInterrupt:
460 except KeyboardInterrupt:
468 pass
461 pass
469
462
470 launch_new_instance = IPKernelApp.launch_instance
463 launch_new_instance = IPKernelApp.launch_instance
471
464
472 def main():
465 def main():
473 """Run an IPKernel as an application"""
466 """Run an IPKernel as an application"""
474 app = IPKernelApp.instance()
467 app = IPKernelApp.instance()
475 app.initialize()
468 app.initialize()
476 app.start()
469 app.start()
477
470
478
471
479 if __name__ == '__main__':
472 if __name__ == '__main__':
480 main()
473 main()
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now