##// END OF EJS Templates
BUG: GUI integration broken in the in-process kernel.
epatters -
Show More
@@ -1,131 +1,176 b''
1 """ An in-process kernel. """
1 """ An in-process kernel. """
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2012 The IPython Development Team
4 # Copyright (C) 2012 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
7 # the file COPYING, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 # Standard library imports
14 # Standard library imports
15 from contextlib import contextmanager
15 from contextlib import contextmanager
16 import logging
16 import logging
17 import sys
17 import sys
18
18
19 # Local imports.
19 # Local imports.
20 from IPython.core.interactiveshell import InteractiveShellABC
20 from IPython.inprocess.socket import DummySocket
21 from IPython.inprocess.socket import DummySocket
21 from IPython.utils.jsonutil import json_clean
22 from IPython.utils.jsonutil import json_clean
22 from IPython.utils.traitlets import Any, Instance, List
23 from IPython.utils.traitlets import Any, Enum, Instance, List, Type
23 from IPython.zmq.ipkernel import Kernel
24 from IPython.zmq.ipkernel import Kernel
25 from IPython.zmq.zmqshell import ZMQInteractiveShell
24
26
25 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
26 # Main kernel class
28 # Main kernel class
27 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
28
30
29 class InProcessKernel(Kernel):
31 class InProcessKernel(Kernel):
30
32
31 #-------------------------------------------------------------------------
33 #-------------------------------------------------------------------------
32 # InProcessKernel interface
34 # InProcessKernel interface
33 #-------------------------------------------------------------------------
35 #-------------------------------------------------------------------------
34
36
37 # The frontends connected to this kernel.
35 frontends = List(
38 frontends = List(
36 Instance('IPython.inprocess.kernelmanager.InProcessKernelManager'))
39 Instance('IPython.inprocess.kernelmanager.InProcessKernelManager'))
37
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
38 raw_input_str = Any()
48 raw_input_str = Any()
39 stdout = Any()
49 stdout = Any()
40 stderr = Any()
50 stderr = Any()
41
51
42 #-------------------------------------------------------------------------
52 #-------------------------------------------------------------------------
43 # Kernel interface
53 # Kernel interface
44 #-------------------------------------------------------------------------
54 #-------------------------------------------------------------------------
45
55
56 shell_class = Type()
46 shell_streams = List()
57 shell_streams = List()
47 control_stream = Any()
58 control_stream = Any()
48 iopub_socket = Instance(DummySocket, ())
59 iopub_socket = Instance(DummySocket, ())
49 stdin_socket = Instance(DummySocket, ())
60 stdin_socket = Instance(DummySocket, ())
50
61
51 def __init__(self, **traits):
62 def __init__(self, **traits):
52 # When an InteractiveShell is instantiated by our base class, it binds
63 # When an InteractiveShell is instantiated by our base class, it binds
53 # the current values of sys.stdout and sys.stderr.
64 # the current values of sys.stdout and sys.stderr.
54 with self._redirected_io():
65 with self._redirected_io():
55 super(InProcessKernel, self).__init__(**traits)
66 super(InProcessKernel, self).__init__(**traits)
56
67
57 self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
68 self.iopub_socket.on_trait_change(self._io_dispatch, 'message_sent')
69 self.shell.kernel = self
58
70
59 def execute_request(self, stream, ident, parent):
71 def execute_request(self, stream, ident, parent):
60 """ Override for temporary IO redirection. """
72 """ Override for temporary IO redirection. """
61 with self._redirected_io():
73 with self._redirected_io():
62 super(InProcessKernel, self).execute_request(stream, ident, parent)
74 super(InProcessKernel, self).execute_request(stream, ident, parent)
63
75
64 def start(self):
76 def start(self):
65 """ Override registration of dispatchers for streams. """
77 """ Override registration of dispatchers for streams. """
66 self.shell.exit_now = False
78 self.shell.exit_now = False
67
79
68 def _abort_queue(self, stream):
80 def _abort_queue(self, stream):
69 """ The in-process kernel doesn't abort requests. """
81 """ The in-process kernel doesn't abort requests. """
70 pass
82 pass
71
83
72 def _raw_input(self, prompt, ident, parent):
84 def _raw_input(self, prompt, ident, parent):
73 # Flush output before making the request.
85 # Flush output before making the request.
74 self.raw_input_str = None
86 self.raw_input_str = None
75 sys.stderr.flush()
87 sys.stderr.flush()
76 sys.stdout.flush()
88 sys.stdout.flush()
77
89
78 # Send the input request.
90 # Send the input request.
79 content = json_clean(dict(prompt=prompt))
91 content = json_clean(dict(prompt=prompt))
80 msg = self.session.msg(u'input_request', content, parent)
92 msg = self.session.msg(u'input_request', content, parent)
81 for frontend in self.frontends:
93 for frontend in self.frontends:
82 if frontend.session.session == parent['header']['session']:
94 if frontend.session.session == parent['header']['session']:
83 frontend.stdin_channel.call_handlers(msg)
95 frontend.stdin_channel.call_handlers(msg)
84 break
96 break
85 else:
97 else:
86 log.error('No frontend found for raw_input request')
98 log.error('No frontend found for raw_input request')
87 return str()
99 return str()
88
100
89 # Await a response.
101 # Await a response.
90 while self.raw_input_str is None:
102 while self.raw_input_str is None:
91 frontend.stdin_channel.process_events()
103 frontend.stdin_channel.process_events()
92 return self.raw_input_str
104 return self.raw_input_str
93
105
94 #-------------------------------------------------------------------------
106 #-------------------------------------------------------------------------
95 # Protected interface
107 # Protected interface
96 #-------------------------------------------------------------------------
108 #-------------------------------------------------------------------------
97
109
98 @contextmanager
110 @contextmanager
99 def _redirected_io(self):
111 def _redirected_io(self):
100 """ Temporarily redirect IO to the kernel.
112 """ Temporarily redirect IO to the kernel.
101 """
113 """
102 sys_stdout, sys_stderr = sys.stdout, sys.stderr
114 sys_stdout, sys_stderr = sys.stdout, sys.stderr
103 sys.stdout, sys.stderr = self.stdout, self.stderr
115 sys.stdout, sys.stderr = self.stdout, self.stderr
104 yield
116 yield
105 sys.stdout, sys.stderr = sys_stdout, sys_stderr
117 sys.stdout, sys.stderr = sys_stdout, sys_stderr
106
118
107 #------ Trait change handlers --------------------------------------------
119 #------ Trait change handlers --------------------------------------------
108
120
109 def _io_dispatch(self):
121 def _io_dispatch(self):
110 """ Called when a message is sent to the IO socket.
122 """ Called when a message is sent to the IO socket.
111 """
123 """
112 ident, msg = self.session.recv(self.iopub_socket, copy=False)
124 ident, msg = self.session.recv(self.iopub_socket, copy=False)
113 for frontend in self.frontends:
125 for frontend in self.frontends:
114 frontend.sub_channel.call_handlers(msg)
126 frontend.sub_channel.call_handlers(msg)
115
127
116 #------ Trait initializers -----------------------------------------------
128 #------ Trait initializers -----------------------------------------------
117
129
118 def _log_default(self):
130 def _log_default(self):
119 return logging.getLogger(__name__)
131 return logging.getLogger(__name__)
120
132
121 def _session_default(self):
133 def _session_default(self):
122 from IPython.zmq.session import Session
134 from IPython.zmq.session import Session
123 return Session(config=self.config)
135 return Session(config=self.config)
124
136
137 def _shell_class_default(self):
138 return InProcessInteractiveShell
139
125 def _stdout_default(self):
140 def _stdout_default(self):
126 from IPython.zmq.iostream import OutStream
141 from IPython.zmq.iostream import OutStream
127 return OutStream(self.session, self.iopub_socket, u'stdout')
142 return OutStream(self.session, self.iopub_socket, u'stdout')
128
143
129 def _stderr_default(self):
144 def _stderr_default(self):
130 from IPython.zmq.iostream import OutStream
145 from IPython.zmq.iostream import OutStream
131 return OutStream(self.session, self.iopub_socket, u'stderr')
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)
@@ -1,900 +1,903 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """A simple interactive kernel that talks to a frontend over 0MQ.
2 """A simple interactive kernel that talks to a frontend over 0MQ.
3
3
4 Things to do:
4 Things to do:
5
5
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 call set_parent on all the PUB objects with the message about to be executed.
7 call set_parent on all the PUB objects with the message about to be executed.
8 * Implement random port and security key logic.
8 * Implement random port and security key logic.
9 * Implement control messages.
9 * Implement control messages.
10 * Implement event loop and poll version.
10 * Implement event loop and poll version.
11 """
11 """
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Standard library imports
18 # Standard library imports
19 import __builtin__
19 import __builtin__
20 import atexit
20 import atexit
21 import sys
21 import sys
22 import time
22 import time
23 import traceback
23 import traceback
24 import logging
24 import logging
25 import uuid
25 import uuid
26
26
27 from datetime import datetime
27 from datetime import datetime
28 from signal import (
28 from signal import (
29 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
29 signal, getsignal, default_int_handler, SIGINT, SIG_IGN
30 )
30 )
31
31
32 # System library imports
32 # System library imports
33 import zmq
33 import zmq
34 from zmq.eventloop import ioloop
34 from zmq.eventloop import ioloop
35 from zmq.eventloop.zmqstream import ZMQStream
35 from zmq.eventloop.zmqstream import ZMQStream
36
36
37 # Local imports
37 # Local imports
38 from IPython.config.configurable import Configurable
38 from IPython.config.configurable import Configurable
39 from IPython.config.application import boolean_flag, catch_config_error
39 from IPython.config.application import boolean_flag, catch_config_error
40 from IPython.core.application import ProfileDir
40 from IPython.core.application import ProfileDir
41 from IPython.core.error import StdinNotImplementedError
41 from IPython.core.error import StdinNotImplementedError
42 from IPython.core.shellapp import (
42 from IPython.core.shellapp import (
43 InteractiveShellApp, shell_flags, shell_aliases
43 InteractiveShellApp, shell_flags, shell_aliases
44 )
44 )
45 from IPython.utils import io
45 from IPython.utils import io
46 from IPython.utils import py3compat
46 from IPython.utils import py3compat
47 from IPython.utils.frame import extract_module_locals
47 from IPython.utils.frame import extract_module_locals
48 from IPython.utils.jsonutil import json_clean
48 from IPython.utils.jsonutil import json_clean
49 from IPython.utils.traitlets import (
49 from IPython.utils.traitlets import (
50 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode
50 Any, Instance, Float, Dict, CaselessStrEnum, List, Set, Integer, Unicode,
51 Type
51 )
52 )
52
53
53 from entry_point import base_launch_kernel
54 from entry_point import base_launch_kernel
54 from kernelapp import KernelApp, kernel_flags, kernel_aliases
55 from kernelapp import KernelApp, kernel_flags, kernel_aliases
55 from serialize import serialize_object, unpack_apply_message
56 from serialize import serialize_object, unpack_apply_message
56 from session import Session, Message
57 from session import Session, Message
57 from zmqshell import ZMQInteractiveShell
58 from zmqshell import ZMQInteractiveShell
58
59
59
60
60 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
61 # Main kernel class
62 # Main kernel class
62 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
63
64
64 class Kernel(Configurable):
65 class Kernel(Configurable):
65
66
66 #---------------------------------------------------------------------------
67 #---------------------------------------------------------------------------
67 # Kernel interface
68 # Kernel interface
68 #---------------------------------------------------------------------------
69 #---------------------------------------------------------------------------
69
70
70 # attribute to override with a GUI
71 # attribute to override with a GUI
71 eventloop = Any(None)
72 eventloop = Any(None)
72 def _eventloop_changed(self, name, old, new):
73 def _eventloop_changed(self, name, old, new):
73 """schedule call to eventloop from IOLoop"""
74 """schedule call to eventloop from IOLoop"""
74 loop = ioloop.IOLoop.instance()
75 loop = ioloop.IOLoop.instance()
75 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
76 loop.add_timeout(time.time()+0.1, self.enter_eventloop)
76
77
77 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
78 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
79 shell_class = Type(ZMQInteractiveShell)
80
78 session = Instance(Session)
81 session = Instance(Session)
79 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
82 profile_dir = Instance('IPython.core.profiledir.ProfileDir')
80 shell_streams = List()
83 shell_streams = List()
81 control_stream = Instance(ZMQStream)
84 control_stream = Instance(ZMQStream)
82 iopub_socket = Instance(zmq.Socket)
85 iopub_socket = Instance(zmq.Socket)
83 stdin_socket = Instance(zmq.Socket)
86 stdin_socket = Instance(zmq.Socket)
84 log = Instance(logging.Logger)
87 log = Instance(logging.Logger)
85
88
86 user_module = Any()
89 user_module = Any()
87 def _user_module_changed(self, name, old, new):
90 def _user_module_changed(self, name, old, new):
88 if self.shell is not None:
91 if self.shell is not None:
89 self.shell.user_module = new
92 self.shell.user_module = new
90
93
91 user_ns = Dict(default_value=None)
94 user_ns = Dict(default_value=None)
92 def _user_ns_changed(self, name, old, new):
95 def _user_ns_changed(self, name, old, new):
93 if self.shell is not None:
96 if self.shell is not None:
94 self.shell.user_ns = new
97 self.shell.user_ns = new
95 self.shell.init_user_ns()
98 self.shell.init_user_ns()
96
99
97 # identities:
100 # identities:
98 int_id = Integer(-1)
101 int_id = Integer(-1)
99 ident = Unicode()
102 ident = Unicode()
100
103
101 def _ident_default(self):
104 def _ident_default(self):
102 return unicode(uuid.uuid4())
105 return unicode(uuid.uuid4())
103
106
104
107
105 # Private interface
108 # Private interface
106
109
107 # Time to sleep after flushing the stdout/err buffers in each execute
110 # Time to sleep after flushing the stdout/err buffers in each execute
108 # cycle. While this introduces a hard limit on the minimal latency of the
111 # cycle. While this introduces a hard limit on the minimal latency of the
109 # execute cycle, it helps prevent output synchronization problems for
112 # execute cycle, it helps prevent output synchronization problems for
110 # clients.
113 # clients.
111 # Units are in seconds. The minimum zmq latency on local host is probably
114 # Units are in seconds. The minimum zmq latency on local host is probably
112 # ~150 microseconds, set this to 500us for now. We may need to increase it
115 # ~150 microseconds, set this to 500us for now. We may need to increase it
113 # a little if it's not enough after more interactive testing.
116 # a little if it's not enough after more interactive testing.
114 _execute_sleep = Float(0.0005, config=True)
117 _execute_sleep = Float(0.0005, config=True)
115
118
116 # Frequency of the kernel's event loop.
119 # Frequency of the kernel's event loop.
117 # Units are in seconds, kernel subclasses for GUI toolkits may need to
120 # Units are in seconds, kernel subclasses for GUI toolkits may need to
118 # adapt to milliseconds.
121 # adapt to milliseconds.
119 _poll_interval = Float(0.05, config=True)
122 _poll_interval = Float(0.05, config=True)
120
123
121 # If the shutdown was requested over the network, we leave here the
124 # If the shutdown was requested over the network, we leave here the
122 # necessary reply message so it can be sent by our registered atexit
125 # necessary reply message so it can be sent by our registered atexit
123 # handler. This ensures that the reply is only sent to clients truly at
126 # handler. This ensures that the reply is only sent to clients truly at
124 # the end of our shutdown process (which happens after the underlying
127 # the end of our shutdown process (which happens after the underlying
125 # IPython shell's own shutdown).
128 # IPython shell's own shutdown).
126 _shutdown_message = None
129 _shutdown_message = None
127
130
128 # This is a dict of port number that the kernel is listening on. It is set
131 # This is a dict of port number that the kernel is listening on. It is set
129 # by record_ports and used by connect_request.
132 # by record_ports and used by connect_request.
130 _recorded_ports = Dict()
133 _recorded_ports = Dict()
131
134
132 # set of aborted msg_ids
135 # set of aborted msg_ids
133 aborted = Set()
136 aborted = Set()
134
137
135
138
136 def __init__(self, **kwargs):
139 def __init__(self, **kwargs):
137 super(Kernel, self).__init__(**kwargs)
140 super(Kernel, self).__init__(**kwargs)
138
141
139 # Initialize the InteractiveShell subclass
142 # Initialize the InteractiveShell subclass
140 self.shell = ZMQInteractiveShell.instance(config=self.config,
143 self.shell = self.shell_class.instance(config=self.config,
141 profile_dir = self.profile_dir,
144 profile_dir = self.profile_dir,
142 user_module = self.user_module,
145 user_module = self.user_module,
143 user_ns = self.user_ns,
146 user_ns = self.user_ns,
144 )
147 )
145 self.shell.displayhook.session = self.session
148 self.shell.displayhook.session = self.session
146 self.shell.displayhook.pub_socket = self.iopub_socket
149 self.shell.displayhook.pub_socket = self.iopub_socket
147 self.shell.displayhook.topic = self._topic('pyout')
150 self.shell.displayhook.topic = self._topic('pyout')
148 self.shell.display_pub.session = self.session
151 self.shell.display_pub.session = self.session
149 self.shell.display_pub.pub_socket = self.iopub_socket
152 self.shell.display_pub.pub_socket = self.iopub_socket
150 self.shell.data_pub.session = self.session
153 self.shell.data_pub.session = self.session
151 self.shell.data_pub.pub_socket = self.iopub_socket
154 self.shell.data_pub.pub_socket = self.iopub_socket
152
155
153 # TMP - hack while developing
156 # TMP - hack while developing
154 self.shell._reply_content = None
157 self.shell._reply_content = None
155
158
156 # Build dict of handlers for message types
159 # Build dict of handlers for message types
157 msg_types = [ 'execute_request', 'complete_request',
160 msg_types = [ 'execute_request', 'complete_request',
158 'object_info_request', 'history_request',
161 'object_info_request', 'history_request',
159 'connect_request', 'shutdown_request',
162 'connect_request', 'shutdown_request',
160 'apply_request',
163 'apply_request',
161 ]
164 ]
162 self.shell_handlers = {}
165 self.shell_handlers = {}
163 for msg_type in msg_types:
166 for msg_type in msg_types:
164 self.shell_handlers[msg_type] = getattr(self, msg_type)
167 self.shell_handlers[msg_type] = getattr(self, msg_type)
165
168
166 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
169 control_msg_types = msg_types + [ 'clear_request', 'abort_request' ]
167 self.control_handlers = {}
170 self.control_handlers = {}
168 for msg_type in control_msg_types:
171 for msg_type in control_msg_types:
169 self.control_handlers[msg_type] = getattr(self, msg_type)
172 self.control_handlers[msg_type] = getattr(self, msg_type)
170
173
171 def dispatch_control(self, msg):
174 def dispatch_control(self, msg):
172 """dispatch control requests"""
175 """dispatch control requests"""
173 idents,msg = self.session.feed_identities(msg, copy=False)
176 idents,msg = self.session.feed_identities(msg, copy=False)
174 try:
177 try:
175 msg = self.session.unserialize(msg, content=True, copy=False)
178 msg = self.session.unserialize(msg, content=True, copy=False)
176 except:
179 except:
177 self.log.error("Invalid Control Message", exc_info=True)
180 self.log.error("Invalid Control Message", exc_info=True)
178 return
181 return
179
182
180 self.log.debug("Control received: %s", msg)
183 self.log.debug("Control received: %s", msg)
181
184
182 header = msg['header']
185 header = msg['header']
183 msg_id = header['msg_id']
186 msg_id = header['msg_id']
184 msg_type = header['msg_type']
187 msg_type = header['msg_type']
185
188
186 handler = self.control_handlers.get(msg_type, None)
189 handler = self.control_handlers.get(msg_type, None)
187 if handler is None:
190 if handler is None:
188 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
191 self.log.error("UNKNOWN CONTROL MESSAGE TYPE: %r", msg_type)
189 else:
192 else:
190 try:
193 try:
191 handler(self.control_stream, idents, msg)
194 handler(self.control_stream, idents, msg)
192 except Exception:
195 except Exception:
193 self.log.error("Exception in control handler:", exc_info=True)
196 self.log.error("Exception in control handler:", exc_info=True)
194
197
195 def dispatch_shell(self, stream, msg):
198 def dispatch_shell(self, stream, msg):
196 """dispatch shell requests"""
199 """dispatch shell requests"""
197 # flush control requests first
200 # flush control requests first
198 if self.control_stream:
201 if self.control_stream:
199 self.control_stream.flush()
202 self.control_stream.flush()
200
203
201 idents,msg = self.session.feed_identities(msg, copy=False)
204 idents,msg = self.session.feed_identities(msg, copy=False)
202 try:
205 try:
203 msg = self.session.unserialize(msg, content=True, copy=False)
206 msg = self.session.unserialize(msg, content=True, copy=False)
204 except:
207 except:
205 self.log.error("Invalid Message", exc_info=True)
208 self.log.error("Invalid Message", exc_info=True)
206 return
209 return
207
210
208 header = msg['header']
211 header = msg['header']
209 msg_id = header['msg_id']
212 msg_id = header['msg_id']
210 msg_type = msg['header']['msg_type']
213 msg_type = msg['header']['msg_type']
211
214
212 # Print some info about this message and leave a '--->' marker, so it's
215 # Print some info about this message and leave a '--->' marker, so it's
213 # easier to trace visually the message chain when debugging. Each
216 # easier to trace visually the message chain when debugging. Each
214 # handler prints its message at the end.
217 # handler prints its message at the end.
215 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
218 self.log.debug('\n*** MESSAGE TYPE:%s***', msg_type)
216 self.log.debug(' Content: %s\n --->\n ', msg['content'])
219 self.log.debug(' Content: %s\n --->\n ', msg['content'])
217
220
218 if msg_id in self.aborted:
221 if msg_id in self.aborted:
219 self.aborted.remove(msg_id)
222 self.aborted.remove(msg_id)
220 # is it safe to assume a msg_id will not be resubmitted?
223 # is it safe to assume a msg_id will not be resubmitted?
221 reply_type = msg_type.split('_')[0] + '_reply'
224 reply_type = msg_type.split('_')[0] + '_reply'
222 status = {'status' : 'aborted'}
225 status = {'status' : 'aborted'}
223 md = {'engine' : self.ident}
226 md = {'engine' : self.ident}
224 md.update(status)
227 md.update(status)
225 reply_msg = self.session.send(stream, reply_type, metadata=md,
228 reply_msg = self.session.send(stream, reply_type, metadata=md,
226 content=status, parent=msg, ident=idents)
229 content=status, parent=msg, ident=idents)
227 return
230 return
228
231
229 handler = self.shell_handlers.get(msg_type, None)
232 handler = self.shell_handlers.get(msg_type, None)
230 if handler is None:
233 if handler is None:
231 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
234 self.log.error("UNKNOWN MESSAGE TYPE: %r", msg_type)
232 else:
235 else:
233 # ensure default_int_handler during handler call
236 # ensure default_int_handler during handler call
234 sig = signal(SIGINT, default_int_handler)
237 sig = signal(SIGINT, default_int_handler)
235 try:
238 try:
236 handler(stream, idents, msg)
239 handler(stream, idents, msg)
237 except Exception:
240 except Exception:
238 self.log.error("Exception in message handler:", exc_info=True)
241 self.log.error("Exception in message handler:", exc_info=True)
239 finally:
242 finally:
240 signal(SIGINT, sig)
243 signal(SIGINT, sig)
241
244
242 def enter_eventloop(self):
245 def enter_eventloop(self):
243 """enter eventloop"""
246 """enter eventloop"""
244 self.log.info("entering eventloop")
247 self.log.info("entering eventloop")
245 # restore default_int_handler
248 # restore default_int_handler
246 signal(SIGINT, default_int_handler)
249 signal(SIGINT, default_int_handler)
247 while self.eventloop is not None:
250 while self.eventloop is not None:
248 try:
251 try:
249 self.eventloop(self)
252 self.eventloop(self)
250 except KeyboardInterrupt:
253 except KeyboardInterrupt:
251 # Ctrl-C shouldn't crash the kernel
254 # Ctrl-C shouldn't crash the kernel
252 self.log.error("KeyboardInterrupt caught in kernel")
255 self.log.error("KeyboardInterrupt caught in kernel")
253 continue
256 continue
254 else:
257 else:
255 # eventloop exited cleanly, this means we should stop (right?)
258 # eventloop exited cleanly, this means we should stop (right?)
256 self.eventloop = None
259 self.eventloop = None
257 break
260 break
258 self.log.info("exiting eventloop")
261 self.log.info("exiting eventloop")
259
262
260 def start(self):
263 def start(self):
261 """register dispatchers for streams"""
264 """register dispatchers for streams"""
262 self.shell.exit_now = False
265 self.shell.exit_now = False
263 if self.control_stream:
266 if self.control_stream:
264 self.control_stream.on_recv(self.dispatch_control, copy=False)
267 self.control_stream.on_recv(self.dispatch_control, copy=False)
265
268
266 def make_dispatcher(stream):
269 def make_dispatcher(stream):
267 def dispatcher(msg):
270 def dispatcher(msg):
268 return self.dispatch_shell(stream, msg)
271 return self.dispatch_shell(stream, msg)
269 return dispatcher
272 return dispatcher
270
273
271 for s in self.shell_streams:
274 for s in self.shell_streams:
272 s.on_recv(make_dispatcher(s), copy=False)
275 s.on_recv(make_dispatcher(s), copy=False)
273
276
274 def do_one_iteration(self):
277 def do_one_iteration(self):
275 """step eventloop just once"""
278 """step eventloop just once"""
276 if self.control_stream:
279 if self.control_stream:
277 self.control_stream.flush()
280 self.control_stream.flush()
278 for stream in self.shell_streams:
281 for stream in self.shell_streams:
279 # handle at most one request per iteration
282 # handle at most one request per iteration
280 stream.flush(zmq.POLLIN, 1)
283 stream.flush(zmq.POLLIN, 1)
281 stream.flush(zmq.POLLOUT)
284 stream.flush(zmq.POLLOUT)
282
285
283
286
284 def record_ports(self, ports):
287 def record_ports(self, ports):
285 """Record the ports that this kernel is using.
288 """Record the ports that this kernel is using.
286
289
287 The creator of the Kernel instance must call this methods if they
290 The creator of the Kernel instance must call this methods if they
288 want the :meth:`connect_request` method to return the port numbers.
291 want the :meth:`connect_request` method to return the port numbers.
289 """
292 """
290 self._recorded_ports = ports
293 self._recorded_ports = ports
291
294
292 #---------------------------------------------------------------------------
295 #---------------------------------------------------------------------------
293 # Kernel request handlers
296 # Kernel request handlers
294 #---------------------------------------------------------------------------
297 #---------------------------------------------------------------------------
295
298
296 def _make_metadata(self, other=None):
299 def _make_metadata(self, other=None):
297 """init metadata dict, for execute/apply_reply"""
300 """init metadata dict, for execute/apply_reply"""
298 new_md = {
301 new_md = {
299 'dependencies_met' : True,
302 'dependencies_met' : True,
300 'engine' : self.ident,
303 'engine' : self.ident,
301 'started': datetime.now(),
304 'started': datetime.now(),
302 }
305 }
303 if other:
306 if other:
304 new_md.update(other)
307 new_md.update(other)
305 return new_md
308 return new_md
306
309
307 def _publish_pyin(self, code, parent, execution_count):
310 def _publish_pyin(self, code, parent, execution_count):
308 """Publish the code request on the pyin stream."""
311 """Publish the code request on the pyin stream."""
309
312
310 self.session.send(self.iopub_socket, u'pyin',
313 self.session.send(self.iopub_socket, u'pyin',
311 {u'code':code, u'execution_count': execution_count},
314 {u'code':code, u'execution_count': execution_count},
312 parent=parent, ident=self._topic('pyin')
315 parent=parent, ident=self._topic('pyin')
313 )
316 )
314
317
315 def _publish_status(self, status, parent=None):
318 def _publish_status(self, status, parent=None):
316 """send status (busy/idle) on IOPub"""
319 """send status (busy/idle) on IOPub"""
317 self.session.send(self.iopub_socket,
320 self.session.send(self.iopub_socket,
318 u'status',
321 u'status',
319 {u'execution_state': status},
322 {u'execution_state': status},
320 parent=parent,
323 parent=parent,
321 ident=self._topic('status'),
324 ident=self._topic('status'),
322 )
325 )
323
326
324
327
325 def execute_request(self, stream, ident, parent):
328 def execute_request(self, stream, ident, parent):
326 """handle an execute_request"""
329 """handle an execute_request"""
327
330
328 self._publish_status(u'busy', parent)
331 self._publish_status(u'busy', parent)
329
332
330 try:
333 try:
331 content = parent[u'content']
334 content = parent[u'content']
332 code = content[u'code']
335 code = content[u'code']
333 silent = content[u'silent']
336 silent = content[u'silent']
334 store_history = content.get(u'store_history', not silent)
337 store_history = content.get(u'store_history', not silent)
335 except:
338 except:
336 self.log.error("Got bad msg: ")
339 self.log.error("Got bad msg: ")
337 self.log.error("%s", parent)
340 self.log.error("%s", parent)
338 return
341 return
339
342
340 md = self._make_metadata(parent['metadata'])
343 md = self._make_metadata(parent['metadata'])
341
344
342 shell = self.shell # we'll need this a lot here
345 shell = self.shell # we'll need this a lot here
343
346
344 # Replace raw_input. Note that is not sufficient to replace
347 # Replace raw_input. Note that is not sufficient to replace
345 # raw_input in the user namespace.
348 # raw_input in the user namespace.
346 if content.get('allow_stdin', False):
349 if content.get('allow_stdin', False):
347 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
350 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
348 else:
351 else:
349 raw_input = lambda prompt='' : self._no_raw_input()
352 raw_input = lambda prompt='' : self._no_raw_input()
350
353
351 if py3compat.PY3:
354 if py3compat.PY3:
352 __builtin__.input = raw_input
355 __builtin__.input = raw_input
353 else:
356 else:
354 __builtin__.raw_input = raw_input
357 __builtin__.raw_input = raw_input
355
358
356 # Set the parent message of the display hook and out streams.
359 # Set the parent message of the display hook and out streams.
357 shell.displayhook.set_parent(parent)
360 shell.displayhook.set_parent(parent)
358 shell.display_pub.set_parent(parent)
361 shell.display_pub.set_parent(parent)
359 shell.data_pub.set_parent(parent)
362 shell.data_pub.set_parent(parent)
360 sys.stdout.set_parent(parent)
363 sys.stdout.set_parent(parent)
361 sys.stderr.set_parent(parent)
364 sys.stderr.set_parent(parent)
362
365
363 # Re-broadcast our input for the benefit of listening clients, and
366 # Re-broadcast our input for the benefit of listening clients, and
364 # start computing output
367 # start computing output
365 if not silent:
368 if not silent:
366 self._publish_pyin(code, parent, shell.execution_count)
369 self._publish_pyin(code, parent, shell.execution_count)
367
370
368 reply_content = {}
371 reply_content = {}
369 try:
372 try:
370 # FIXME: the shell calls the exception handler itself.
373 # FIXME: the shell calls the exception handler itself.
371 shell.run_cell(code, store_history=store_history, silent=silent)
374 shell.run_cell(code, store_history=store_history, silent=silent)
372 except:
375 except:
373 status = u'error'
376 status = u'error'
374 # FIXME: this code right now isn't being used yet by default,
377 # FIXME: this code right now isn't being used yet by default,
375 # because the run_cell() call above directly fires off exception
378 # because the run_cell() call above directly fires off exception
376 # reporting. This code, therefore, is only active in the scenario
379 # reporting. This code, therefore, is only active in the scenario
377 # where runlines itself has an unhandled exception. We need to
380 # where runlines itself has an unhandled exception. We need to
378 # uniformize this, for all exception construction to come from a
381 # uniformize this, for all exception construction to come from a
379 # single location in the codbase.
382 # single location in the codbase.
380 etype, evalue, tb = sys.exc_info()
383 etype, evalue, tb = sys.exc_info()
381 tb_list = traceback.format_exception(etype, evalue, tb)
384 tb_list = traceback.format_exception(etype, evalue, tb)
382 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
385 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
383 else:
386 else:
384 status = u'ok'
387 status = u'ok'
385
388
386 reply_content[u'status'] = status
389 reply_content[u'status'] = status
387
390
388 # Return the execution counter so clients can display prompts
391 # Return the execution counter so clients can display prompts
389 reply_content['execution_count'] = shell.execution_count - 1
392 reply_content['execution_count'] = shell.execution_count - 1
390
393
391 # FIXME - fish exception info out of shell, possibly left there by
394 # FIXME - fish exception info out of shell, possibly left there by
392 # runlines. We'll need to clean up this logic later.
395 # runlines. We'll need to clean up this logic later.
393 if shell._reply_content is not None:
396 if shell._reply_content is not None:
394 reply_content.update(shell._reply_content)
397 reply_content.update(shell._reply_content)
395 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
398 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='execute')
396 reply_content['engine_info'] = e_info
399 reply_content['engine_info'] = e_info
397 # reset after use
400 # reset after use
398 shell._reply_content = None
401 shell._reply_content = None
399
402
400 # At this point, we can tell whether the main code execution succeeded
403 # At this point, we can tell whether the main code execution succeeded
401 # or not. If it did, we proceed to evaluate user_variables/expressions
404 # or not. If it did, we proceed to evaluate user_variables/expressions
402 if reply_content['status'] == 'ok':
405 if reply_content['status'] == 'ok':
403 reply_content[u'user_variables'] = \
406 reply_content[u'user_variables'] = \
404 shell.user_variables(content.get(u'user_variables', []))
407 shell.user_variables(content.get(u'user_variables', []))
405 reply_content[u'user_expressions'] = \
408 reply_content[u'user_expressions'] = \
406 shell.user_expressions(content.get(u'user_expressions', {}))
409 shell.user_expressions(content.get(u'user_expressions', {}))
407 else:
410 else:
408 # If there was an error, don't even try to compute variables or
411 # If there was an error, don't even try to compute variables or
409 # expressions
412 # expressions
410 reply_content[u'user_variables'] = {}
413 reply_content[u'user_variables'] = {}
411 reply_content[u'user_expressions'] = {}
414 reply_content[u'user_expressions'] = {}
412
415
413 # Payloads should be retrieved regardless of outcome, so we can both
416 # Payloads should be retrieved regardless of outcome, so we can both
414 # recover partial output (that could have been generated early in a
417 # recover partial output (that could have been generated early in a
415 # block, before an error) and clear the payload system always.
418 # block, before an error) and clear the payload system always.
416 reply_content[u'payload'] = shell.payload_manager.read_payload()
419 reply_content[u'payload'] = shell.payload_manager.read_payload()
417 # Be agressive about clearing the payload because we don't want
420 # Be agressive about clearing the payload because we don't want
418 # it to sit in memory until the next execute_request comes in.
421 # it to sit in memory until the next execute_request comes in.
419 shell.payload_manager.clear_payload()
422 shell.payload_manager.clear_payload()
420
423
421 # Flush output before sending the reply.
424 # Flush output before sending the reply.
422 sys.stdout.flush()
425 sys.stdout.flush()
423 sys.stderr.flush()
426 sys.stderr.flush()
424 # FIXME: on rare occasions, the flush doesn't seem to make it to the
427 # FIXME: on rare occasions, the flush doesn't seem to make it to the
425 # clients... This seems to mitigate the problem, but we definitely need
428 # clients... This seems to mitigate the problem, but we definitely need
426 # to better understand what's going on.
429 # to better understand what's going on.
427 if self._execute_sleep:
430 if self._execute_sleep:
428 time.sleep(self._execute_sleep)
431 time.sleep(self._execute_sleep)
429
432
430 # Send the reply.
433 # Send the reply.
431 reply_content = json_clean(reply_content)
434 reply_content = json_clean(reply_content)
432
435
433 md['status'] = reply_content['status']
436 md['status'] = reply_content['status']
434 if reply_content['status'] == 'error' and \
437 if reply_content['status'] == 'error' and \
435 reply_content['ename'] == 'UnmetDependency':
438 reply_content['ename'] == 'UnmetDependency':
436 md['dependencies_met'] = False
439 md['dependencies_met'] = False
437
440
438 reply_msg = self.session.send(stream, u'execute_reply',
441 reply_msg = self.session.send(stream, u'execute_reply',
439 reply_content, parent, metadata=md,
442 reply_content, parent, metadata=md,
440 ident=ident)
443 ident=ident)
441
444
442 self.log.debug("%s", reply_msg)
445 self.log.debug("%s", reply_msg)
443
446
444 if not silent and reply_msg['content']['status'] == u'error':
447 if not silent and reply_msg['content']['status'] == u'error':
445 self._abort_queues()
448 self._abort_queues()
446
449
447 self._publish_status(u'idle', parent)
450 self._publish_status(u'idle', parent)
448
451
449 def complete_request(self, stream, ident, parent):
452 def complete_request(self, stream, ident, parent):
450 txt, matches = self._complete(parent)
453 txt, matches = self._complete(parent)
451 matches = {'matches' : matches,
454 matches = {'matches' : matches,
452 'matched_text' : txt,
455 'matched_text' : txt,
453 'status' : 'ok'}
456 'status' : 'ok'}
454 matches = json_clean(matches)
457 matches = json_clean(matches)
455 completion_msg = self.session.send(stream, 'complete_reply',
458 completion_msg = self.session.send(stream, 'complete_reply',
456 matches, parent, ident)
459 matches, parent, ident)
457 self.log.debug("%s", completion_msg)
460 self.log.debug("%s", completion_msg)
458
461
459 def object_info_request(self, stream, ident, parent):
462 def object_info_request(self, stream, ident, parent):
460 content = parent['content']
463 content = parent['content']
461 object_info = self.shell.object_inspect(content['oname'],
464 object_info = self.shell.object_inspect(content['oname'],
462 detail_level = content.get('detail_level', 0)
465 detail_level = content.get('detail_level', 0)
463 )
466 )
464 # Before we send this object over, we scrub it for JSON usage
467 # Before we send this object over, we scrub it for JSON usage
465 oinfo = json_clean(object_info)
468 oinfo = json_clean(object_info)
466 msg = self.session.send(stream, 'object_info_reply',
469 msg = self.session.send(stream, 'object_info_reply',
467 oinfo, parent, ident)
470 oinfo, parent, ident)
468 self.log.debug("%s", msg)
471 self.log.debug("%s", msg)
469
472
470 def history_request(self, stream, ident, parent):
473 def history_request(self, stream, ident, parent):
471 # We need to pull these out, as passing **kwargs doesn't work with
474 # We need to pull these out, as passing **kwargs doesn't work with
472 # unicode keys before Python 2.6.5.
475 # unicode keys before Python 2.6.5.
473 hist_access_type = parent['content']['hist_access_type']
476 hist_access_type = parent['content']['hist_access_type']
474 raw = parent['content']['raw']
477 raw = parent['content']['raw']
475 output = parent['content']['output']
478 output = parent['content']['output']
476 if hist_access_type == 'tail':
479 if hist_access_type == 'tail':
477 n = parent['content']['n']
480 n = parent['content']['n']
478 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
481 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
479 include_latest=True)
482 include_latest=True)
480
483
481 elif hist_access_type == 'range':
484 elif hist_access_type == 'range':
482 session = parent['content']['session']
485 session = parent['content']['session']
483 start = parent['content']['start']
486 start = parent['content']['start']
484 stop = parent['content']['stop']
487 stop = parent['content']['stop']
485 hist = self.shell.history_manager.get_range(session, start, stop,
488 hist = self.shell.history_manager.get_range(session, start, stop,
486 raw=raw, output=output)
489 raw=raw, output=output)
487
490
488 elif hist_access_type == 'search':
491 elif hist_access_type == 'search':
489 pattern = parent['content']['pattern']
492 pattern = parent['content']['pattern']
490 hist = self.shell.history_manager.search(pattern, raw=raw,
493 hist = self.shell.history_manager.search(pattern, raw=raw,
491 output=output)
494 output=output)
492
495
493 else:
496 else:
494 hist = []
497 hist = []
495 hist = list(hist)
498 hist = list(hist)
496 content = {'history' : hist}
499 content = {'history' : hist}
497 content = json_clean(content)
500 content = json_clean(content)
498 msg = self.session.send(stream, 'history_reply',
501 msg = self.session.send(stream, 'history_reply',
499 content, parent, ident)
502 content, parent, ident)
500 self.log.debug("Sending history reply with %i entries", len(hist))
503 self.log.debug("Sending history reply with %i entries", len(hist))
501
504
502 def connect_request(self, stream, ident, parent):
505 def connect_request(self, stream, ident, parent):
503 if self._recorded_ports is not None:
506 if self._recorded_ports is not None:
504 content = self._recorded_ports.copy()
507 content = self._recorded_ports.copy()
505 else:
508 else:
506 content = {}
509 content = {}
507 msg = self.session.send(stream, 'connect_reply',
510 msg = self.session.send(stream, 'connect_reply',
508 content, parent, ident)
511 content, parent, ident)
509 self.log.debug("%s", msg)
512 self.log.debug("%s", msg)
510
513
511 def shutdown_request(self, stream, ident, parent):
514 def shutdown_request(self, stream, ident, parent):
512 self.shell.exit_now = True
515 self.shell.exit_now = True
513 content = dict(status='ok')
516 content = dict(status='ok')
514 content.update(parent['content'])
517 content.update(parent['content'])
515 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
518 self.session.send(stream, u'shutdown_reply', content, parent, ident=ident)
516 # same content, but different msg_id for broadcasting on IOPub
519 # same content, but different msg_id for broadcasting on IOPub
517 self._shutdown_message = self.session.msg(u'shutdown_reply',
520 self._shutdown_message = self.session.msg(u'shutdown_reply',
518 content, parent
521 content, parent
519 )
522 )
520
523
521 self._at_shutdown()
524 self._at_shutdown()
522 # call sys.exit after a short delay
525 # call sys.exit after a short delay
523 loop = ioloop.IOLoop.instance()
526 loop = ioloop.IOLoop.instance()
524 loop.add_timeout(time.time()+0.1, loop.stop)
527 loop.add_timeout(time.time()+0.1, loop.stop)
525
528
526 #---------------------------------------------------------------------------
529 #---------------------------------------------------------------------------
527 # Engine methods
530 # Engine methods
528 #---------------------------------------------------------------------------
531 #---------------------------------------------------------------------------
529
532
530 def apply_request(self, stream, ident, parent):
533 def apply_request(self, stream, ident, parent):
531 try:
534 try:
532 content = parent[u'content']
535 content = parent[u'content']
533 bufs = parent[u'buffers']
536 bufs = parent[u'buffers']
534 msg_id = parent['header']['msg_id']
537 msg_id = parent['header']['msg_id']
535 except:
538 except:
536 self.log.error("Got bad msg: %s", parent, exc_info=True)
539 self.log.error("Got bad msg: %s", parent, exc_info=True)
537 return
540 return
538
541
539 self._publish_status(u'busy', parent)
542 self._publish_status(u'busy', parent)
540
543
541 # Set the parent message of the display hook and out streams.
544 # Set the parent message of the display hook and out streams.
542 shell = self.shell
545 shell = self.shell
543 shell.displayhook.set_parent(parent)
546 shell.displayhook.set_parent(parent)
544 shell.display_pub.set_parent(parent)
547 shell.display_pub.set_parent(parent)
545 shell.data_pub.set_parent(parent)
548 shell.data_pub.set_parent(parent)
546 sys.stdout.set_parent(parent)
549 sys.stdout.set_parent(parent)
547 sys.stderr.set_parent(parent)
550 sys.stderr.set_parent(parent)
548
551
549 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
552 # pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
550 # self.iopub_socket.send(pyin_msg)
553 # self.iopub_socket.send(pyin_msg)
551 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
554 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
552 md = self._make_metadata(parent['metadata'])
555 md = self._make_metadata(parent['metadata'])
553 try:
556 try:
554 working = shell.user_ns
557 working = shell.user_ns
555
558
556 prefix = "_"+str(msg_id).replace("-","")+"_"
559 prefix = "_"+str(msg_id).replace("-","")+"_"
557
560
558 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
561 f,args,kwargs = unpack_apply_message(bufs, working, copy=False)
559
562
560 fname = getattr(f, '__name__', 'f')
563 fname = getattr(f, '__name__', 'f')
561
564
562 fname = prefix+"f"
565 fname = prefix+"f"
563 argname = prefix+"args"
566 argname = prefix+"args"
564 kwargname = prefix+"kwargs"
567 kwargname = prefix+"kwargs"
565 resultname = prefix+"result"
568 resultname = prefix+"result"
566
569
567 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
570 ns = { fname : f, argname : args, kwargname : kwargs , resultname : None }
568 # print ns
571 # print ns
569 working.update(ns)
572 working.update(ns)
570 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
573 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
571 try:
574 try:
572 exec code in shell.user_global_ns, shell.user_ns
575 exec code in shell.user_global_ns, shell.user_ns
573 result = working.get(resultname)
576 result = working.get(resultname)
574 finally:
577 finally:
575 for key in ns.iterkeys():
578 for key in ns.iterkeys():
576 working.pop(key)
579 working.pop(key)
577
580
578 result_buf = serialize_object(result,
581 result_buf = serialize_object(result,
579 buffer_threshold=self.session.buffer_threshold,
582 buffer_threshold=self.session.buffer_threshold,
580 item_threshold=self.session.item_threshold,
583 item_threshold=self.session.item_threshold,
581 )
584 )
582
585
583 except:
586 except:
584 # invoke IPython traceback formatting
587 # invoke IPython traceback formatting
585 shell.showtraceback()
588 shell.showtraceback()
586 # FIXME - fish exception info out of shell, possibly left there by
589 # FIXME - fish exception info out of shell, possibly left there by
587 # run_code. We'll need to clean up this logic later.
590 # run_code. We'll need to clean up this logic later.
588 reply_content = {}
591 reply_content = {}
589 if shell._reply_content is not None:
592 if shell._reply_content is not None:
590 reply_content.update(shell._reply_content)
593 reply_content.update(shell._reply_content)
591 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
594 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
592 reply_content['engine_info'] = e_info
595 reply_content['engine_info'] = e_info
593 # reset after use
596 # reset after use
594 shell._reply_content = None
597 shell._reply_content = None
595
598
596 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
599 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
597 ident=self._topic('pyerr'))
600 ident=self._topic('pyerr'))
598 result_buf = []
601 result_buf = []
599
602
600 if reply_content['ename'] == 'UnmetDependency':
603 if reply_content['ename'] == 'UnmetDependency':
601 md['dependencies_met'] = False
604 md['dependencies_met'] = False
602 else:
605 else:
603 reply_content = {'status' : 'ok'}
606 reply_content = {'status' : 'ok'}
604
607
605 # put 'ok'/'error' status in header, for scheduler introspection:
608 # put 'ok'/'error' status in header, for scheduler introspection:
606 md['status'] = reply_content['status']
609 md['status'] = reply_content['status']
607
610
608 # flush i/o
611 # flush i/o
609 sys.stdout.flush()
612 sys.stdout.flush()
610 sys.stderr.flush()
613 sys.stderr.flush()
611
614
612 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
615 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
613 parent=parent, ident=ident,buffers=result_buf, metadata=md)
616 parent=parent, ident=ident,buffers=result_buf, metadata=md)
614
617
615 self._publish_status(u'idle', parent)
618 self._publish_status(u'idle', parent)
616
619
617 #---------------------------------------------------------------------------
620 #---------------------------------------------------------------------------
618 # Control messages
621 # Control messages
619 #---------------------------------------------------------------------------
622 #---------------------------------------------------------------------------
620
623
621 def abort_request(self, stream, ident, parent):
624 def abort_request(self, stream, ident, parent):
622 """abort a specifig msg by id"""
625 """abort a specifig msg by id"""
623 msg_ids = parent['content'].get('msg_ids', None)
626 msg_ids = parent['content'].get('msg_ids', None)
624 if isinstance(msg_ids, basestring):
627 if isinstance(msg_ids, basestring):
625 msg_ids = [msg_ids]
628 msg_ids = [msg_ids]
626 if not msg_ids:
629 if not msg_ids:
627 self.abort_queues()
630 self.abort_queues()
628 for mid in msg_ids:
631 for mid in msg_ids:
629 self.aborted.add(str(mid))
632 self.aborted.add(str(mid))
630
633
631 content = dict(status='ok')
634 content = dict(status='ok')
632 reply_msg = self.session.send(stream, 'abort_reply', content=content,
635 reply_msg = self.session.send(stream, 'abort_reply', content=content,
633 parent=parent, ident=ident)
636 parent=parent, ident=ident)
634 self.log.debug("%s", reply_msg)
637 self.log.debug("%s", reply_msg)
635
638
636 def clear_request(self, stream, idents, parent):
639 def clear_request(self, stream, idents, parent):
637 """Clear our namespace."""
640 """Clear our namespace."""
638 self.shell.reset(False)
641 self.shell.reset(False)
639 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
642 msg = self.session.send(stream, 'clear_reply', ident=idents, parent=parent,
640 content = dict(status='ok'))
643 content = dict(status='ok'))
641
644
642
645
643 #---------------------------------------------------------------------------
646 #---------------------------------------------------------------------------
644 # Protected interface
647 # Protected interface
645 #---------------------------------------------------------------------------
648 #---------------------------------------------------------------------------
646
649
647 def _wrap_exception(self, method=None):
650 def _wrap_exception(self, method=None):
648 # import here, because _wrap_exception is only used in parallel,
651 # import here, because _wrap_exception is only used in parallel,
649 # and parallel has higher min pyzmq version
652 # and parallel has higher min pyzmq version
650 from IPython.parallel.error import wrap_exception
653 from IPython.parallel.error import wrap_exception
651 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
654 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method=method)
652 content = wrap_exception(e_info)
655 content = wrap_exception(e_info)
653 return content
656 return content
654
657
655 def _topic(self, topic):
658 def _topic(self, topic):
656 """prefixed topic for IOPub messages"""
659 """prefixed topic for IOPub messages"""
657 if self.int_id >= 0:
660 if self.int_id >= 0:
658 base = "engine.%i" % self.int_id
661 base = "engine.%i" % self.int_id
659 else:
662 else:
660 base = "kernel.%s" % self.ident
663 base = "kernel.%s" % self.ident
661
664
662 return py3compat.cast_bytes("%s.%s" % (base, topic))
665 return py3compat.cast_bytes("%s.%s" % (base, topic))
663
666
664 def _abort_queues(self):
667 def _abort_queues(self):
665 for stream in self.shell_streams:
668 for stream in self.shell_streams:
666 if stream:
669 if stream:
667 self._abort_queue(stream)
670 self._abort_queue(stream)
668
671
669 def _abort_queue(self, stream):
672 def _abort_queue(self, stream):
670 poller = zmq.Poller()
673 poller = zmq.Poller()
671 poller.register(stream.socket, zmq.POLLIN)
674 poller.register(stream.socket, zmq.POLLIN)
672 while True:
675 while True:
673 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
676 idents,msg = self.session.recv(stream, zmq.NOBLOCK, content=True)
674 if msg is None:
677 if msg is None:
675 return
678 return
676
679
677 self.log.info("Aborting:")
680 self.log.info("Aborting:")
678 self.log.info("%s", msg)
681 self.log.info("%s", msg)
679 msg_type = msg['header']['msg_type']
682 msg_type = msg['header']['msg_type']
680 reply_type = msg_type.split('_')[0] + '_reply'
683 reply_type = msg_type.split('_')[0] + '_reply'
681
684
682 status = {'status' : 'aborted'}
685 status = {'status' : 'aborted'}
683 md = {'engine' : self.ident}
686 md = {'engine' : self.ident}
684 md.update(status)
687 md.update(status)
685 reply_msg = self.session.send(stream, reply_type, metadata=md,
688 reply_msg = self.session.send(stream, reply_type, metadata=md,
686 content=status, parent=msg, ident=idents)
689 content=status, parent=msg, ident=idents)
687 self.log.debug("%s", reply_msg)
690 self.log.debug("%s", reply_msg)
688 # We need to wait a bit for requests to come in. This can probably
691 # We need to wait a bit for requests to come in. This can probably
689 # be set shorter for true asynchronous clients.
692 # be set shorter for true asynchronous clients.
690 poller.poll(50)
693 poller.poll(50)
691
694
692
695
693 def _no_raw_input(self):
696 def _no_raw_input(self):
694 """Raise StdinNotImplentedError if active frontend doesn't support
697 """Raise StdinNotImplentedError if active frontend doesn't support
695 stdin."""
698 stdin."""
696 raise StdinNotImplementedError("raw_input was called, but this "
699 raise StdinNotImplementedError("raw_input was called, but this "
697 "frontend does not support stdin.")
700 "frontend does not support stdin.")
698
701
699 def _raw_input(self, prompt, ident, parent):
702 def _raw_input(self, prompt, ident, parent):
700 # Flush output before making the request.
703 # Flush output before making the request.
701 sys.stderr.flush()
704 sys.stderr.flush()
702 sys.stdout.flush()
705 sys.stdout.flush()
703
706
704 # Send the input request.
707 # Send the input request.
705 content = json_clean(dict(prompt=prompt))
708 content = json_clean(dict(prompt=prompt))
706 self.session.send(self.stdin_socket, u'input_request', content, parent,
709 self.session.send(self.stdin_socket, u'input_request', content, parent,
707 ident=ident)
710 ident=ident)
708
711
709 # Await a response.
712 # Await a response.
710 while True:
713 while True:
711 try:
714 try:
712 ident, reply = self.session.recv(self.stdin_socket, 0)
715 ident, reply = self.session.recv(self.stdin_socket, 0)
713 except Exception:
716 except Exception:
714 self.log.warn("Invalid Message:", exc_info=True)
717 self.log.warn("Invalid Message:", exc_info=True)
715 else:
718 else:
716 break
719 break
717 try:
720 try:
718 value = reply['content']['value']
721 value = reply['content']['value']
719 except:
722 except:
720 self.log.error("Got bad raw_input reply: ")
723 self.log.error("Got bad raw_input reply: ")
721 self.log.error("%s", parent)
724 self.log.error("%s", parent)
722 value = ''
725 value = ''
723 if value == '\x04':
726 if value == '\x04':
724 # EOF
727 # EOF
725 raise EOFError
728 raise EOFError
726 return value
729 return value
727
730
728 def _complete(self, msg):
731 def _complete(self, msg):
729 c = msg['content']
732 c = msg['content']
730 try:
733 try:
731 cpos = int(c['cursor_pos'])
734 cpos = int(c['cursor_pos'])
732 except:
735 except:
733 # If we don't get something that we can convert to an integer, at
736 # If we don't get something that we can convert to an integer, at
734 # least attempt the completion guessing the cursor is at the end of
737 # least attempt the completion guessing the cursor is at the end of
735 # the text, if there's any, and otherwise of the line
738 # the text, if there's any, and otherwise of the line
736 cpos = len(c['text'])
739 cpos = len(c['text'])
737 if cpos==0:
740 if cpos==0:
738 cpos = len(c['line'])
741 cpos = len(c['line'])
739 return self.shell.complete(c['text'], c['line'], cpos)
742 return self.shell.complete(c['text'], c['line'], cpos)
740
743
741 def _at_shutdown(self):
744 def _at_shutdown(self):
742 """Actions taken at shutdown by the kernel, called by python's atexit.
745 """Actions taken at shutdown by the kernel, called by python's atexit.
743 """
746 """
744 # io.rprint("Kernel at_shutdown") # dbg
747 # io.rprint("Kernel at_shutdown") # dbg
745 if self._shutdown_message is not None:
748 if self._shutdown_message is not None:
746 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
749 self.session.send(self.iopub_socket, self._shutdown_message, ident=self._topic('shutdown'))
747 self.log.debug("%s", self._shutdown_message)
750 self.log.debug("%s", self._shutdown_message)
748 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
751 [ s.flush(zmq.POLLOUT) for s in self.shell_streams ]
749
752
750 #-----------------------------------------------------------------------------
753 #-----------------------------------------------------------------------------
751 # Aliases and Flags for the IPKernelApp
754 # Aliases and Flags for the IPKernelApp
752 #-----------------------------------------------------------------------------
755 #-----------------------------------------------------------------------------
753
756
754 flags = dict(kernel_flags)
757 flags = dict(kernel_flags)
755 flags.update(shell_flags)
758 flags.update(shell_flags)
756
759
757 addflag = lambda *args: flags.update(boolean_flag(*args))
760 addflag = lambda *args: flags.update(boolean_flag(*args))
758
761
759 flags['pylab'] = (
762 flags['pylab'] = (
760 {'IPKernelApp' : {'pylab' : 'auto'}},
763 {'IPKernelApp' : {'pylab' : 'auto'}},
761 """Pre-load matplotlib and numpy for interactive use with
764 """Pre-load matplotlib and numpy for interactive use with
762 the default matplotlib backend."""
765 the default matplotlib backend."""
763 )
766 )
764
767
765 aliases = dict(kernel_aliases)
768 aliases = dict(kernel_aliases)
766 aliases.update(shell_aliases)
769 aliases.update(shell_aliases)
767
770
768 #-----------------------------------------------------------------------------
771 #-----------------------------------------------------------------------------
769 # The IPKernelApp class
772 # The IPKernelApp class
770 #-----------------------------------------------------------------------------
773 #-----------------------------------------------------------------------------
771
774
772 class IPKernelApp(KernelApp, InteractiveShellApp):
775 class IPKernelApp(KernelApp, InteractiveShellApp):
773 name = 'ipkernel'
776 name = 'ipkernel'
774
777
775 aliases = Dict(aliases)
778 aliases = Dict(aliases)
776 flags = Dict(flags)
779 flags = Dict(flags)
777 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
780 classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session]
778
781
779 @catch_config_error
782 @catch_config_error
780 def initialize(self, argv=None):
783 def initialize(self, argv=None):
781 super(IPKernelApp, self).initialize(argv)
784 super(IPKernelApp, self).initialize(argv)
782 self.init_path()
785 self.init_path()
783 self.init_shell()
786 self.init_shell()
784 self.init_gui_pylab()
787 self.init_gui_pylab()
785 self.init_extensions()
788 self.init_extensions()
786 self.init_code()
789 self.init_code()
787
790
788 def init_kernel(self):
791 def init_kernel(self):
789
792
790 shell_stream = ZMQStream(self.shell_socket)
793 shell_stream = ZMQStream(self.shell_socket)
791
794
792 kernel = Kernel(config=self.config, session=self.session,
795 kernel = Kernel(config=self.config, session=self.session,
793 shell_streams=[shell_stream],
796 shell_streams=[shell_stream],
794 iopub_socket=self.iopub_socket,
797 iopub_socket=self.iopub_socket,
795 stdin_socket=self.stdin_socket,
798 stdin_socket=self.stdin_socket,
796 log=self.log,
799 log=self.log,
797 profile_dir=self.profile_dir,
800 profile_dir=self.profile_dir,
798 )
801 )
799 self.kernel = kernel
802 self.kernel = kernel
800 kernel.record_ports(self.ports)
803 kernel.record_ports(self.ports)
801 shell = kernel.shell
804 shell = kernel.shell
802
805
803 def init_gui_pylab(self):
806 def init_gui_pylab(self):
804 """Enable GUI event loop integration, taking pylab into account."""
807 """Enable GUI event loop integration, taking pylab into account."""
805
808
806 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
809 # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab`
807 # to ensure that any exception is printed straight to stderr.
810 # to ensure that any exception is printed straight to stderr.
808 # Normally _showtraceback associates the reply with an execution,
811 # Normally _showtraceback associates the reply with an execution,
809 # which means frontends will never draw it, as this exception
812 # which means frontends will never draw it, as this exception
810 # is not associated with any execute request.
813 # is not associated with any execute request.
811
814
812 shell = self.shell
815 shell = self.shell
813 _showtraceback = shell._showtraceback
816 _showtraceback = shell._showtraceback
814 try:
817 try:
815 # replace pyerr-sending traceback with stderr
818 # replace pyerr-sending traceback with stderr
816 def print_tb(etype, evalue, stb):
819 def print_tb(etype, evalue, stb):
817 print ("GUI event loop or pylab initialization failed",
820 print ("GUI event loop or pylab initialization failed",
818 file=io.stderr)
821 file=io.stderr)
819 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
822 print (shell.InteractiveTB.stb2text(stb), file=io.stderr)
820 shell._showtraceback = print_tb
823 shell._showtraceback = print_tb
821 InteractiveShellApp.init_gui_pylab(self)
824 InteractiveShellApp.init_gui_pylab(self)
822 finally:
825 finally:
823 shell._showtraceback = _showtraceback
826 shell._showtraceback = _showtraceback
824
827
825 def init_shell(self):
828 def init_shell(self):
826 self.shell = self.kernel.shell
829 self.shell = self.kernel.shell
827 self.shell.configurables.append(self)
830 self.shell.configurables.append(self)
828
831
829
832
830 #-----------------------------------------------------------------------------
833 #-----------------------------------------------------------------------------
831 # Kernel main and launch functions
834 # Kernel main and launch functions
832 #-----------------------------------------------------------------------------
835 #-----------------------------------------------------------------------------
833
836
834 def launch_kernel(*args, **kwargs):
837 def launch_kernel(*args, **kwargs):
835 """Launches a localhost IPython kernel, binding to the specified ports.
838 """Launches a localhost IPython kernel, binding to the specified ports.
836
839
837 This function simply calls entry_point.base_launch_kernel with the right
840 This function simply calls entry_point.base_launch_kernel with the right
838 first command to start an ipkernel. See base_launch_kernel for arguments.
841 first command to start an ipkernel. See base_launch_kernel for arguments.
839
842
840 Returns
843 Returns
841 -------
844 -------
842 A tuple of form:
845 A tuple of form:
843 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
846 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
844 where kernel_process is a Popen object and the ports are integers.
847 where kernel_process is a Popen object and the ports are integers.
845 """
848 """
846 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
849 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
847 *args, **kwargs)
850 *args, **kwargs)
848
851
849
852
850 def embed_kernel(module=None, local_ns=None, **kwargs):
853 def embed_kernel(module=None, local_ns=None, **kwargs):
851 """Embed and start an IPython kernel in a given scope.
854 """Embed and start an IPython kernel in a given scope.
852
855
853 Parameters
856 Parameters
854 ----------
857 ----------
855 module : ModuleType, optional
858 module : ModuleType, optional
856 The module to load into IPython globals (default: caller)
859 The module to load into IPython globals (default: caller)
857 local_ns : dict, optional
860 local_ns : dict, optional
858 The namespace to load into IPython user namespace (default: caller)
861 The namespace to load into IPython user namespace (default: caller)
859
862
860 kwargs : various, optional
863 kwargs : various, optional
861 Further keyword args are relayed to the KernelApp constructor,
864 Further keyword args are relayed to the KernelApp constructor,
862 allowing configuration of the Kernel. Will only have an effect
865 allowing configuration of the Kernel. Will only have an effect
863 on the first embed_kernel call for a given process.
866 on the first embed_kernel call for a given process.
864
867
865 """
868 """
866 # get the app if it exists, or set it up if it doesn't
869 # get the app if it exists, or set it up if it doesn't
867 if IPKernelApp.initialized():
870 if IPKernelApp.initialized():
868 app = IPKernelApp.instance()
871 app = IPKernelApp.instance()
869 else:
872 else:
870 app = IPKernelApp.instance(**kwargs)
873 app = IPKernelApp.instance(**kwargs)
871 app.initialize([])
874 app.initialize([])
872 # Undo unnecessary sys module mangling from init_sys_modules.
875 # Undo unnecessary sys module mangling from init_sys_modules.
873 # This would not be necessary if we could prevent it
876 # This would not be necessary if we could prevent it
874 # in the first place by using a different InteractiveShell
877 # in the first place by using a different InteractiveShell
875 # subclass, as in the regular embed case.
878 # subclass, as in the regular embed case.
876 main = app.kernel.shell._orig_sys_modules_main_mod
879 main = app.kernel.shell._orig_sys_modules_main_mod
877 if main is not None:
880 if main is not None:
878 sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main
881 sys.modules[app.kernel.shell._orig_sys_modules_main_name] = main
879
882
880 # load the calling scope if not given
883 # load the calling scope if not given
881 (caller_module, caller_locals) = extract_module_locals(1)
884 (caller_module, caller_locals) = extract_module_locals(1)
882 if module is None:
885 if module is None:
883 module = caller_module
886 module = caller_module
884 if local_ns is None:
887 if local_ns is None:
885 local_ns = caller_locals
888 local_ns = caller_locals
886
889
887 app.kernel.user_module = module
890 app.kernel.user_module = module
888 app.kernel.user_ns = local_ns
891 app.kernel.user_ns = local_ns
889 app.shell.set_completer_frame()
892 app.shell.set_completer_frame()
890 app.start()
893 app.start()
891
894
892 def main():
895 def main():
893 """Run an IPKernel as an application"""
896 """Run an IPKernel as an application"""
894 app = IPKernelApp.instance()
897 app = IPKernelApp.instance()
895 app.initialize()
898 app.initialize()
896 app.start()
899 app.start()
897
900
898
901
899 if __name__ == '__main__':
902 if __name__ == '__main__':
900 main()
903 main()
@@ -1,48 +1,58 b''
1 """ A simple example of using the Qt console with an in-process kernel.
1 """ A simple example of using the Qt console with an in-process kernel.
2
2
3 We shall see how to create the frontend widget, create an in-process kernel,
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
4 push Python objects into the kernel's namespace, and execute code in the
5 kernel, both directly and via the frontend widget.
5 kernel, both directly and via the frontend widget.
6 """
6 """
7
7
8 from IPython.inprocess.ipkernel import InProcessKernel
8 from IPython.inprocess.ipkernel import InProcessKernel
9 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
9 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
10 from IPython.frontend.qt.inprocess_kernelmanager import QtInProcessKernelManager
10 from IPython.frontend.qt.inprocess_kernelmanager import QtInProcessKernelManager
11 from IPython.lib import guisupport
11 from IPython.lib import guisupport
12
12
13
13
14 def main():
14 def main():
15 app = guisupport.get_app_qt4()
15 app = guisupport.get_app_qt4()
16
16
17 # Create a kernel and populate the namespace.
17 # Create a kernel.
18 kernel = InProcessKernel()
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.
19 kernel.shell.push({'x': 0, 'y': 1, 'z': 2})
29 kernel.shell.push({'x': 0, 'y': 1, 'z': 2})
20
30
21 # Create a kernel manager for the frontend and register it with the kernel.
31 # Create a kernel manager for the frontend and register it with the kernel.
22 km = QtInProcessKernelManager(kernel=kernel)
32 km = QtInProcessKernelManager(kernel=kernel)
23 km.start_channels()
33 km.start_channels()
24 kernel.frontends.append(km)
34 kernel.frontends.append(km)
25
35
26 # Create the Qt console frontend.
36 # Create the Qt console frontend.
27 control = RichIPythonWidget()
37 control = RichIPythonWidget()
28 control.exit_requested.connect(app.quit)
38 control.exit_requested.connect(app.quit)
29 control.kernel_manager = km
39 control.kernel_manager = km
30 control.show()
40 control.show()
31
41
32 # Execute some code directly. Note where the output appears.
42 # Execute some code directly. Note where the output appears.
33 kernel.shell.run_cell('print "x=%r, y=%r, z=%r" % (x,y,z)')
43 kernel.shell.run_cell('print "x=%r, y=%r, z=%r" % (x,y,z)')
34
44
35 # Execute some code through the frontend (once the event loop is
45 # Execute some code through the frontend (once the event loop is
36 # running). Again, note where the output appears.
46 # running). Again, note where the output appears.
37 do_later(control.execute, '%who')
47 do_later(control.execute, '%who')
38
48
39 guisupport.start_event_loop_qt4(app)
49 guisupport.start_event_loop_qt4(app)
40
50
41
51
42 def do_later(func, *args, **kwds):
52 def do_later(func, *args, **kwds):
43 from IPython.external.qt import QtCore
53 from IPython.external.qt import QtCore
44 QtCore.QTimer.singleShot(0, lambda: func(*args, **kwds))
54 QtCore.QTimer.singleShot(0, lambda: func(*args, **kwds))
45
55
46
56
47 if __name__ == '__main__':
57 if __name__ == '__main__':
48 main()
58 main()
General Comments 0
You need to be logged in to leave comments. Login now