##// END OF EJS Templates
Merge pull request #4453 from minrk/appnap...
Min RK -
r13730:a711e50f merge
parent child Browse files
Show More
@@ -0,0 +1,15 b''
1
2 try:
3 from appnope import *
4 except ImportError:
5 __version__ = '0.0.5'
6 import sys
7 import platform
8 from distutils.version import LooseVersion as V
9
10 if sys.platform != "darwin" or V(platform.mac_ver()[0]) < V("10.9"):
11 from _dummy import *
12 else:
13 from ._nope import *
14
15 del sys, platform, V
@@ -0,0 +1,30 b''
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2013 Min RK
3 #
4 # Distributed under the terms of the 2-clause BSD License.
5 #-----------------------------------------------------------------------------
6
7 from contextlib import contextmanager
8
9 def beginActivityWithOptions(options, reason=""):
10 return
11
12 def endActivity(activity):
13 return
14
15 def nope():
16 return
17
18 def nap():
19 return
20
21
22 @contextmanager
23 def nope_scope(
24 options=0,
25 reason="Because Reasons"
26 ):
27 yield
28
29 def napping_allowed():
30 return True No newline at end of file
@@ -0,0 +1,126 b''
1 #-----------------------------------------------------------------------------
2 # Copyright (C) 2013 Min RK
3 #
4 # Distributed under the terms of the 2-clause BSD License.
5 #-----------------------------------------------------------------------------
6
7 from contextlib import contextmanager
8
9 import ctypes
10 import ctypes.util
11
12 objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
13
14 void_p = ctypes.c_void_p
15 ull = ctypes.c_uint64
16
17 objc.objc_getClass.restype = void_p
18 objc.sel_registerName.restype = void_p
19 objc.objc_msgSend.restype = void_p
20 objc.objc_msgSend.argtypes = [void_p, void_p]
21
22 msg = objc.objc_msgSend
23
24 def _utf8(s):
25 """ensure utf8 bytes"""
26 if not isinstance(s, bytes):
27 s = s.encode('utf8')
28 return s
29
30 def n(name):
31 """create a selector name (for methods)"""
32 return objc.sel_registerName(_utf8(name))
33
34 def C(classname):
35 """get an ObjC Class by name"""
36 return objc.objc_getClass(_utf8(classname))
37
38 # constants from Foundation
39
40 NSActivityIdleDisplaySleepDisabled = (1 << 40)
41 NSActivityIdleSystemSleepDisabled = (1 << 20)
42 NSActivitySuddenTerminationDisabled = (1 << 14)
43 NSActivityAutomaticTerminationDisabled = (1 << 15)
44 NSActivityUserInitiated = (0x00FFFFFF | NSActivityIdleSystemSleepDisabled)
45 NSActivityUserInitiatedAllowingIdleSystemSleep = (NSActivityUserInitiated & ~NSActivityIdleSystemSleepDisabled)
46 NSActivityBackground = 0x000000FF
47 NSActivityLatencyCritical = 0xFF00000000
48
49 def beginActivityWithOptions(options, reason=""):
50 """Wrapper for:
51
52 [ [ NSProcessInfo processInfo]
53 beginActivityWithOptions: (uint64)options
54 reason: (str)reason
55 ]
56 """
57 NSProcessInfo = C('NSProcessInfo')
58 NSString = C('NSString')
59
60 reason = msg(NSString, n("stringWithUTF8String:"), _utf8(reason))
61 info = msg(NSProcessInfo, n('processInfo'))
62 activity = msg(info,
63 n('beginActivityWithOptions:reason:'),
64 ull(options),
65 void_p(reason)
66 )
67 return activity
68
69 def endActivity(activity):
70 """end a process activity assertion"""
71 NSProcessInfo = C('NSProcessInfo')
72 info = msg(NSProcessInfo, n('processInfo'))
73 msg(info, n("endActivity:"), void_p(activity))
74
75 _theactivity = None
76
77 def nope():
78 """disable App Nap by setting NSActivityUserInitiatedAllowingIdleSystemSleep"""
79 global _theactivity
80 _theactivity = beginActivityWithOptions(
81 NSActivityUserInitiatedAllowingIdleSystemSleep,
82 "Because Reasons"
83 )
84
85 def nap():
86 """end the caffeinated state started by `nope`"""
87 global _theactivity
88 if _theactivity is not None:
89 endActivity(_theactivity)
90 _theactivity = None
91
92 def napping_allowed():
93 """is napping allowed?"""
94 return _theactivity is None
95
96 @contextmanager
97 def nope_scope(
98 options=NSActivityUserInitiatedAllowingIdleSystemSleep,
99 reason="Because Reasons"
100 ):
101 """context manager for beginActivityWithOptions.
102
103 Within this context, App Nap will be disabled.
104 """
105 activity = beginActivityWithOptions(options, reason)
106 try:
107 yield
108 finally:
109 endActivity(activity)
110
111 __all__ = [
112 "NSActivityIdleDisplaySleepDisabled",
113 "NSActivityIdleSystemSleepDisabled",
114 "NSActivitySuddenTerminationDisabled",
115 "NSActivityAutomaticTerminationDisabled",
116 "NSActivityUserInitiated",
117 "NSActivityUserInitiatedAllowingIdleSystemSleep",
118 "NSActivityBackground",
119 "NSActivityLatencyCritical",
120 "beginActivityWithOptions",
121 "endActivity",
122 "nope",
123 "nap",
124 "napping_allowed",
125 "nope_scope",
126 ]
@@ -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
General Comments 0
You need to be logged in to leave comments. Login now