##// END OF EJS Templates
Merge pull request #2724 from pberkes/embedded-ipython-v2...
Brian E. Granger -
r9109:98e3fdd6 merge
parent child Browse files
Show More
@@ -0,0 +1,260 b''
1 """ Defines a KernelManager that provides signals and slots.
2 """
3
4 # System library imports.
5 from IPython.external.qt import QtCore
6
7 # IPython imports.
8 from IPython.utils.traitlets import HasTraits, Type
9 from util import MetaQObjectHasTraits, SuperQObject
10
11
12 class ChannelQObject(SuperQObject):
13
14 # Emitted when the channel is started.
15 started = QtCore.Signal()
16
17 # Emitted when the channel is stopped.
18 stopped = QtCore.Signal()
19
20 #---------------------------------------------------------------------------
21 # Channel interface
22 #---------------------------------------------------------------------------
23
24 def start(self):
25 """ Reimplemented to emit signal.
26 """
27 super(ChannelQObject, self).start()
28 self.started.emit()
29
30 def stop(self):
31 """ Reimplemented to emit signal.
32 """
33 super(ChannelQObject, self).stop()
34 self.stopped.emit()
35
36 #---------------------------------------------------------------------------
37 # InProcessChannel interface
38 #---------------------------------------------------------------------------
39
40 def call_handlers_later(self, *args, **kwds):
41 """ Call the message handlers later.
42 """
43 do_later = lambda: self.call_handlers(*args, **kwds)
44 QtCore.QTimer.singleShot(0, do_later)
45
46 def process_events(self):
47 """ Process any pending GUI events.
48 """
49 QtCore.QCoreApplication.instance().processEvents()
50
51
52 class QtShellChannelMixin(ChannelQObject):
53
54 # Emitted when any message is received.
55 message_received = QtCore.Signal(object)
56
57 # Emitted when a reply has been received for the corresponding request
58 # type.
59 execute_reply = QtCore.Signal(object)
60 complete_reply = QtCore.Signal(object)
61 object_info_reply = QtCore.Signal(object)
62 history_reply = QtCore.Signal(object)
63
64 # Emitted when the first reply comes back.
65 first_reply = QtCore.Signal()
66
67 # Used by the first_reply signal logic to determine if a reply is the
68 # first.
69 _handlers_called = False
70
71 #---------------------------------------------------------------------------
72 # 'ShellSocketChannel' interface
73 #---------------------------------------------------------------------------
74
75 def call_handlers(self, msg):
76 """ Reimplemented to emit signals instead of making callbacks.
77 """
78 # Emit the generic signal.
79 self.message_received.emit(msg)
80
81 # Emit signals for specialized message types.
82 msg_type = msg['header']['msg_type']
83 signal = getattr(self, msg_type, None)
84 if signal:
85 signal.emit(msg)
86
87 if not self._handlers_called:
88 self.first_reply.emit()
89 self._handlers_called = True
90
91 #---------------------------------------------------------------------------
92 # 'QtShellChannelMixin' interface
93 #---------------------------------------------------------------------------
94
95 def reset_first_reply(self):
96 """ Reset the first_reply signal to fire again on the next reply.
97 """
98 self._handlers_called = False
99
100
101 class QtSubChannelMixin(ChannelQObject):
102
103 # Emitted when any message is received.
104 message_received = QtCore.Signal(object)
105
106 # Emitted when a message of type 'stream' is received.
107 stream_received = QtCore.Signal(object)
108
109 # Emitted when a message of type 'pyin' is received.
110 pyin_received = QtCore.Signal(object)
111
112 # Emitted when a message of type 'pyout' is received.
113 pyout_received = QtCore.Signal(object)
114
115 # Emitted when a message of type 'pyerr' is received.
116 pyerr_received = QtCore.Signal(object)
117
118 # Emitted when a message of type 'display_data' is received
119 display_data_received = QtCore.Signal(object)
120
121 # Emitted when a crash report message is received from the kernel's
122 # last-resort sys.excepthook.
123 crash_received = QtCore.Signal(object)
124
125 # Emitted when a shutdown is noticed.
126 shutdown_reply_received = QtCore.Signal(object)
127
128 #---------------------------------------------------------------------------
129 # 'SubSocketChannel' interface
130 #---------------------------------------------------------------------------
131
132 def call_handlers(self, msg):
133 """ Reimplemented to emit signals instead of making callbacks.
134 """
135 # Emit the generic signal.
136 self.message_received.emit(msg)
137 # Emit signals for specialized message types.
138 msg_type = msg['header']['msg_type']
139 signal = getattr(self, msg_type + '_received', None)
140 if signal:
141 signal.emit(msg)
142 elif msg_type in ('stdout', 'stderr'):
143 self.stream_received.emit(msg)
144
145 def flush(self):
146 """ Reimplemented to ensure that signals are dispatched immediately.
147 """
148 super(QtSubChannelMixin, self).flush()
149 QtCore.QCoreApplication.instance().processEvents()
150
151
152 class QtStdInChannelMixin(ChannelQObject):
153
154 # Emitted when any message is received.
155 message_received = QtCore.Signal(object)
156
157 # Emitted when an input request is received.
158 input_requested = QtCore.Signal(object)
159
160 #---------------------------------------------------------------------------
161 # 'StdInSocketChannel' interface
162 #---------------------------------------------------------------------------
163
164 def call_handlers(self, msg):
165 """ Reimplemented to emit signals instead of making callbacks.
166 """
167 # Emit the generic signal.
168 self.message_received.emit(msg)
169
170 # Emit signals for specialized message types.
171 msg_type = msg['header']['msg_type']
172 if msg_type == 'input_request':
173 self.input_requested.emit(msg)
174
175
176 class QtHBChannelMixin(ChannelQObject):
177
178 # Emitted when the kernel has died.
179 kernel_died = QtCore.Signal(object)
180
181 #---------------------------------------------------------------------------
182 # 'HBSocketChannel' interface
183 #---------------------------------------------------------------------------
184
185 def call_handlers(self, since_last_heartbeat):
186 """ Reimplemented to emit signals instead of making callbacks.
187 """
188 # Emit the generic signal.
189 self.kernel_died.emit(since_last_heartbeat)
190
191
192 class QtKernelManagerMixin(HasTraits, SuperQObject):
193 """ A KernelManager that provides signals and slots.
194 """
195
196 __metaclass__ = MetaQObjectHasTraits
197
198 # Emitted when the kernel manager has started listening.
199 started_kernel = QtCore.Signal()
200
201 # Emitted when the kernel manager has started listening.
202 started_channels = QtCore.Signal()
203
204 # Emitted when the kernel manager has stopped listening.
205 stopped_channels = QtCore.Signal()
206
207 # Use Qt-specific channel classes that emit signals.
208 sub_channel_class = Type(QtSubChannelMixin)
209 shell_channel_class = Type(QtShellChannelMixin)
210 stdin_channel_class = Type(QtStdInChannelMixin)
211 hb_channel_class = Type(QtHBChannelMixin)
212
213 #---------------------------------------------------------------------------
214 # 'KernelManager' interface
215 #---------------------------------------------------------------------------
216
217 #------ Kernel process management ------------------------------------------
218
219 def start_kernel(self, *args, **kw):
220 """ Reimplemented for proper heartbeat management.
221 """
222 if self._shell_channel is not None:
223 self._shell_channel.reset_first_reply()
224 super(QtKernelManagerMixin, self).start_kernel(*args, **kw)
225 self.started_kernel.emit()
226
227 #------ Channel management -------------------------------------------------
228
229 def start_channels(self, *args, **kw):
230 """ Reimplemented to emit signal.
231 """
232 super(QtKernelManagerMixin, self).start_channels(*args, **kw)
233 self.started_channels.emit()
234
235 def stop_channels(self):
236 """ Reimplemented to emit signal.
237 """
238 super(QtKernelManagerMixin, self).stop_channels()
239 self.stopped_channels.emit()
240
241 @property
242 def shell_channel(self):
243 """ Reimplemented for proper heartbeat management.
244 """
245 if self._shell_channel is None:
246 self._shell_channel = super(QtKernelManagerMixin,self).shell_channel
247 self._shell_channel.first_reply.connect(self._first_reply)
248 return self._shell_channel
249
250 #---------------------------------------------------------------------------
251 # Protected interface
252 #---------------------------------------------------------------------------
253
254 def _first_reply(self):
255 """ Unpauses the heartbeat channel when the first reply is received on
256 the execute channel. Note that this will *not* start the heartbeat
257 channel if it is not already running!
258 """
259 if self._hb_channel is not None:
260 self._hb_channel.unpause()
@@ -0,0 +1,33 b''
1 """ Defines an in-process KernelManager with signals and slots.
2 """
3
4 # Local imports.
5 from IPython.inprocess.kernelmanager import \
6 ShellInProcessChannel, SubInProcessChannel, StdInInProcessChannel, \
7 HBInProcessChannel, InProcessKernelManager
8 from IPython.utils.traitlets import Type
9 from base_kernelmanager import QtShellChannelMixin, QtSubChannelMixin, \
10 QtStdInChannelMixin, QtHBChannelMixin, QtKernelManagerMixin
11
12
13 class QtShellInProcessChannel(QtShellChannelMixin, ShellInProcessChannel):
14 pass
15
16 class QtSubInProcessChannel(QtSubChannelMixin, SubInProcessChannel):
17 pass
18
19 class QtStdInInProcessChannel(QtStdInChannelMixin, StdInInProcessChannel):
20 pass
21
22 class QtHBInProcessChannel(QtHBChannelMixin, HBInProcessChannel):
23 pass
24
25
26 class QtInProcessKernelManager(QtKernelManagerMixin, InProcessKernelManager):
27 """ An in-process KernelManager with signals and slots.
28 """
29
30 sub_channel_class = Type(QtSubInProcessChannel)
31 shell_channel_class = Type(QtShellInProcessChannel)
32 stdin_channel_class = Type(QtStdInInProcessChannel)
33 hb_channel_class = Type(QtHBInProcessChannel)
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,87 b''
1 """ Implements a fully blocking kernel manager.
2
3 Useful for test suites and blocking terminal interfaces.
4 """
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2012 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is in
9 # the file COPYING.txt, distributed as part of this software.
10 #-----------------------------------------------------------------------------
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15 from __future__ import print_function
16
17 # Standard library imports.
18 import Queue
19 from threading import Event
20
21 # Local imports.
22 from IPython.utils.io import raw_print
23 from IPython.utils.traitlets import Type
24 from kernelmanager import InProcessKernelManager, ShellInProcessChannel, \
25 SubInProcessChannel, StdInInProcessChannel
26
27 #-----------------------------------------------------------------------------
28 # Utility classes
29 #-----------------------------------------------------------------------------
30
31 class BlockingChannelMixin(object):
32
33 def __init__(self, *args, **kwds):
34 super(BlockingChannelMixin, self).__init__(*args, **kwds)
35 self._in_queue = Queue.Queue()
36
37 def call_handlers(self, msg):
38 self._in_queue.put(msg)
39
40 def get_msg(self, block=True, timeout=None):
41 """ Gets a message if there is one that is ready. """
42 return self._in_queue.get(block, timeout)
43
44 def get_msgs(self):
45 """ Get all messages that are currently ready. """
46 msgs = []
47 while True:
48 try:
49 msgs.append(self.get_msg(block=False))
50 except Queue.Empty:
51 break
52 return msgs
53
54 def msg_ready(self):
55 """ Is there a message that has been received? """
56 return not self._in_queue.empty()
57
58 #-----------------------------------------------------------------------------
59 # Blocking kernel manager
60 #-----------------------------------------------------------------------------
61
62 class BlockingShellInProcessChannel(BlockingChannelMixin, ShellInProcessChannel):
63 pass
64
65 class BlockingSubInProcessChannel(BlockingChannelMixin, SubInProcessChannel):
66 pass
67
68 class BlockingStdInInProcessChannel(BlockingChannelMixin, StdInInProcessChannel):
69
70 def call_handlers(self, msg):
71 """ Overridden for the in-process channel.
72
73 This methods simply calls raw_input directly.
74 """
75 msg_type = msg['header']['msg_type']
76 if msg_type == 'input_request':
77 _raw_input = self.manager.kernel._sys_raw_input
78 prompt = msg['content']['prompt']
79 raw_print(prompt, end='')
80 self.input(_raw_input())
81
82 class BlockingInProcessKernelManager(InProcessKernelManager):
83
84 # The classes to use for the various channels.
85 shell_channel_class = Type(BlockingShellInProcessChannel)
86 sub_channel_class = Type(BlockingSubInProcessChannel)
87 stdin_channel_class = Type(BlockingStdInInProcessChannel)
@@ -0,0 +1,176 b''
1 """ An in-process kernel. """
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2012 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 # Standard library imports
15 from contextlib import contextmanager
16 import logging
17 import sys
18
19 # Local imports.
20 from IPython.core.interactiveshell import InteractiveShellABC
21 from IPython.inprocess.socket import DummySocket
22 from IPython.utils.jsonutil import json_clean
23 from IPython.utils.traitlets import Any, Enum, Instance, List, Type
24 from IPython.zmq.ipkernel import Kernel
25 from IPython.zmq.zmqshell import ZMQInteractiveShell
26
27 #-----------------------------------------------------------------------------
28 # Main kernel class
29 #-----------------------------------------------------------------------------
30
31 class InProcessKernel(Kernel):
32
33 #-------------------------------------------------------------------------
34 # InProcessKernel interface
35 #-------------------------------------------------------------------------
36
37 # The frontends connected to this kernel.
38 frontends = List(
39 Instance('IPython.inprocess.kernelmanager.InProcessKernelManager'))
40
41 # The GUI environment that the kernel is running under. This need not be
42 # specified for the normal operation for the kernel, but is required for
43 # IPython's GUI support (including pylab). The default is 'inline' because
44 # it is safe under all GUI toolkits.
45 gui = Enum(('tk', 'gtk', 'wx', 'qt', 'qt4', 'inline'),
46 default_value='inline')
47
48 raw_input_str = Any()
49 stdout = Any()
50 stderr = Any()
51
52 #-------------------------------------------------------------------------
53 # Kernel interface
54 #-------------------------------------------------------------------------
55
56 shell_class = Type()
57 shell_streams = List()
58 control_stream = Any()
59 iopub_socket = Instance(DummySocket, ())
60 stdin_socket = Instance(DummySocket, ())
61
62 def __init__(self, **traits):
63 # When an InteractiveShell is instantiated by our base class, it binds
64 # the current values of sys.stdout and sys.stderr.
65 with self._redirected_io():
66 super(InProcessKernel, self).__init__(**traits)
67
68 self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
69 self.shell.kernel = self
70
71 def execute_request(self, stream, ident, parent):
72 """ Override for temporary IO redirection. """
73 with self._redirected_io():
74 super(InProcessKernel, self).execute_request(stream, ident, parent)
75
76 def start(self):
77 """ Override registration of dispatchers for streams. """
78 self.shell.exit_now = False
79
80 def _abort_queue(self, stream):
81 """ The in-process kernel doesn't abort requests. """
82 pass
83
84 def _raw_input(self, prompt, ident, parent):
85 # Flush output before making the request.
86 self.raw_input_str = None
87 sys.stderr.flush()
88 sys.stdout.flush()
89
90 # Send the input request.
91 content = json_clean(dict(prompt=prompt))
92 msg = self.session.msg(u'input_request', content, parent)
93 for frontend in self.frontends:
94 if frontend.session.session == parent['header']['session']:
95 frontend.stdin_channel.call_handlers(msg)
96 break
97 else:
98 logging.error('No frontend found for raw_input request')
99 return str()
100
101 # Await a response.
102 while self.raw_input_str is None:
103 frontend.stdin_channel.process_events()
104 return self.raw_input_str
105
106 #-------------------------------------------------------------------------
107 # Protected interface
108 #-------------------------------------------------------------------------
109
110 @contextmanager
111 def _redirected_io(self):
112 """ Temporarily redirect IO to the kernel.
113 """
114 sys_stdout, sys_stderr = sys.stdout, sys.stderr
115 sys.stdout, sys.stderr = self.stdout, self.stderr
116 yield
117 sys.stdout, sys.stderr = sys_stdout, sys_stderr
118
119 #------ Trait change handlers --------------------------------------------
120
121 def _io_dispatch(self):
122 """ Called when a message is sent to the IO socket.
123 """
124 ident, msg = self.session.recv(self.iopub_socket, copy=False)
125 for frontend in self.frontends:
126 frontend.sub_channel.call_handlers(msg)
127
128 #------ Trait initializers -----------------------------------------------
129
130 def _log_default(self):
131 return logging.getLogger(__name__)
132
133 def _session_default(self):
134 from IPython.zmq.session import Session
135 return Session(config=self.config)
136
137 def _shell_class_default(self):
138 return InProcessInteractiveShell
139
140 def _stdout_default(self):
141 from IPython.zmq.iostream import OutStream
142 return OutStream(self.session, self.iopub_socket, u'stdout')
143
144 def _stderr_default(self):
145 from IPython.zmq.iostream import OutStream
146 return OutStream(self.session, self.iopub_socket, u'stderr')
147
148 #-----------------------------------------------------------------------------
149 # Interactive shell subclass
150 #-----------------------------------------------------------------------------
151
152 class InProcessInteractiveShell(ZMQInteractiveShell):
153
154 kernel = Instance('IPython.inprocess.ipkernel.InProcessKernel')
155
156 #-------------------------------------------------------------------------
157 # InteractiveShell interface
158 #-------------------------------------------------------------------------
159
160 def enable_gui(self, gui=None):
161 """ Enable GUI integration for the kernel.
162 """
163 from IPython.zmq.eventloops import enable_gui
164 if not gui:
165 gui = self.kernel.gui
166 enable_gui(gui, kernel=self.kernel)
167
168 def enable_pylab(self, gui=None, import_all=True, welcome_message=False):
169 """ Activate pylab support at runtime.
170 """
171 if not gui:
172 gui = self.kernel.gui
173 super(InProcessInteractiveShell, self).enable_pylab(gui, import_all,
174 welcome_message)
175
176 InteractiveShellABC.register(InProcessInteractiveShell)
@@ -0,0 +1,443 b''
1 """ A kernel manager for in-process kernels. """
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2012 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 # Local imports.
15 from IPython.config.loader import Config
16 from IPython.inprocess.socket import DummySocket
17 from IPython.utils.traitlets import HasTraits, Any, Instance, Type
18
19 #-----------------------------------------------------------------------------
20 # Channel classes
21 #-----------------------------------------------------------------------------
22
23 class InProcessChannel(object):
24 """ Base class for in-process channels.
25 """
26
27 def __init__(self, manager):
28 super(InProcessChannel, self).__init__()
29 self.manager = manager
30 self._is_alive = False
31
32 #--------------------------------------------------------------------------
33 # Channel interface
34 #--------------------------------------------------------------------------
35
36 def is_alive(self):
37 return self._is_alive
38
39 def start(self):
40 self._is_alive = True
41
42 def stop(self):
43 self._is_alive = False
44
45 def call_handlers(self, msg):
46 """ This method is called in the main thread when a message arrives.
47
48 Subclasses should override this method to handle incoming messages.
49 """
50 raise NotImplementedError('call_handlers must be defined in a subclass.')
51
52 #--------------------------------------------------------------------------
53 # InProcessChannel interface
54 #--------------------------------------------------------------------------
55
56 def call_handlers_later(self, *args, **kwds):
57 """ Call the message handlers later.
58
59 The default implementation just calls the handlers immediately, but this
60 method exists so that GUI toolkits can defer calling the handlers until
61 after the event loop has run, as expected by GUI frontends.
62 """
63 self.call_handlers(*args, **kwds)
64
65 def process_events(self):
66 """ Process any pending GUI events.
67
68 This method will be never be called from a frontend without an event
69 loop (e.g., a terminal frontend).
70 """
71 raise NotImplementedError
72
73
74 class ShellInProcessChannel(InProcessChannel):
75 """The DEALER channel for issues request/replies to the kernel.
76 """
77
78 # flag for whether execute requests should be allowed to call raw_input
79 allow_stdin = True
80
81 #--------------------------------------------------------------------------
82 # ShellChannel interface
83 #--------------------------------------------------------------------------
84
85 def execute(self, code, silent=False, store_history=True,
86 user_variables=[], user_expressions={}, allow_stdin=None):
87 """Execute code in the kernel.
88
89 Parameters
90 ----------
91 code : str
92 A string of Python code.
93
94 silent : bool, optional (default False)
95 If set, the kernel will execute the code as quietly possible, and
96 will force store_history to be False.
97
98 store_history : bool, optional (default True)
99 If set, the kernel will store command history. This is forced
100 to be False if silent is True.
101
102 user_variables : list, optional
103 A list of variable names to pull from the user's namespace. They
104 will come back as a dict with these names as keys and their
105 :func:`repr` as values.
106
107 user_expressions : dict, optional
108 A dict mapping names to expressions to be evaluated in the user's
109 dict. The expression values are returned as strings formatted using
110 :func:`repr`.
111
112 allow_stdin : bool, optional (default self.allow_stdin)
113 Flag for whether the kernel can send stdin requests to frontends.
114
115 Some frontends (e.g. the Notebook) do not support stdin requests.
116 If raw_input is called from code executed from such a frontend, a
117 StdinNotImplementedError will be raised.
118
119 Returns
120 -------
121 The msg_id of the message sent.
122 """
123 if allow_stdin is None:
124 allow_stdin = self.allow_stdin
125 content = dict(code=code, silent=silent, store_history=store_history,
126 user_variables=user_variables,
127 user_expressions=user_expressions,
128 allow_stdin=allow_stdin)
129 msg = self.manager.session.msg('execute_request', content)
130 self._dispatch_to_kernel(msg)
131 return msg['header']['msg_id']
132
133 def complete(self, text, line, cursor_pos, block=None):
134 """Tab complete text in the kernel's namespace.
135
136 Parameters
137 ----------
138 text : str
139 The text to complete.
140 line : str
141 The full line of text that is the surrounding context for the
142 text to complete.
143 cursor_pos : int
144 The position of the cursor in the line where the completion was
145 requested.
146 block : str, optional
147 The full block of code in which the completion is being requested.
148
149 Returns
150 -------
151 The msg_id of the message sent.
152 """
153 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
154 msg = self.manager.session.msg('complete_request', content)
155 self._dispatch_to_kernel(msg)
156 return msg['header']['msg_id']
157
158 def object_info(self, oname, detail_level=0):
159 """Get metadata information about an object.
160
161 Parameters
162 ----------
163 oname : str
164 A string specifying the object name.
165 detail_level : int, optional
166 The level of detail for the introspection (0-2)
167
168 Returns
169 -------
170 The msg_id of the message sent.
171 """
172 content = dict(oname=oname, detail_level=detail_level)
173 msg = self.manager.session.msg('object_info_request', content)
174 self._dispatch_to_kernel(msg)
175 return msg['header']['msg_id']
176
177 def history(self, raw=True, output=False, hist_access_type='range', **kwds):
178 """Get entries from the history list.
179
180 Parameters
181 ----------
182 raw : bool
183 If True, return the raw input.
184 output : bool
185 If True, then return the output as well.
186 hist_access_type : str
187 'range' (fill in session, start and stop params), 'tail' (fill in n)
188 or 'search' (fill in pattern param).
189
190 session : int
191 For a range request, the session from which to get lines. Session
192 numbers are positive integers; negative ones count back from the
193 current session.
194 start : int
195 The first line number of a history range.
196 stop : int
197 The final (excluded) line number of a history range.
198
199 n : int
200 The number of lines of history to get for a tail request.
201
202 pattern : str
203 The glob-syntax pattern for a search request.
204
205 Returns
206 -------
207 The msg_id of the message sent.
208 """
209 content = dict(raw=raw, output=output,
210 hist_access_type=hist_access_type, **kwds)
211 msg = self.manager.session.msg('history_request', content)
212 self._dispatch_to_kernel(msg)
213 return msg['header']['msg_id']
214
215 def shutdown(self, restart=False):
216 """ Request an immediate kernel shutdown.
217
218 A dummy method for the in-process kernel.
219 """
220 # FIXME: What to do here?
221 raise NotImplementedError('Cannot shutdown in-process kernel')
222
223 #--------------------------------------------------------------------------
224 # Protected interface
225 #--------------------------------------------------------------------------
226
227 def _dispatch_to_kernel(self, msg):
228 """ Send a message to the kernel and handle a reply.
229 """
230 kernel = self.manager.kernel
231 if kernel is None:
232 raise RuntimeError('Cannot send request. No kernel exists.')
233
234 stream = DummySocket()
235 self.manager.session.send(stream, msg)
236 msg_parts = stream.recv_multipart()
237 kernel.dispatch_shell(stream, msg_parts)
238
239 idents, reply_msg = self.manager.session.recv(stream, copy=False)
240 self.call_handlers_later(reply_msg)
241
242
243 class SubInProcessChannel(InProcessChannel):
244 """The SUB channel which listens for messages that the kernel publishes.
245 """
246
247 def flush(self, timeout=1.0):
248 """ Immediately processes all pending messages on the SUB channel.
249
250 A dummy method for the in-process kernel.
251 """
252 pass
253
254
255 class StdInInProcessChannel(InProcessChannel):
256 """ A reply channel to handle raw_input requests that the kernel makes. """
257
258 def input(self, string):
259 """ Send a string of raw input to the kernel.
260 """
261 kernel = self.manager.kernel
262 if kernel is None:
263 raise RuntimeError('Cannot send input reply. No kernel exists.')
264 kernel.raw_input_str = string
265
266
267 class HBInProcessChannel(InProcessChannel):
268 """ A dummy heartbeat channel. """
269
270 time_to_dead = 3.0
271
272 def __init__(self, *args, **kwds):
273 super(HBInProcessChannel, self).__init__(*args, **kwds)
274 self._pause = True
275
276 def pause(self):
277 """ Pause the heartbeat. """
278 self._pause = True
279
280 def unpause(self):
281 """ Unpause the heartbeat. """
282 self._pause = False
283
284 def is_beating(self):
285 """ Is the heartbeat running and responsive (and not paused). """
286 return not self._pause
287
288
289 #-----------------------------------------------------------------------------
290 # Main kernel manager class
291 #-----------------------------------------------------------------------------
292
293 class InProcessKernelManager(HasTraits):
294 """ A manager for an in-process kernel.
295
296 This class implements most of the interface of
297 ``IPython.zmq.kernelmanager.KernelManager`` and allows (asynchronous)
298 frontends to be used seamlessly with an in-process kernel.
299 """
300 # Config object for passing to child configurables
301 config = Instance(Config)
302
303 # The Session to use for building messages.
304 session = Instance('IPython.zmq.session.Session')
305 def _session_default(self):
306 from IPython.zmq.session import Session
307 return Session(config=self.config)
308
309 # The kernel process with which the KernelManager is communicating.
310 kernel = Instance('IPython.inprocess.ipkernel.InProcessKernel')
311
312 # The classes to use for the various channels.
313 shell_channel_class = Type(ShellInProcessChannel)
314 sub_channel_class = Type(SubInProcessChannel)
315 stdin_channel_class = Type(StdInInProcessChannel)
316 hb_channel_class = Type(HBInProcessChannel)
317
318 # Protected traits.
319 _shell_channel = Any
320 _sub_channel = Any
321 _stdin_channel = Any
322 _hb_channel = Any
323
324 #--------------------------------------------------------------------------
325 # Channel management methods:
326 #--------------------------------------------------------------------------
327
328 def start_channels(self, shell=True, sub=True, stdin=True, hb=True):
329 """ Starts the channels for this kernel.
330 """
331 if shell:
332 self.shell_channel.start()
333 if sub:
334 self.sub_channel.start()
335 if stdin:
336 self.stdin_channel.start()
337 self.shell_channel.allow_stdin = True
338 else:
339 self.shell_channel.allow_stdin = False
340 if hb:
341 self.hb_channel.start()
342
343 def stop_channels(self):
344 """ Stops all the running channels for this kernel.
345 """
346 if self.shell_channel.is_alive():
347 self.shell_channel.stop()
348 if self.sub_channel.is_alive():
349 self.sub_channel.stop()
350 if self.stdin_channel.is_alive():
351 self.stdin_channel.stop()
352 if self.hb_channel.is_alive():
353 self.hb_channel.stop()
354
355 @property
356 def channels_running(self):
357 """ Are any of the channels created and running? """
358 return (self.shell_channel.is_alive() or self.sub_channel.is_alive() or
359 self.stdin_channel.is_alive() or self.hb_channel.is_alive())
360
361 #--------------------------------------------------------------------------
362 # Kernel management methods:
363 #--------------------------------------------------------------------------
364
365 def start_kernel(self, **kwds):
366 """ Starts a kernel process and configures the manager to use it.
367 """
368 from IPython.inprocess.ipkernel import InProcessKernel
369 self.kernel = InProcessKernel()
370 self.kernel.frontends.append(self)
371
372 def shutdown_kernel(self):
373 """ Attempts to the stop the kernel process cleanly. If the kernel
374 cannot be stopped and the kernel is local, it is killed.
375 """
376 self.kill_kernel()
377
378 def restart_kernel(self, now=False, **kwds):
379 """ Restarts a kernel with the arguments that were used to launch it.
380
381 The 'now' parameter is ignored.
382 """
383 self.shutdown_kernel()
384 self.start_kernel(**kwds)
385
386 @property
387 def has_kernel(self):
388 """ Returns whether a kernel process has been specified for the kernel
389 manager.
390 """
391 return self.kernel is not None
392
393 def kill_kernel(self):
394 """ Kill the running kernel.
395 """
396 self.kernel.frontends.remove(self)
397 self.kernel = None
398
399 def interrupt_kernel(self):
400 """ Interrupts the kernel. """
401 raise NotImplementedError("Cannot interrupt in-process kernel.")
402
403 def signal_kernel(self, signum):
404 """ Sends a signal to the kernel. """
405 raise NotImplementedError("Cannot signal in-process kernel.")
406
407 @property
408 def is_alive(self):
409 """ Is the kernel process still running? """
410 return True
411
412 #--------------------------------------------------------------------------
413 # Channels used for communication with the kernel:
414 #--------------------------------------------------------------------------
415
416 @property
417 def shell_channel(self):
418 """Get the REQ socket channel object to make requests of the kernel."""
419 if self._shell_channel is None:
420 self._shell_channel = self.shell_channel_class(self)
421 return self._shell_channel
422
423 @property
424 def sub_channel(self):
425 """Get the SUB socket channel object."""
426 if self._sub_channel is None:
427 self._sub_channel = self.sub_channel_class(self)
428 return self._sub_channel
429
430 @property
431 def stdin_channel(self):
432 """Get the REP socket channel object to handle stdin (raw_input)."""
433 if self._stdin_channel is None:
434 self._stdin_channel = self.stdin_channel_class(self)
435 return self._stdin_channel
436
437 @property
438 def hb_channel(self):
439 """Get the heartbeat socket channel object to check that the
440 kernel is alive."""
441 if self._hb_channel is None:
442 self._hb_channel = self.hb_channel_class(self)
443 return self._hb_channel
@@ -0,0 +1,63 b''
1 """ Defines a dummy socket implementing (part of) the zmq.Socket interface. """
2
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2012 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
9
10 #-----------------------------------------------------------------------------
11 # Imports
12 #-----------------------------------------------------------------------------
13
14 # Standard library imports.
15 import abc
16 import Queue
17
18 # System library imports.
19 import zmq
20
21 # Local imports.
22 from IPython.utils.traitlets import HasTraits, Instance, Int
23
24 #-----------------------------------------------------------------------------
25 # Generic socket interface
26 #-----------------------------------------------------------------------------
27
28 class SocketABC(object):
29 __metaclass__ = abc.ABCMeta
30
31 @abc.abstractmethod
32 def recv_multipart(self, flags=0, copy=True, track=False):
33 raise NotImplementedError
34
35 @abc.abstractmethod
36 def send_multipart(self, msg_parts, flags=0, copy=True, track=False):
37 raise NotImplementedError
38
39 SocketABC.register(zmq.Socket)
40
41 #-----------------------------------------------------------------------------
42 # Dummy socket class
43 #-----------------------------------------------------------------------------
44
45 class DummySocket(HasTraits):
46 """ A dummy socket implementing (part of) the zmq.Socket interface. """
47
48 queue = Instance(Queue.Queue, ())
49 message_sent = Int(0) # Should be an Event
50
51 #-------------------------------------------------------------------------
52 # Socket interface
53 #-------------------------------------------------------------------------
54
55 def recv_multipart(self, flags=0, copy=True, track=False):
56 return self.queue.get_nowait()
57
58 def send_multipart(self, msg_parts, flags=0, copy=True, track=False):
59 msg_parts = map(zmq.Message, msg_parts)
60 self.queue.put_nowait(msg_parts)
61 self.message_sent += 1
62
63 SocketABC.register(DummySocket)
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,89 b''
1 #-------------------------------------------------------------------------------
2 # Copyright (C) 2012 The IPython Development Team
3 #
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING, distributed as part of this software.
6 #-------------------------------------------------------------------------------
7
8 #-----------------------------------------------------------------------------
9 # Imports
10 #-----------------------------------------------------------------------------
11 from __future__ import print_function
12
13 # Standard library imports
14 from StringIO import StringIO
15 import sys
16 import unittest
17
18 # Local imports
19 from IPython.inprocess.blockingkernelmanager import \
20 BlockingInProcessKernelManager
21 from IPython.inprocess.ipkernel import InProcessKernel
22 from IPython.testing.decorators import skipif_not_matplotlib
23 from IPython.utils.io import capture_output
24 from IPython.utils import py3compat
25
26 #-----------------------------------------------------------------------------
27 # Test case
28 #-----------------------------------------------------------------------------
29
30 class InProcessKernelTestCase(unittest.TestCase):
31
32 @skipif_not_matplotlib
33 def test_pylab(self):
34 """ Does pylab work in the in-process kernel?
35 """
36 km = BlockingInProcessKernelManager()
37 km.start_kernel()
38 km.shell_channel.execute('%pylab')
39 msg = get_stream_message(km)
40 self.assert_('Welcome to pylab' in msg['content']['data'])
41
42 def test_raw_input(self):
43 """ Does the in-process kernel handle raw_input correctly?
44 """
45 km = BlockingInProcessKernelManager()
46 km.start_kernel()
47
48 io = StringIO('foobar\n')
49 sys_stdin = sys.stdin
50 sys.stdin = io
51 try:
52 if py3compat.PY3:
53 km.shell_channel.execute('x = input()')
54 else:
55 km.shell_channel.execute('x = raw_input()')
56 finally:
57 sys.stdin = sys_stdin
58 self.assertEqual(km.kernel.shell.user_ns.get('x'), 'foobar')
59
60 def test_stdout(self):
61 """ Does the in-process kernel correctly capture IO?
62 """
63 kernel = InProcessKernel()
64
65 with capture_output() as io:
66 kernel.shell.run_cell('print("foo")')
67 self.assertEqual(io.stdout, 'foo\n')
68
69 km = BlockingInProcessKernelManager(kernel=kernel)
70 kernel.frontends.append(km)
71 km.shell_channel.execute('print("bar")')
72 msg = get_stream_message(km)
73 self.assertEqual(msg['content']['data'], 'bar\n')
74
75 #-----------------------------------------------------------------------------
76 # Utility functions
77 #-----------------------------------------------------------------------------
78
79 def get_stream_message(kernel_manager, timeout=5):
80 """ Gets a single stream message synchronously from the sub channel.
81 """
82 while True:
83 msg = kernel_manager.sub_channel.get_msg(timeout=timeout)
84 if msg['header']['msg_type'] == 'stream':
85 return msg
86
87
88 if __name__ == '__main__':
89 unittest.main()
@@ -0,0 +1,102 b''
1 #-------------------------------------------------------------------------------
2 # Copyright (C) 2012 The IPython Development Team
3 #
4 # Distributed under the terms of the BSD License. The full license is in
5 # the file COPYING, distributed as part of this software.
6 #-------------------------------------------------------------------------------
7
8 #-----------------------------------------------------------------------------
9 # Imports
10 #-----------------------------------------------------------------------------
11 from __future__ import print_function
12
13 # Standard library imports
14 import unittest
15
16 # Local imports
17 from IPython.inprocess.blockingkernelmanager import \
18 BlockingInProcessKernelManager
19 from IPython.inprocess.ipkernel import InProcessKernel
20
21 #-----------------------------------------------------------------------------
22 # Test case
23 #-----------------------------------------------------------------------------
24
25 class InProcessKernelManagerTestCase(unittest.TestCase):
26
27 def test_inteface(self):
28 """ Does the in-process kernel manager implement the basic KM interface?
29 """
30 km = BlockingInProcessKernelManager()
31 self.assert_(not km.channels_running)
32 self.assert_(not km.has_kernel)
33
34 km.start_channels()
35 self.assert_(km.channels_running)
36
37 km.start_kernel()
38 self.assert_(km.has_kernel)
39 self.assert_(km.kernel is not None)
40
41 old_kernel = km.kernel
42 km.restart_kernel()
43 self.assert_(km.kernel is not None)
44 self.assertNotEquals(km.kernel, old_kernel)
45
46 km.shutdown_kernel()
47 self.assert_(not km.has_kernel)
48
49 self.assertRaises(NotImplementedError, km.interrupt_kernel)
50 self.assertRaises(NotImplementedError, km.signal_kernel, 9)
51
52 km.stop_channels()
53 self.assert_(not km.channels_running)
54
55 def test_execute(self):
56 """ Does executing code in an in-process kernel work?
57 """
58 km = BlockingInProcessKernelManager()
59 km.start_kernel()
60 km.shell_channel.execute('foo = 1')
61 self.assertEquals(km.kernel.shell.user_ns['foo'], 1)
62
63 def test_complete(self):
64 """ Does requesting completion from an in-process kernel work?
65 """
66 km = BlockingInProcessKernelManager()
67 km.start_kernel()
68 km.kernel.shell.push({'my_bar': 0, 'my_baz': 1})
69 km.shell_channel.complete('my_ba', 'my_ba', 5)
70 msg = km.shell_channel.get_msg()
71 self.assertEquals(msg['header']['msg_type'], 'complete_reply')
72 self.assertEquals(sorted(msg['content']['matches']),
73 ['my_bar', 'my_baz'])
74
75 def test_object_info(self):
76 """ Does requesting object information from an in-process kernel work?
77 """
78 km = BlockingInProcessKernelManager()
79 km.start_kernel()
80 km.kernel.shell.user_ns['foo'] = 1
81 km.shell_channel.object_info('foo')
82 msg = km.shell_channel.get_msg()
83 self.assertEquals(msg['header']['msg_type'], 'object_info_reply')
84 self.assertEquals(msg['content']['name'], 'foo')
85 self.assertEquals(msg['content']['type_name'], 'int')
86
87 def test_history(self):
88 """ Does requesting history from an in-process kernel work?
89 """
90 km = BlockingInProcessKernelManager()
91 km.start_kernel()
92 km.shell_channel.execute('%who')
93 km.shell_channel.history(hist_access_type='tail', n=1)
94 msg = km.shell_channel.get_msgs()[-1]
95 self.assertEquals(msg['header']['msg_type'], 'history_reply')
96 history = msg['content']['history']
97 self.assertEquals(len(history), 1)
98 self.assertEquals(history[0][2], '%who')
99
100
101 if __name__ == '__main__':
102 unittest.main()
@@ -0,0 +1,58 b''
1 """ A simple example of using the Qt console with an in-process kernel.
2
3 We shall see how to create the frontend widget, create an in-process kernel,
4 push Python objects into the kernel's namespace, and execute code in the
5 kernel, both directly and via the frontend widget.
6 """
7
8 from IPython.inprocess.ipkernel import InProcessKernel
9 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
10 from IPython.frontend.qt.inprocess_kernelmanager import QtInProcessKernelManager
11 from IPython.lib import guisupport
12
13
14 def main():
15 app = guisupport.get_app_qt4()
16
17 # Create a kernel.
18 #
19 # Setting the GUI is not necessary for the normal operation of the kernel,
20 # but it is used for IPython GUI's integration, particularly in pylab. By
21 # default, the inline backend is used, which is safe under all toolkits.
22 #
23 # WARNING: Under no circumstances should another GUI toolkit, like wx, be
24 # used when running a Qt application. This will lead to unexpected behavior,
25 # including segfaults.
26 kernel = InProcessKernel(gui='qt4')
27
28 # Populate the kernel's namespace.
29 kernel.shell.push({'x': 0, 'y': 1, 'z': 2})
30
31 # Create a kernel manager for the frontend and register it with the kernel.
32 km = QtInProcessKernelManager(kernel=kernel)
33 km.start_channels()
34 kernel.frontends.append(km)
35
36 # Create the Qt console frontend.
37 control = RichIPythonWidget()
38 control.exit_requested.connect(app.quit)
39 control.kernel_manager = km
40 control.show()
41
42 # Execute some code directly. Note where the output appears.
43 kernel.shell.run_cell('print "x=%r, y=%r, z=%r" % (x,y,z)')
44
45 # Execute some code through the frontend (once the event loop is
46 # running). Again, note where the output appears.
47 do_later(control.execute, '%who')
48
49 guisupport.start_event_loop_qt4(app)
50
51
52 def do_later(func, *args, **kwds):
53 from IPython.external.qt import QtCore
54 QtCore.QTimer.singleShot(0, lambda: func(*args, **kwds))
55
56
57 if __name__ == '__main__':
58 main()
@@ -1,247 +1,32 b''
1 """ Defines a KernelManager that provides signals and slots.
1 """ Defines a KernelManager that provides signals and slots.
2 """
2 """
3
3
4 # System library imports.
4 # Local imports.
5 from IPython.external.qt import QtCore
6
7 # IPython imports.
8 from IPython.utils.traitlets import Type
5 from IPython.utils.traitlets import Type
9 from IPython.zmq.kernelmanager import KernelManager, SubSocketChannel, \
6 from IPython.zmq.kernelmanager import ShellSocketChannel, SubSocketChannel, \
10 ShellSocketChannel, StdInSocketChannel, HBSocketChannel
7 StdInSocketChannel, HBSocketChannel, KernelManager
11 from util import MetaQObjectHasTraits, SuperQObject
8 from base_kernelmanager import QtShellChannelMixin, QtSubChannelMixin, \
12
9 QtStdInChannelMixin, QtHBChannelMixin, QtKernelManagerMixin
13
14 class SocketChannelQObject(SuperQObject):
15
16 # Emitted when the channel is started.
17 started = QtCore.Signal()
18
19 # Emitted when the channel is stopped.
20 stopped = QtCore.Signal()
21
22 #---------------------------------------------------------------------------
23 # 'ZMQSocketChannel' interface
24 #---------------------------------------------------------------------------
25
26 def start(self):
27 """ Reimplemented to emit signal.
28 """
29 super(SocketChannelQObject, self).start()
30 self.started.emit()
31
32 def stop(self):
33 """ Reimplemented to emit signal.
34 """
35 super(SocketChannelQObject, self).stop()
36 self.stopped.emit()
37
38
39 class QtShellSocketChannel(SocketChannelQObject, ShellSocketChannel):
40
41 # Emitted when any message is received.
42 message_received = QtCore.Signal(object)
43
44 # Emitted when a reply has been received for the corresponding request
45 # type.
46 execute_reply = QtCore.Signal(object)
47 complete_reply = QtCore.Signal(object)
48 object_info_reply = QtCore.Signal(object)
49 history_reply = QtCore.Signal(object)
50
51 # Emitted when the first reply comes back.
52 first_reply = QtCore.Signal()
53
54 # Used by the first_reply signal logic to determine if a reply is the
55 # first.
56 _handlers_called = False
57
58 #---------------------------------------------------------------------------
59 # 'ShellSocketChannel' interface
60 #---------------------------------------------------------------------------
61
62 def call_handlers(self, msg):
63 """ Reimplemented to emit signals instead of making callbacks.
64 """
65 # Emit the generic signal.
66 self.message_received.emit(msg)
67
68 # Emit signals for specialized message types.
69 msg_type = msg['header']['msg_type']
70 signal = getattr(self, msg_type, None)
71 if signal:
72 signal.emit(msg)
73
74 if not self._handlers_called:
75 self.first_reply.emit()
76 self._handlers_called = True
77
78 #---------------------------------------------------------------------------
79 # 'QtShellSocketChannel' interface
80 #---------------------------------------------------------------------------
81
82 def reset_first_reply(self):
83 """ Reset the first_reply signal to fire again on the next reply.
84 """
85 self._handlers_called = False
86
87
88 class QtSubSocketChannel(SocketChannelQObject, SubSocketChannel):
89
90 # Emitted when any message is received.
91 message_received = QtCore.Signal(object)
92
93 # Emitted when a message of type 'stream' is received.
94 stream_received = QtCore.Signal(object)
95
96 # Emitted when a message of type 'pyin' is received.
97 pyin_received = QtCore.Signal(object)
98
99 # Emitted when a message of type 'pyout' is received.
100 pyout_received = QtCore.Signal(object)
101
102 # Emitted when a message of type 'pyerr' is received.
103 pyerr_received = QtCore.Signal(object)
104
10
105 # Emitted when a message of type 'display_data' is received
106 display_data_received = QtCore.Signal(object)
107
11
108 # Emitted when a crash report message is received from the kernel's
12 class QtShellSocketChannel(QtShellChannelMixin, ShellSocketChannel):
109 # last-resort sys.excepthook.
13 pass
110 crash_received = QtCore.Signal(object)
111
14
112 # Emitted when a shutdown is noticed.
15 class QtSubSocketChannel(QtSubChannelMixin, SubSocketChannel):
113 shutdown_reply_received = QtCore.Signal(object)
16 pass
114
17
115 #---------------------------------------------------------------------------
18 class QtStdInSocketChannel(QtStdInChannelMixin, StdInSocketChannel):
116 # 'SubSocketChannel' interface
19 pass
117 #---------------------------------------------------------------------------
118
20
119 def call_handlers(self, msg):
21 class QtHBSocketChannel(QtHBChannelMixin, HBSocketChannel):
120 """ Reimplemented to emit signals instead of making callbacks.
22 pass
121 """
122 # Emit the generic signal.
123 self.message_received.emit(msg)
124 # Emit signals for specialized message types.
125 msg_type = msg['header']['msg_type']
126 signal = getattr(self, msg_type + '_received', None)
127 if signal:
128 signal.emit(msg)
129 elif msg_type in ('stdout', 'stderr'):
130 self.stream_received.emit(msg)
131
23
132 def flush(self):
133 """ Reimplemented to ensure that signals are dispatched immediately.
134 """
135 super(QtSubSocketChannel, self).flush()
136 QtCore.QCoreApplication.instance().processEvents()
137
24
138
25 class QtKernelManager(QtKernelManagerMixin, KernelManager):
139 class QtStdInSocketChannel(SocketChannelQObject, StdInSocketChannel):
140
141 # Emitted when any message is received.
142 message_received = QtCore.Signal(object)
143
144 # Emitted when an input request is received.
145 input_requested = QtCore.Signal(object)
146
147 #---------------------------------------------------------------------------
148 # 'StdInSocketChannel' interface
149 #---------------------------------------------------------------------------
150
151 def call_handlers(self, msg):
152 """ Reimplemented to emit signals instead of making callbacks.
153 """
154 # Emit the generic signal.
155 self.message_received.emit(msg)
156
157 # Emit signals for specialized message types.
158 msg_type = msg['header']['msg_type']
159 if msg_type == 'input_request':
160 self.input_requested.emit(msg)
161
162
163 class QtHBSocketChannel(SocketChannelQObject, HBSocketChannel):
164
165 # Emitted when the kernel has died.
166 kernel_died = QtCore.Signal(object)
167
168 #---------------------------------------------------------------------------
169 # 'HBSocketChannel' interface
170 #---------------------------------------------------------------------------
171
172 def call_handlers(self, since_last_heartbeat):
173 """ Reimplemented to emit signals instead of making callbacks.
174 """
175 # Emit the generic signal.
176 self.kernel_died.emit(since_last_heartbeat)
177
178
179 class QtKernelManager(KernelManager, SuperQObject):
180 """ A KernelManager that provides signals and slots.
26 """ A KernelManager that provides signals and slots.
181 """
27 """
182
28
183 __metaclass__ = MetaQObjectHasTraits
184
185 # Emitted when the kernel manager has started listening.
186 started_kernel = QtCore.Signal()
187
188 # Emitted when the kernel manager has started listening.
189 started_channels = QtCore.Signal()
190
191 # Emitted when the kernel manager has stopped listening.
192 stopped_channels = QtCore.Signal()
193
194 # Use Qt-specific channel classes that emit signals.
195 sub_channel_class = Type(QtSubSocketChannel)
29 sub_channel_class = Type(QtSubSocketChannel)
196 shell_channel_class = Type(QtShellSocketChannel)
30 shell_channel_class = Type(QtShellSocketChannel)
197 stdin_channel_class = Type(QtStdInSocketChannel)
31 stdin_channel_class = Type(QtStdInSocketChannel)
198 hb_channel_class = Type(QtHBSocketChannel)
32 hb_channel_class = Type(QtHBSocketChannel)
199
200 #---------------------------------------------------------------------------
201 # 'KernelManager' interface
202 #---------------------------------------------------------------------------
203
204 #------ Kernel process management ------------------------------------------
205
206 def start_kernel(self, *args, **kw):
207 """ Reimplemented for proper heartbeat management.
208 """
209 if self._shell_channel is not None:
210 self._shell_channel.reset_first_reply()
211 super(QtKernelManager, self).start_kernel(*args, **kw)
212 self.started_kernel.emit()
213
214 #------ Channel management -------------------------------------------------
215
216 def start_channels(self, *args, **kw):
217 """ Reimplemented to emit signal.
218 """
219 super(QtKernelManager, self).start_channels(*args, **kw)
220 self.started_channels.emit()
221
222 def stop_channels(self):
223 """ Reimplemented to emit signal.
224 """
225 super(QtKernelManager, self).stop_channels()
226 self.stopped_channels.emit()
227
228 @property
229 def shell_channel(self):
230 """ Reimplemented for proper heartbeat management.
231 """
232 if self._shell_channel is None:
233 self._shell_channel = super(QtKernelManager, self).shell_channel
234 self._shell_channel.first_reply.connect(self._first_reply)
235 return self._shell_channel
236
237 #---------------------------------------------------------------------------
238 # Protected interface
239 #---------------------------------------------------------------------------
240
241 def _first_reply(self):
242 """ Unpauses the heartbeat channel when the first reply is received on
243 the execute channel. Note that this will *not* start the heartbeat
244 channel if it is not already running!
245 """
246 if self._hb_channel is not None:
247 self._hb_channel.unpause()
@@ -56,7 +56,7 b' class SuperQObject(QtCore.QObject):'
56 they don't inherit QObject.)
56 they don't inherit QObject.)
57
57
58 This class is primarily useful for attaching signals to existing non-Qt
58 This class is primarily useful for attaching signals to existing non-Qt
59 classes. See QtKernelManager for an example.
59 classes. See QtKernelManagerMixin for an example.
60 """
60 """
61
61
62 def __new__(cls, *args, **kw):
62 def __new__(cls, *args, **kw):
@@ -441,7 +441,7 b' def make_runners(inc_slow=False):'
441
441
442 # Packages to be tested via nose, that only depend on the stdlib
442 # Packages to be tested via nose, that only depend on the stdlib
443 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
443 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
444 'testing', 'utils', 'nbformat' ]
444 'testing', 'utils', 'nbformat', 'inprocess' ]
445
445
446 if have['zmq']:
446 if have['zmq']:
447 nose_pkg_names.append('zmq')
447 nose_pkg_names.append('zmq')
@@ -504,8 +504,17 b' def run_iptest():'
504 # use our plugin for doctesting. It will remove the standard doctest plugin
504 # use our plugin for doctesting. It will remove the standard doctest plugin
505 # if it finds it enabled
505 # if it finds it enabled
506 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
506 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
507 # We need a global ipython running in this process
507
508 globalipapp.start_ipython()
508 # We need a global ipython running in this process, but the special
509 # in-process group spawns its own IPython kernels, so for *that* group we
510 # must avoid also opening the global one (otherwise there's a conflict of
511 # singletons). Ultimately the solution to this problem is to refactor our
512 # assumptions about what needs to be a singleton and what doesn't (app
513 # objects should, individual shells shouldn't). But for now, this
514 # workaround allows the test suite for the inprocess module to complete.
515 if not 'IPython.inprocess' in sys.argv:
516 globalipapp.start_ipython()
517
509 # Now nose can run
518 # Now nose can run
510 TestProgram(argv=argv, addplugins=plugins)
519 TestProgram(argv=argv, addplugins=plugins)
511
520
@@ -1,9 +1,9 b''
1 """Implement a fully blocking kernel manager.
1 """ Implements a fully blocking kernel manager.
2
2
3 Useful for test suites and blocking terminal interfaces.
3 Useful for test suites and blocking terminal interfaces.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2010-2011 The IPython Development Team
6 # Copyright (C) 2010-2012 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.txt, distributed as part of this software.
9 # the file COPYING.txt, distributed as part of this software.
@@ -12,125 +12,25 b' Useful for test suites and blocking terminal interfaces.'
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 from __future__ import print_function
16
15
17 # Stdlib
16 # Local imports.
18 from Queue import Queue, Empty
17 from IPython.inprocess.blockingkernelmanager import BlockingChannelMixin
19 from threading import Event
20
21 # Our own
22 from IPython.utils import io
23 from IPython.utils.traitlets import Type
18 from IPython.utils.traitlets import Type
24
19 from kernelmanager import KernelManager, SubSocketChannel, HBSocketChannel, \
25 from .kernelmanager import (KernelManager, SubSocketChannel, HBSocketChannel,
20 ShellSocketChannel, StdInSocketChannel
26 ShellSocketChannel, StdInSocketChannel)
27
21
28 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
29 # Functions and classes
23 # Blocking kernel manager
30 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
31
25
32 class BlockingSubSocketChannel(SubSocketChannel):
26 class BlockingSubSocketChannel(BlockingChannelMixin, SubSocketChannel):
33
27 pass
34 def __init__(self, context, session, address=None):
35 super(BlockingSubSocketChannel, self).__init__(context, session,
36 address)
37 self._in_queue = Queue()
38
39 def call_handlers(self, msg):
40 #io.rprint('[[Sub]]', msg) # dbg
41 self._in_queue.put(msg)
42
43 def msg_ready(self):
44 """Is there a message that has been received?"""
45 if self._in_queue.qsize() == 0:
46 return False
47 else:
48 return True
49
50 def get_msg(self, block=True, timeout=None):
51 """Get a message if there is one that is ready."""
52 if block and timeout is None:
53 # never use timeout=None, because get
54 # becomes uninterruptible
55 timeout = 1e6
56 return self._in_queue.get(block, timeout)
57
58 def get_msgs(self):
59 """Get all messages that are currently ready."""
60 msgs = []
61 while True:
62 try:
63 msgs.append(self.get_msg(block=False))
64 except Empty:
65 break
66 return msgs
67
68
69 class BlockingShellSocketChannel(ShellSocketChannel):
70
28
71 def __init__(self, context, session, address=None):
29 class BlockingShellSocketChannel(BlockingChannelMixin, ShellSocketChannel):
72 super(BlockingShellSocketChannel, self).__init__(context, session,
30 pass
73 address)
74 self._in_queue = Queue()
75
76 def call_handlers(self, msg):
77 #io.rprint('[[Shell]]', msg) # dbg
78 self._in_queue.put(msg)
79
80 def msg_ready(self):
81 """Is there a message that has been received?"""
82 if self._in_queue.qsize() == 0:
83 return False
84 else:
85 return True
86
87 def get_msg(self, block=True, timeout=None):
88 """Get a message if there is one that is ready."""
89 if block and timeout is None:
90 # never use timeout=None, because get
91 # becomes uninterruptible
92 timeout = 1e6
93 return self._in_queue.get(block, timeout)
94
95 def get_msgs(self):
96 """Get all messages that are currently ready."""
97 msgs = []
98 while True:
99 try:
100 msgs.append(self.get_msg(block=False))
101 except Empty:
102 break
103 return msgs
104
105
106 class BlockingStdInSocketChannel(StdInSocketChannel):
107
108 def __init__(self, context, session, address=None):
109 super(BlockingStdInSocketChannel, self).__init__(context, session, address)
110 self._in_queue = Queue()
111
112 def call_handlers(self, msg):
113 #io.rprint('[[Rep]]', msg) # dbg
114 self._in_queue.put(msg)
115
116 def get_msg(self, block=True, timeout=None):
117 "Gets a message if there is one that is ready."
118 return self._in_queue.get(block, timeout)
119
120 def get_msgs(self):
121 """Get all messages that are currently ready."""
122 msgs = []
123 while True:
124 try:
125 msgs.append(self.get_msg(block=False))
126 except Empty:
127 break
128 return msgs
129
130 def msg_ready(self):
131 "Is there a message that has been received?"
132 return not self._in_queue.empty()
133
31
32 class BlockingStdInSocketChannel(BlockingChannelMixin, StdInSocketChannel):
33 pass
134
34
135 class BlockingHBSocketChannel(HBSocketChannel):
35 class BlockingHBSocketChannel(HBSocketChannel):
136
36
@@ -140,10 +40,9 b' class BlockingHBSocketChannel(HBSocketChannel):'
140 time_to_dead = 1.
40 time_to_dead = 1.
141
41
142 def call_handlers(self, since_last_heartbeat):
42 def call_handlers(self, since_last_heartbeat):
143 """pause beating on missed heartbeat"""
43 """ Pause beating on missed heartbeat. """
144 pass
44 pass
145
45
146
147 class BlockingKernelManager(KernelManager):
46 class BlockingKernelManager(KernelManager):
148
47
149 # The classes to use for the various channels.
48 # The classes to use for the various channels.
@@ -13,10 +13,9 b''
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from IPython.config import Configurable
15 from IPython.config import Configurable
16
16 from IPython.inprocess.socket import SocketABC
17 from IPython.utils.jsonutil import json_clean
17 from IPython.utils.jsonutil import json_clean
18 from IPython.utils.traitlets import Instance, Dict, CBytes
18 from IPython.utils.traitlets import Instance, Dict, CBytes
19
20 from IPython.zmq.serialize import serialize_object
19 from IPython.zmq.serialize import serialize_object
21 from IPython.zmq.session import Session, extract_header
20 from IPython.zmq.session import Session, extract_header
22
21
@@ -29,7 +28,7 b' class ZMQDataPublisher(Configurable):'
29
28
30 topic = topic = CBytes(b'datapub')
29 topic = topic = CBytes(b'datapub')
31 session = Instance(Session)
30 session = Instance(Session)
32 pub_socket = Instance('zmq.Socket')
31 pub_socket = Instance(SocketABC)
33 parent_header = Dict({})
32 parent_header = Dict({})
34
33
35 def set_parent(self, parent):
34 def set_parent(self, parent):
@@ -2,6 +2,7 b' import __builtin__'
2 import sys
2 import sys
3
3
4 from IPython.core.displayhook import DisplayHook
4 from IPython.core.displayhook import DisplayHook
5 from IPython.inprocess.socket import SocketABC
5 from IPython.utils.jsonutil import encode_images
6 from IPython.utils.jsonutil import encode_images
6 from IPython.utils.traitlets import Instance, Dict
7 from IPython.utils.traitlets import Instance, Dict
7 from session import extract_header, Session
8 from session import extract_header, Session
@@ -37,7 +38,7 b' class ZMQShellDisplayHook(DisplayHook):'
37 topic=None
38 topic=None
38
39
39 session = Instance(Session)
40 session = Instance(Session)
40 pub_socket = Instance('zmq.Socket')
41 pub_socket = Instance(SocketABC)
41 parent_header = Dict({})
42 parent_header = Dict({})
42
43
43 def set_parent(self, parent):
44 def set_parent(self, parent):
@@ -48,7 +48,8 b' from IPython.utils import py3compat'
48 from IPython.utils.frame import extract_module_locals
48 from IPython.utils.frame import extract_module_locals
49 from IPython.utils.jsonutil import json_clean
49 from IPython.utils.jsonutil import json_clean
50 from IPython.utils.traitlets import (
50 from IPython.utils.traitlets import (
51 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
51 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode,
52 Type
52 )
53 )
53
54
54 from entry_point import base_launch_kernel
55 from entry_point import base_launch_kernel
@@ -81,6 +82,8 b' class Kernel(Configurable):'
81 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
82 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
82
83
83 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
84 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
85 shell_class = Type(ZMQInteractiveShell)
86
84 session = Instance(Session)
87 session = Instance(Session)
85 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
88 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
86 shell_streams = List()
89 shell_streams = List()
@@ -134,7 +137,11 b' class Kernel(Configurable):'
134 # This is a dict of port number that the kernel is listening on. It is set
137 # This is a dict of port number that the kernel is listening on. It is set
135 # by record_ports and used by connect_request.
138 # by record_ports and used by connect_request.
136 _recorded_ports = Dict()
139 _recorded_ports = Dict()
137
140
141 # A reference to the Python builtin 'raw_input' function.
142 # (i.e., __builtin__.raw_input for Python 2.7, builtins.input for Python 3)
143 _sys_raw_input = Any()
144
138 # set of aborted msg_ids
145 # set of aborted msg_ids
139 aborted = Set()
146 aborted = Set()
140
147
@@ -143,7 +150,7 b' class Kernel(Configurable):'
143 super(Kernel, self).__init__(**kwargs)
150 super(Kernel, self).__init__(**kwargs)
144
151
145 # Initialize the InteractiveShell subclass
152 # Initialize the InteractiveShell subclass
146 self.shell = ZMQInteractiveShell.instance(config=self.config,
153 self.shell = self.shell_class.instance(config=self.config,
147 profile_dir = self.profile_dir,
154 profile_dir = self.profile_dir,
148 user_module = self.user_module,
155 user_module = self.user_module,
149 user_ns = self.user_ns,
156 user_ns = self.user_ns,
@@ -356,8 +363,10 b' class Kernel(Configurable):'
356 raw_input = lambda prompt='' : self._no_raw_input()
363 raw_input = lambda prompt='' : self._no_raw_input()
357
364
358 if py3compat.PY3:
365 if py3compat.PY3:
366 self._sys_raw_input = __builtin__.input
359 __builtin__.input = raw_input
367 __builtin__.input = raw_input
360 else:
368 else:
369 self._sys_raw_input = __builtin__.raw_input
361 __builtin__.raw_input = raw_input
370 __builtin__.raw_input = raw_input
362
371
363 # Set the parent message of the display hook and out streams.
372 # Set the parent message of the display hook and out streams.
@@ -389,6 +398,12 b' class Kernel(Configurable):'
389 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
398 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
390 else:
399 else:
391 status = u'ok'
400 status = u'ok'
401 finally:
402 # Restore raw_input.
403 if py3compat.PY3:
404 __builtin__.input = self._sys_raw_input
405 else:
406 __builtin__.raw_input = self._sys_raw_input
392
407
393 reply_content[u'status'] = status
408 reply_content[u'status'] = status
394
409
@@ -663,7 +678,6 b' class Kernel(Configurable):'
663 # Protected interface
678 # Protected interface
664 #---------------------------------------------------------------------------
679 #---------------------------------------------------------------------------
665
680
666
667 def _wrap_exception(self, method=None):
681 def _wrap_exception(self, method=None):
668 # import here, because _wrap_exception is only used in parallel,
682 # import here, because _wrap_exception is only used in parallel,
669 # and parallel has higher min pyzmq version
683 # and parallel has higher min pyzmq version
@@ -758,36 +772,6 b' class Kernel(Configurable):'
758 cpos = len(c['line'])
772 cpos = len(c['line'])
759 return self.shell.complete(c['text'], c['line'], cpos)
773 return self.shell.complete(c['text'], c['line'], cpos)
760
774
761 def _object_info(self, context):
762 symbol, leftover = self._symbol_from_context(context)
763 if symbol is not None and not leftover:
764 doc = getattr(symbol, '__doc__', '')
765 else:
766 doc = ''
767 object_info = dict(docstring = doc)
768 return object_info
769
770 def _symbol_from_context(self, context):
771 if not context:
772 return None, context
773
774 base_symbol_string = context[0]
775 symbol = self.shell.user_ns.get(base_symbol_string, None)
776 if symbol is None:
777 symbol = __builtin__.__dict__.get(base_symbol_string, None)
778 if symbol is None:
779 return None, context
780
781 context = context[1:]
782 for i, name in enumerate(context):
783 new_symbol = getattr(symbol, name, None)
784 if new_symbol is None:
785 return symbol, context[i:]
786 else:
787 symbol = new_symbol
788
789 return symbol, []
790
791 def _at_shutdown(self):
775 def _at_shutdown(self):
792 """Actions taken at shutdown by the kernel, called by python's atexit.
776 """Actions taken at shutdown by the kernel, called by python's atexit.
793 """
777 """
@@ -659,6 +659,8 b' class KernelManager(HasTraits):'
659
659
660 # The Session to use for communication with the kernel.
660 # The Session to use for communication with the kernel.
661 session = Instance(Session)
661 session = Instance(Session)
662 def _session_default(self):
663 return Session(config=self.config)
662
664
663 # The kernel process with which the KernelManager is communicating.
665 # The kernel process with which the KernelManager is communicating.
664 kernel = Instance(Popen)
666 kernel = Instance(Popen)
@@ -691,16 +693,10 b' class KernelManager(HasTraits):'
691 _stdin_channel = Any
693 _stdin_channel = Any
692 _hb_channel = Any
694 _hb_channel = Any
693 _connection_file_written=Bool(False)
695 _connection_file_written=Bool(False)
694
695 def __init__(self, **kwargs):
696 super(KernelManager, self).__init__(**kwargs)
697 if self.session is None:
698 self.session = Session(config=self.config)
699
696
700 def __del__(self):
697 def __del__(self):
701 self.cleanup_connection_file()
698 self.cleanup_connection_file()
702
699
703
704 #--------------------------------------------------------------------------
700 #--------------------------------------------------------------------------
705 # Channel management methods:
701 # Channel management methods:
706 #--------------------------------------------------------------------------
702 #--------------------------------------------------------------------------
@@ -557,11 +557,9 b' class Session(Configurable):'
557 msg : dict
557 msg : dict
558 The constructed message.
558 The constructed message.
559 """
559 """
560
560 if not isinstance(stream, zmq.Socket):
561 if not isinstance(stream, (zmq.Socket, ZMQStream)):
561 # ZMQStreams and dummy sockets do not support tracking.
562 raise TypeError("stream must be Socket or ZMQStream, not %r"%type(stream))
562 track = False
563 elif track and isinstance(stream, ZMQStream):
564 raise TypeError("ZMQStream cannot track messages")
565
563
566 if isinstance(msg_or_type, (Message, dict)):
564 if isinstance(msg_or_type, (Message, dict)):
567 # We got a Message or message dict, not a msg_type so don't
565 # We got a Message or message dict, not a msg_type so don't
@@ -34,6 +34,7 b' from IPython.core.error import UsageError'
34 from IPython.core.magics import MacroToEdit, CodeMagics
34 from IPython.core.magics import MacroToEdit, CodeMagics
35 from IPython.core.magic import magics_class, line_magic, Magics
35 from IPython.core.magic import magics_class, line_magic, Magics
36 from IPython.core.payloadpage import install_payload_page
36 from IPython.core.payloadpage import install_payload_page
37 from IPython.inprocess.socket import SocketABC
37 from IPython.lib.kernel import (
38 from IPython.lib.kernel import (
38 get_connection_file, get_connection_info, connect_qtconsole
39 get_connection_file, get_connection_info, connect_qtconsole
39 )
40 )
@@ -57,7 +58,7 b' class ZMQDisplayPublisher(DisplayPublisher):'
57 """A display publisher that publishes data using a ZeroMQ PUB socket."""
58 """A display publisher that publishes data using a ZeroMQ PUB socket."""
58
59
59 session = Instance(Session)
60 session = Instance(Session)
60 pub_socket = Instance('zmq.Socket')
61 pub_socket = Instance(SocketABC)
61 parent_header = Dict({})
62 parent_header = Dict({})
62 topic = CBytes(b'displaypub')
63 topic = CBytes(b'displaypub')
63
64
@@ -12,6 +12,17 b' especially intenting/deindenting blocks that is now bound to Ctrl+] and ctr+['
12 ``_render_traceback_()`` method which returns a list of strings, each
12 ``_render_traceback_()`` method which returns a list of strings, each
13 containing one line of the traceback.
13 containing one line of the traceback.
14
14
15 In-process kernels
16 ------------------
17
18 The Python-language frontends, particularly the Qt console, may now communicate
19 with in-process kernels, in addition to the traditional out-of-process
20 kernels. An in-process kernel permits direct access to the kernel namespace,
21 which is necessary in some applications. It should be understood, however, that
22 the in-process kernel is not robust to bad user input and will block the main
23 (GUI) thread while executing. Developers must decide on a case-by-case basis
24 whether this tradeoff is appropriate for their application.
25
15 Backwards incompatible changes
26 Backwards incompatible changes
16 ------------------------------
27 ------------------------------
17
28
General Comments 0
You need to be logged in to leave comments. Login now