##// END OF EJS Templates
Minor work on the GUI support in the kernel.
Brian Granger -
Show More
@@ -1,140 +1,143 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # coding: utf-8
2 # coding: utf-8
3 """
3 """
4 Support for creating GUI apps and starting event loops.
4 Support for creating GUI apps and starting event loops.
5
5
6 IPython's GUI integration allows interative plotting and GUI usage in IPython
6 IPython's GUI integration allows interative plotting and GUI usage in IPython
7 session. IPython has two different types of GUI integration:
7 session. IPython has two different types of GUI integration:
8
8
9 1. The terminal based IPython supports GUI event loops through Python's
9 1. The terminal based IPython supports GUI event loops through Python's
10 PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically
10 PyOS_InputHook. PyOS_InputHook is a hook that Python calls periodically
11 whenever raw_input is waiting for a user to type code. We implement GUI
11 whenever raw_input is waiting for a user to type code. We implement GUI
12 support in the terminal by setting PyOS_InputHook to a function that
12 support in the terminal by setting PyOS_InputHook to a function that
13 iterates the event loop for a short while. It is important to note that
13 iterates the event loop for a short while. It is important to note that
14 in this situation, the real GUI event loop is NOT run in the normal
14 in this situation, the real GUI event loop is NOT run in the normal
15 manner, so you can't use the normal means to detect that it is running.
15 manner, so you can't use the normal means to detect that it is running.
16 2. In the two process IPython kernel/frontend, the GUI event loop is run in
16 2. In the two process IPython kernel/frontend, the GUI event loop is run in
17 the kernel. In this case, the event loop is run in the normal manner by
17 the kernel. In this case, the event loop is run in the normal manner by
18 calling the function or method of the GUI toolkit that starts the event
18 calling the function or method of the GUI toolkit that starts the event
19 loop.
19 loop.
20
20
21 In addition to starting the GUI event loops in one of these two ways, IPython
21 In addition to starting the GUI event loops in one of these two ways, IPython
22 will *always* create an appropriate GUI application object when GUi
22 will *always* create an appropriate GUI application object when GUi
23 integration is enabled.
23 integration is enabled.
24
24
25 If you want your GUI apps to run in IPython you need to do two things:
25 If you want your GUI apps to run in IPython you need to do two things:
26
26
27 1. Test to see if there is already an existing main application object. If
27 1. Test to see if there is already an existing main application object. If
28 there is, you should use it. If there is not an existing application object
28 there is, you should use it. If there is not an existing application object
29 you should create one.
29 you should create one.
30 2. Test to see if the GUI event loop is running. If it is, you should not
30 2. Test to see if the GUI event loop is running. If it is, you should not
31 start it. If the event loop is not running you may start it.
31 start it. If the event loop is not running you may start it.
32
32
33 This module contains functions for each toolkit that perform these things
33 This module contains functions for each toolkit that perform these things
34 in a consistent manner. Because of how PyOS_InputHook runs the event loop
34 in a consistent manner. Because of how PyOS_InputHook runs the event loop
35 you cannot detect if the event loop is running using the traditional calls
35 you cannot detect if the event loop is running using the traditional calls
36 (such as ``wx.GetApp.IsMainLoopRunning()`` in wxPython). If PyOS_InputHook is
36 (such as ``wx.GetApp.IsMainLoopRunning()`` in wxPython). If PyOS_InputHook is
37 set These methods will return a false negative. That is, they will say the
37 set These methods will return a false negative. That is, they will say the
38 event loop is not running, when is actually is. To work around this limitation
38 event loop is not running, when is actually is. To work around this limitation
39 we proposed the following informal protocol:
39 we proposed the following informal protocol:
40
40
41 * Whenever someone starts the event loop, they *must* set the ``_in_event_loop``
41 * Whenever someone starts the event loop, they *must* set the ``_in_event_loop``
42 attribute of the main application object to ``True``. This should be done
42 attribute of the main application object to ``True``. This should be done
43 regardless of how the event loop is actually run.
43 regardless of how the event loop is actually run.
44 * Whenever someone stops the event loop, they *must* set the ``_in_event_loop``
44 * Whenever someone stops the event loop, they *must* set the ``_in_event_loop``
45 attribute of the main application object to ``False``.
45 attribute of the main application object to ``False``.
46 * If you want to see if the event loop is running, you *must* use ``hasattr``
46 * If you want to see if the event loop is running, you *must* use ``hasattr``
47 to see if ``_in_event_loop`` attribute has been set. If it is set, you
47 to see if ``_in_event_loop`` attribute has been set. If it is set, you
48 *must* use its value. If it has not been set, you can query the toolkit
48 *must* use its value. If it has not been set, you can query the toolkit
49 in the normal manner.
49 in the normal manner.
50 * If you want GUI support and no one else has created an application or
51 started the event loop you *must* do this. We don't want projects to
52 attempt to defer these things to someone else if they themselves need it.
50
53
51 The functions below implement this logic for each GUI toolkit. If you need
54 The functions below implement this logic for each GUI toolkit. If you need
52 to create custom application subclasses, you will likely have to modify this
55 to create custom application subclasses, you will likely have to modify this
53 code for your own purposes. This code can be copied into your own project
56 code for your own purposes. This code can be copied into your own project
54 so you don't have to depend on IPython.
57 so you don't have to depend on IPython.
55
58
56 """
59 """
57
60
58 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
59 # Copyright (C) 2008-2010 The IPython Development Team
62 # Copyright (C) 2008-2010 The IPython Development Team
60 #
63 #
61 # Distributed under the terms of the BSD License. The full license is in
64 # Distributed under the terms of the BSD License. The full license is in
62 # the file COPYING, distributed as part of this software.
65 # the file COPYING, distributed as part of this software.
63 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
64
67
65 #-----------------------------------------------------------------------------
68 #-----------------------------------------------------------------------------
66 # Imports
69 # Imports
67 #-----------------------------------------------------------------------------
70 #-----------------------------------------------------------------------------
68
71
69 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
70 # wx
73 # wx
71 #-----------------------------------------------------------------------------
74 #-----------------------------------------------------------------------------
72
75
73 def get_app_wx(*args, **kwargs):
76 def get_app_wx(*args, **kwargs):
74 """Create a new wx app or return an exiting one."""
77 """Create a new wx app or return an exiting one."""
75 import wx
78 import wx
76 app = wx.GetApp()
79 app = wx.GetApp()
77 if app is None:
80 if app is None:
78 app = wx.PySimpleApp(*args, **kwargs)
81 app = wx.PySimpleApp(*args, **kwargs)
79 return app
82 return app
80
83
81 def is_event_loop_running_wx(app=None):
84 def is_event_loop_running_wx(app=None):
82 """Is the wx event loop running."""
85 """Is the wx event loop running."""
83 if app is None:
86 if app is None:
84 app = get_app_wx()
87 app = get_app_wx()
85 if hasattr(app, '_in_event_loop'):
88 if hasattr(app, '_in_event_loop'):
86 return app._in_event_loop
89 return app._in_event_loop
87 else:
90 else:
88 return app.IsMainLoopRunning()
91 return app.IsMainLoopRunning()
89
92
90 def start_event_loop_wx(app=None):
93 def start_event_loop_wx(app=None):
91 """Start the wx event loop in a consistent manner."""
94 """Start the wx event loop in a consistent manner."""
92 if app is None:
95 if app is None:
93 app = get_app_wx()
96 app = get_app_wx()
94 if not is_event_loop_running_wx(app):
97 if not is_event_loop_running_wx(app):
95 app._in_event_loop = True
98 app._in_event_loop = True
96 app.MainLoop()
99 app.MainLoop()
97 app._in_event_loop = False
100 app._in_event_loop = False
98 else:
101 else:
99 app._in_event_loop = True
102 app._in_event_loop = True
100
103
101 #-----------------------------------------------------------------------------
104 #-----------------------------------------------------------------------------
102 # qt4
105 # qt4
103 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
104
107
105 def get_app_qt4(*args, **kwargs):
108 def get_app_qt4(*args, **kwargs):
106 """Create a new qt4 app or return an existing one."""
109 """Create a new qt4 app or return an existing one."""
107 from PyQt4 import QtGui
110 from PyQt4 import QtGui
108 app = QtGui.QApplication.instance()
111 app = QtGui.QApplication.instance()
109 if app is None:
112 if app is None:
110 app = QtGui.QApplication(*args, **kwargs)
113 app = QtGui.QApplication(*args, **kwargs)
111 return app
114 return app
112
115
113 def is_event_loop_running_qt4(app=None):
116 def is_event_loop_running_qt4(app=None):
114 """Is the qt4 event loop running."""
117 """Is the qt4 event loop running."""
115 if app is None:
118 if app is None:
116 app = get_app_qt4()
119 app = get_app_qt4()
117 if hasattr(app, '_in_event_loop'):
120 if hasattr(app, '_in_event_loop'):
118 return app._in_event_loop
121 return app._in_event_loop
119 else:
122 else:
120 # Does qt4 provide a other way to detect this?
123 # Does qt4 provide a other way to detect this?
121 return False
124 return False
122
125
123 def start_event_loop_qt4(app=None):
126 def start_event_loop_qt4(app=None):
124 """Start the qt4 event loop in a consistent manner."""
127 """Start the qt4 event loop in a consistent manner."""
125 if app is None:
128 if app is None:
126 app = get_app_qt4()
129 app = get_app_qt4()
127 if not is_event_loop_running_qt4(app):
130 if not is_event_loop_running_qt4(app):
128 app._in_event_loop = True
131 app._in_event_loop = True
129 app.exec_()
132 app.exec_()
130 app._in_event_loop = False
133 app._in_event_loop = False
131 else:
134 else:
132 app._in_event_loop = True
135 app._in_event_loop = True
133
136
134 #-----------------------------------------------------------------------------
137 #-----------------------------------------------------------------------------
135 # Tk
138 # Tk
136 #-----------------------------------------------------------------------------
139 #-----------------------------------------------------------------------------
137
140
138 #-----------------------------------------------------------------------------
141 #-----------------------------------------------------------------------------
139 # gtk
142 # gtk
140 #-----------------------------------------------------------------------------
143 #-----------------------------------------------------------------------------
@@ -1,456 +1,459 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 sys
20 import sys
21 import time
21 import time
22 import traceback
22 import traceback
23
23
24 # System library imports.
24 # System library imports.
25 import zmq
25 import zmq
26
26
27 # Local imports.
27 # Local imports.
28 from IPython.config.configurable import Configurable
28 from IPython.config.configurable import Configurable
29 from IPython.utils import io
29 from IPython.utils import io
30 from IPython.lib import pylabtools
30 from IPython.lib import pylabtools
31 from IPython.utils.traitlets import Instance
31 from IPython.utils.traitlets import Instance
32 from entry_point import base_launch_kernel, make_argument_parser, make_kernel, \
32 from entry_point import base_launch_kernel, make_argument_parser, make_kernel, \
33 start_kernel
33 start_kernel
34 from iostream import OutStream
34 from iostream import OutStream
35 from session import Session, Message
35 from session import Session, Message
36 from zmqshell import ZMQInteractiveShell
36 from zmqshell import ZMQInteractiveShell
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Main kernel class
39 # Main kernel class
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 class Kernel(Configurable):
42 class Kernel(Configurable):
43
43
44 #---------------------------------------------------------------------------
44 #---------------------------------------------------------------------------
45 # Kernel interface
45 # Kernel interface
46 #---------------------------------------------------------------------------
46 #---------------------------------------------------------------------------
47
47
48 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
48 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
49 session = Instance(Session)
49 session = Instance(Session)
50 reply_socket = Instance('zmq.Socket')
50 reply_socket = Instance('zmq.Socket')
51 pub_socket = Instance('zmq.Socket')
51 pub_socket = Instance('zmq.Socket')
52 req_socket = Instance('zmq.Socket')
52 req_socket = Instance('zmq.Socket')
53
53
54 def __init__(self, **kwargs):
54 def __init__(self, **kwargs):
55 super(Kernel, self).__init__(**kwargs)
55 super(Kernel, self).__init__(**kwargs)
56
56
57 # Initialize the InteractiveShell subclass
57 # Initialize the InteractiveShell subclass
58 self.shell = ZMQInteractiveShell.instance()
58 self.shell = ZMQInteractiveShell.instance()
59 self.shell.displayhook.session = self.session
59 self.shell.displayhook.session = self.session
60 self.shell.displayhook.pub_socket = self.pub_socket
60 self.shell.displayhook.pub_socket = self.pub_socket
61
61
62 # TMP - hack while developing
62 # TMP - hack while developing
63 self.shell._reply_content = None
63 self.shell._reply_content = None
64
64
65 # Build dict of handlers for message types
65 # Build dict of handlers for message types
66 msg_types = [ 'execute_request', 'complete_request',
66 msg_types = [ 'execute_request', 'complete_request',
67 'object_info_request', 'prompt_request',
67 'object_info_request', 'prompt_request',
68 'history_request' ]
68 'history_request' ]
69 self.handlers = {}
69 self.handlers = {}
70 for msg_type in msg_types:
70 for msg_type in msg_types:
71 self.handlers[msg_type] = getattr(self, msg_type)
71 self.handlers[msg_type] = getattr(self, msg_type)
72
72
73 def do_one_iteration(self):
73 def do_one_iteration(self):
74 try:
74 try:
75 ident = self.reply_socket.recv(zmq.NOBLOCK)
75 ident = self.reply_socket.recv(zmq.NOBLOCK)
76 except zmq.ZMQError, e:
76 except zmq.ZMQError, e:
77 if e.errno == zmq.EAGAIN:
77 if e.errno == zmq.EAGAIN:
78 return
78 return
79 else:
79 else:
80 raise
80 raise
81 # FIXME: Bug in pyzmq/zmq?
81 # FIXME: Bug in pyzmq/zmq?
82 # assert self.reply_socket.rcvmore(), "Missing message part."
82 # assert self.reply_socket.rcvmore(), "Missing message part."
83 msg = self.reply_socket.recv_json()
83 msg = self.reply_socket.recv_json()
84 omsg = Message(msg)
84 omsg = Message(msg)
85 io.raw_print('\n')
85 io.raw_print('\n')
86 io.raw_print(omsg)
86 io.raw_print(omsg)
87 handler = self.handlers.get(omsg.msg_type, None)
87 handler = self.handlers.get(omsg.msg_type, None)
88 if handler is None:
88 if handler is None:
89 io.raw_print_err("UNKNOWN MESSAGE TYPE:", omsg)
89 io.raw_print_err("UNKNOWN MESSAGE TYPE:", omsg)
90 else:
90 else:
91 handler(ident, omsg)
91 handler(ident, omsg)
92
92
93 def start(self):
93 def start(self):
94 """ Start the kernel main loop.
94 """ Start the kernel main loop.
95 """
95 """
96 while True:
96 while True:
97 time.sleep(0.05)
97 time.sleep(0.05)
98 self.do_one_iteration()
98 self.do_one_iteration()
99
99
100
100
101 #---------------------------------------------------------------------------
101 #---------------------------------------------------------------------------
102 # Kernel request handlers
102 # Kernel request handlers
103 #---------------------------------------------------------------------------
103 #---------------------------------------------------------------------------
104
104
105 def execute_request(self, ident, parent):
105 def execute_request(self, ident, parent):
106 try:
106 try:
107 code = parent[u'content'][u'code']
107 code = parent[u'content'][u'code']
108 except:
108 except:
109 io.raw_print_err("Got bad msg: ")
109 io.raw_print_err("Got bad msg: ")
110 io.raw_print_err(Message(parent))
110 io.raw_print_err(Message(parent))
111 return
111 return
112 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
112 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
113 self.pub_socket.send_json(pyin_msg)
113 self.pub_socket.send_json(pyin_msg)
114
114
115 try:
115 try:
116 # Replace raw_input. Note that is not sufficient to replace
116 # Replace raw_input. Note that is not sufficient to replace
117 # raw_input in the user namespace.
117 # raw_input in the user namespace.
118 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
118 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
119 __builtin__.raw_input = raw_input
119 __builtin__.raw_input = raw_input
120
120
121 # Set the parent message of the display hook and out streams.
121 # Set the parent message of the display hook and out streams.
122 self.shell.displayhook.set_parent(parent)
122 self.shell.displayhook.set_parent(parent)
123 sys.stdout.set_parent(parent)
123 sys.stdout.set_parent(parent)
124 sys.stderr.set_parent(parent)
124 sys.stderr.set_parent(parent)
125
125
126 # FIXME: runlines calls the exception handler itself. We should
126 # FIXME: runlines calls the exception handler itself. We should
127 # clean this up.
127 # clean this up.
128 self.shell._reply_content = None
128 self.shell._reply_content = None
129 self.shell.runlines(code)
129 self.shell.runlines(code)
130 except:
130 except:
131 # FIXME: this code right now isn't being used yet by default,
131 # FIXME: this code right now isn't being used yet by default,
132 # because the runlines() call above directly fires off exception
132 # because the runlines() call above directly fires off exception
133 # reporting. This code, therefore, is only active in the scenario
133 # reporting. This code, therefore, is only active in the scenario
134 # where runlines itself has an unhandled exception. We need to
134 # where runlines itself has an unhandled exception. We need to
135 # uniformize this, for all exception construction to come from a
135 # uniformize this, for all exception construction to come from a
136 # single location in the codbase.
136 # single location in the codbase.
137 etype, evalue, tb = sys.exc_info()
137 etype, evalue, tb = sys.exc_info()
138 tb_list = traceback.format_exception(etype, evalue, tb)
138 tb_list = traceback.format_exception(etype, evalue, tb)
139 reply_content = self.shell._showtraceback(etype, evalue, tb_list)
139 reply_content = self.shell._showtraceback(etype, evalue, tb_list)
140 else:
140 else:
141 payload = self.shell.payload_manager.read_payload()
141 payload = self.shell.payload_manager.read_payload()
142 # Be agressive about clearing the payload because we don't want
142 # Be agressive about clearing the payload because we don't want
143 # it to sit in memory until the next execute_request comes in.
143 # it to sit in memory until the next execute_request comes in.
144 self.shell.payload_manager.clear_payload()
144 self.shell.payload_manager.clear_payload()
145 reply_content = { 'status' : 'ok', 'payload' : payload }
145 reply_content = { 'status' : 'ok', 'payload' : payload }
146
146
147 # Compute the prompt information
147 # Compute the prompt information
148 prompt_number = self.shell.displayhook.prompt_count
148 prompt_number = self.shell.displayhook.prompt_count
149 reply_content['prompt_number'] = prompt_number
149 reply_content['prompt_number'] = prompt_number
150 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
150 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
151 next_prompt = {'prompt_string' : prompt_string,
151 next_prompt = {'prompt_string' : prompt_string,
152 'prompt_number' : prompt_number+1,
152 'prompt_number' : prompt_number+1,
153 'input_sep' : self.shell.displayhook.input_sep}
153 'input_sep' : self.shell.displayhook.input_sep}
154 reply_content['next_prompt'] = next_prompt
154 reply_content['next_prompt'] = next_prompt
155
155
156 # TMP - fish exception info out of shell, possibly left there by
156 # TMP - fish exception info out of shell, possibly left there by
157 # runlines
157 # runlines
158 if self.shell._reply_content is not None:
158 if self.shell._reply_content is not None:
159 reply_content.update(self.shell._reply_content)
159 reply_content.update(self.shell._reply_content)
160
160
161 # Flush output before sending the reply.
161 # Flush output before sending the reply.
162 sys.stderr.flush()
162 sys.stderr.flush()
163 sys.stdout.flush()
163 sys.stdout.flush()
164
164
165 # Send the reply.
165 # Send the reply.
166 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
166 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
167 io.raw_print(Message(reply_msg))
167 io.raw_print(Message(reply_msg))
168 self.reply_socket.send(ident, zmq.SNDMORE)
168 self.reply_socket.send(ident, zmq.SNDMORE)
169 self.reply_socket.send_json(reply_msg)
169 self.reply_socket.send_json(reply_msg)
170 if reply_msg['content']['status'] == u'error':
170 if reply_msg['content']['status'] == u'error':
171 self._abort_queue()
171 self._abort_queue()
172
172
173 def complete_request(self, ident, parent):
173 def complete_request(self, ident, parent):
174 txt, matches = self._complete(parent)
174 txt, matches = self._complete(parent)
175 matches = {'matches' : matches,
175 matches = {'matches' : matches,
176 'matched_text' : txt,
176 'matched_text' : txt,
177 'status' : 'ok'}
177 'status' : 'ok'}
178 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
178 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
179 matches, parent, ident)
179 matches, parent, ident)
180 io.raw_print(completion_msg)
180 io.raw_print(completion_msg)
181
181
182 def object_info_request(self, ident, parent):
182 def object_info_request(self, ident, parent):
183 context = parent['content']['oname'].split('.')
183 context = parent['content']['oname'].split('.')
184 object_info = self._object_info(context)
184 object_info = self._object_info(context)
185 msg = self.session.send(self.reply_socket, 'object_info_reply',
185 msg = self.session.send(self.reply_socket, 'object_info_reply',
186 object_info, parent, ident)
186 object_info, parent, ident)
187 io.raw_print(msg)
187 io.raw_print(msg)
188
188
189 def prompt_request(self, ident, parent):
189 def prompt_request(self, ident, parent):
190 prompt_number = self.shell.displayhook.prompt_count
190 prompt_number = self.shell.displayhook.prompt_count
191 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
191 prompt_string = self.shell.displayhook.prompt1.peek_next_prompt()
192 content = {'prompt_string' : prompt_string,
192 content = {'prompt_string' : prompt_string,
193 'prompt_number' : prompt_number+1,
193 'prompt_number' : prompt_number+1,
194 'input_sep' : self.shell.displayhook.input_sep}
194 'input_sep' : self.shell.displayhook.input_sep}
195 msg = self.session.send(self.reply_socket, 'prompt_reply',
195 msg = self.session.send(self.reply_socket, 'prompt_reply',
196 content, parent, ident)
196 content, parent, ident)
197 io.raw_print(msg)
197 io.raw_print(msg)
198
198
199 def history_request(self, ident, parent):
199 def history_request(self, ident, parent):
200 output = parent['content']['output']
200 output = parent['content']['output']
201 index = parent['content']['index']
201 index = parent['content']['index']
202 raw = parent['content']['raw']
202 raw = parent['content']['raw']
203 hist = self.shell.get_history(index=index, raw=raw, output=output)
203 hist = self.shell.get_history(index=index, raw=raw, output=output)
204 content = {'history' : hist}
204 content = {'history' : hist}
205 msg = self.session.send(self.reply_socket, 'history_reply',
205 msg = self.session.send(self.reply_socket, 'history_reply',
206 content, parent, ident)
206 content, parent, ident)
207 io.raw_print(msg)
207 io.raw_print(msg)
208
208
209 #---------------------------------------------------------------------------
209 #---------------------------------------------------------------------------
210 # Protected interface
210 # Protected interface
211 #---------------------------------------------------------------------------
211 #---------------------------------------------------------------------------
212
212
213 def _abort_queue(self):
213 def _abort_queue(self):
214 while True:
214 while True:
215 try:
215 try:
216 ident = self.reply_socket.recv(zmq.NOBLOCK)
216 ident = self.reply_socket.recv(zmq.NOBLOCK)
217 except zmq.ZMQError, e:
217 except zmq.ZMQError, e:
218 if e.errno == zmq.EAGAIN:
218 if e.errno == zmq.EAGAIN:
219 break
219 break
220 else:
220 else:
221 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
221 assert self.reply_socket.rcvmore(), "Unexpected missing message part."
222 msg = self.reply_socket.recv_json()
222 msg = self.reply_socket.recv_json()
223 io.raw_print("Aborting:\n", Message(msg))
223 io.raw_print("Aborting:\n", Message(msg))
224 msg_type = msg['msg_type']
224 msg_type = msg['msg_type']
225 reply_type = msg_type.split('_')[0] + '_reply'
225 reply_type = msg_type.split('_')[0] + '_reply'
226 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
226 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
227 io.raw_print(Message(reply_msg))
227 io.raw_print(Message(reply_msg))
228 self.reply_socket.send(ident,zmq.SNDMORE)
228 self.reply_socket.send(ident,zmq.SNDMORE)
229 self.reply_socket.send_json(reply_msg)
229 self.reply_socket.send_json(reply_msg)
230 # We need to wait a bit for requests to come in. This can probably
230 # We need to wait a bit for requests to come in. This can probably
231 # be set shorter for true asynchronous clients.
231 # be set shorter for true asynchronous clients.
232 time.sleep(0.1)
232 time.sleep(0.1)
233
233
234 def _raw_input(self, prompt, ident, parent):
234 def _raw_input(self, prompt, ident, parent):
235 # Flush output before making the request.
235 # Flush output before making the request.
236 sys.stderr.flush()
236 sys.stderr.flush()
237 sys.stdout.flush()
237 sys.stdout.flush()
238
238
239 # Send the input request.
239 # Send the input request.
240 content = dict(prompt=prompt)
240 content = dict(prompt=prompt)
241 msg = self.session.msg(u'input_request', content, parent)
241 msg = self.session.msg(u'input_request', content, parent)
242 self.req_socket.send_json(msg)
242 self.req_socket.send_json(msg)
243
243
244 # Await a response.
244 # Await a response.
245 reply = self.req_socket.recv_json()
245 reply = self.req_socket.recv_json()
246 try:
246 try:
247 value = reply['content']['value']
247 value = reply['content']['value']
248 except:
248 except:
249 io.raw_print_err("Got bad raw_input reply: ")
249 io.raw_print_err("Got bad raw_input reply: ")
250 io.raw_print_err(Message(parent))
250 io.raw_print_err(Message(parent))
251 value = ''
251 value = ''
252 return value
252 return value
253
253
254 def _complete(self, msg):
254 def _complete(self, msg):
255 c = msg['content']
255 c = msg['content']
256 try:
256 try:
257 cpos = int(c['cursor_pos'])
257 cpos = int(c['cursor_pos'])
258 except:
258 except:
259 # If we don't get something that we can convert to an integer, at
259 # If we don't get something that we can convert to an integer, at
260 # least attempt the completion guessing the cursor is at the end of
260 # least attempt the completion guessing the cursor is at the end of
261 # the text, if there's any, and otherwise of the line
261 # the text, if there's any, and otherwise of the line
262 cpos = len(c['text'])
262 cpos = len(c['text'])
263 if cpos==0:
263 if cpos==0:
264 cpos = len(c['line'])
264 cpos = len(c['line'])
265 return self.shell.complete(c['text'], c['line'], cpos)
265 return self.shell.complete(c['text'], c['line'], cpos)
266
266
267 def _object_info(self, context):
267 def _object_info(self, context):
268 symbol, leftover = self._symbol_from_context(context)
268 symbol, leftover = self._symbol_from_context(context)
269 if symbol is not None and not leftover:
269 if symbol is not None and not leftover:
270 doc = getattr(symbol, '__doc__', '')
270 doc = getattr(symbol, '__doc__', '')
271 else:
271 else:
272 doc = ''
272 doc = ''
273 object_info = dict(docstring = doc)
273 object_info = dict(docstring = doc)
274 return object_info
274 return object_info
275
275
276 def _symbol_from_context(self, context):
276 def _symbol_from_context(self, context):
277 if not context:
277 if not context:
278 return None, context
278 return None, context
279
279
280 base_symbol_string = context[0]
280 base_symbol_string = context[0]
281 symbol = self.shell.user_ns.get(base_symbol_string, None)
281 symbol = self.shell.user_ns.get(base_symbol_string, None)
282 if symbol is None:
282 if symbol is None:
283 symbol = __builtin__.__dict__.get(base_symbol_string, None)
283 symbol = __builtin__.__dict__.get(base_symbol_string, None)
284 if symbol is None:
284 if symbol is None:
285 return None, context
285 return None, context
286
286
287 context = context[1:]
287 context = context[1:]
288 for i, name in enumerate(context):
288 for i, name in enumerate(context):
289 new_symbol = getattr(symbol, name, None)
289 new_symbol = getattr(symbol, name, None)
290 if new_symbol is None:
290 if new_symbol is None:
291 return symbol, context[i:]
291 return symbol, context[i:]
292 else:
292 else:
293 symbol = new_symbol
293 symbol = new_symbol
294
294
295 return symbol, []
295 return symbol, []
296
296
297
297
298 class QtKernel(Kernel):
298 class QtKernel(Kernel):
299 """A Kernel subclass with Qt support."""
299 """A Kernel subclass with Qt support."""
300
300
301 def start(self):
301 def start(self):
302 """Start a kernel with QtPy4 event loop integration."""
302 """Start a kernel with QtPy4 event loop integration."""
303
303
304 from PyQt4 import QtGui, QtCore
304 from PyQt4 import QtGui, QtCore
305 self.app = QtGui.QApplication([])
305 from IPython.lib.guisupport import (
306 get_app_qt4, start_event_loop_qt4
307 )
308 self.app = get_app_qt4([" "])
306 self.app.setQuitOnLastWindowClosed (False)
309 self.app.setQuitOnLastWindowClosed(False)
307 self.timer = QtCore.QTimer()
310 self.timer = QtCore.QTimer()
308 self.timer.timeout.connect(self.do_one_iteration)
311 self.timer.timeout.connect(self.do_one_iteration)
309 self.timer.start(50)
312 self.timer.start(50)
310 self.app.exec_()
313 start_event_loop_qt4(self.app)
311
312
314
313 class WxKernel(Kernel):
315 class WxKernel(Kernel):
314 """A Kernel subclass with Wx support."""
316 """A Kernel subclass with Wx support."""
315
317
316 def start(self):
318 def start(self):
317 """Start a kernel with wx event loop support."""
319 """Start a kernel with wx event loop support."""
318
320
319 import wx
321 import wx
322 from IPython.lib.guisupport import start_event_loop_wx
320 doi = self.do_one_iteration
323 doi = self.do_one_iteration
321
324
322 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
325 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
323 # We make the Frame hidden when we create it in the main app below.
326 # We make the Frame hidden when we create it in the main app below.
324 class TimerFrame(wx.Frame):
327 class TimerFrame(wx.Frame):
325 def __init__(self, func):
328 def __init__(self, func):
326 wx.Frame.__init__(self, None, -1)
329 wx.Frame.__init__(self, None, -1)
327 self.timer = wx.Timer(self)
330 self.timer = wx.Timer(self)
328 self.timer.Start(50)
331 self.timer.Start(50)
329 self.Bind(wx.EVT_TIMER, self.on_timer)
332 self.Bind(wx.EVT_TIMER, self.on_timer)
330 self.func = func
333 self.func = func
331 def on_timer(self, event):
334 def on_timer(self, event):
332 self.func()
335 self.func()
333
336
334 # We need a custom wx.App to create our Frame subclass that has the
337 # We need a custom wx.App to create our Frame subclass that has the
335 # wx.Timer to drive the ZMQ event loop.
338 # wx.Timer to drive the ZMQ event loop.
336 class IPWxApp(wx.App):
339 class IPWxApp(wx.App):
337 def OnInit(self):
340 def OnInit(self):
338 self.frame = TimerFrame(doi)
341 self.frame = TimerFrame(doi)
339 self.frame.Show(False)
342 self.frame.Show(False)
340 return True
343 return True
341
344
342 # The redirect=False here makes sure that wx doesn't replace
345 # The redirect=False here makes sure that wx doesn't replace
343 # sys.stdout/stderr with its own classes.
346 # sys.stdout/stderr with its own classes.
344 self.app = IPWxApp(redirect=False)
347 self.app = IPWxApp(redirect=False)
345 self.app.MainLoop()
348 start_event_loop_wx(self.app)
346
349
347
350
348 class TkKernel(Kernel):
351 class TkKernel(Kernel):
349 """A Kernel subclass with Tk support."""
352 """A Kernel subclass with Tk support."""
350
353
351 def start(self):
354 def start(self):
352 """Start a Tk enabled event loop."""
355 """Start a Tk enabled event loop."""
353
356
354 import Tkinter
357 import Tkinter
355 doi = self.do_one_iteration
358 doi = self.do_one_iteration
356
359
357 # For Tkinter, we create a Tk object and call its withdraw method.
360 # For Tkinter, we create a Tk object and call its withdraw method.
358 class Timer(object):
361 class Timer(object):
359 def __init__(self, func):
362 def __init__(self, func):
360 self.app = Tkinter.Tk()
363 self.app = Tkinter.Tk()
361 self.app.withdraw()
364 self.app.withdraw()
362 self.func = func
365 self.func = func
363 def on_timer(self):
366 def on_timer(self):
364 self.func()
367 self.func()
365 self.app.after(50, self.on_timer)
368 self.app.after(50, self.on_timer)
366 def start(self):
369 def start(self):
367 self.on_timer() # Call it once to get things going.
370 self.on_timer() # Call it once to get things going.
368 self.app.mainloop()
371 self.app.mainloop()
369
372
370 self.timer = Timer(doi)
373 self.timer = Timer(doi)
371 self.timer.start()
374 self.timer.start()
372
375
373 #-----------------------------------------------------------------------------
376 #-----------------------------------------------------------------------------
374 # Kernel main and launch functions
377 # Kernel main and launch functions
375 #-----------------------------------------------------------------------------
378 #-----------------------------------------------------------------------------
376
379
377 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,
380 def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False,
378 pylab=False):
381 pylab=False):
379 """ Launches a localhost kernel, binding to the specified ports.
382 """ Launches a localhost kernel, binding to the specified ports.
380
383
381 Parameters
384 Parameters
382 ----------
385 ----------
383 xrep_port : int, optional
386 xrep_port : int, optional
384 The port to use for XREP channel.
387 The port to use for XREP channel.
385
388
386 pub_port : int, optional
389 pub_port : int, optional
387 The port to use for the SUB channel.
390 The port to use for the SUB channel.
388
391
389 req_port : int, optional
392 req_port : int, optional
390 The port to use for the REQ (raw input) channel.
393 The port to use for the REQ (raw input) channel.
391
394
392 independent : bool, optional (default False)
395 independent : bool, optional (default False)
393 If set, the kernel process is guaranteed to survive if this process
396 If set, the kernel process is guaranteed to survive if this process
394 dies. If not set, an effort is made to ensure that the kernel is killed
397 dies. If not set, an effort is made to ensure that the kernel is killed
395 when this process dies. Note that in this case it is still good practice
398 when this process dies. Note that in this case it is still good practice
396 to kill kernels manually before exiting.
399 to kill kernels manually before exiting.
397
400
398 pylab : bool or string, optional (default False)
401 pylab : bool or string, optional (default False)
399 If not False, the kernel will be launched with pylab enabled. If a
402 If not False, the kernel will be launched with pylab enabled. If a
400 string is passed, matplotlib will use the specified backend. Otherwise,
403 string is passed, matplotlib will use the specified backend. Otherwise,
401 matplotlib's default backend will be used.
404 matplotlib's default backend will be used.
402
405
403 Returns
406 Returns
404 -------
407 -------
405 A tuple of form:
408 A tuple of form:
406 (kernel_process, xrep_port, pub_port, req_port)
409 (kernel_process, xrep_port, pub_port, req_port)
407 where kernel_process is a Popen object and the ports are integers.
410 where kernel_process is a Popen object and the ports are integers.
408 """
411 """
409 extra_arguments = []
412 extra_arguments = []
410 if pylab:
413 if pylab:
411 extra_arguments.append('--pylab')
414 extra_arguments.append('--pylab')
412 if isinstance(pylab, basestring):
415 if isinstance(pylab, basestring):
413 extra_arguments.append(pylab)
416 extra_arguments.append(pylab)
414 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
417 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
415 xrep_port, pub_port, req_port, independent,
418 xrep_port, pub_port, req_port, independent,
416 extra_arguments)
419 extra_arguments)
417
420
418 def main():
421 def main():
419 """ The IPython kernel main entry point.
422 """ The IPython kernel main entry point.
420 """
423 """
421 parser = make_argument_parser()
424 parser = make_argument_parser()
422 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
425 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
423 const='auto', help = \
426 const='auto', help = \
424 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
427 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
425 given, the GUI backend is matplotlib's, otherwise use one of: \
428 given, the GUI backend is matplotlib's, otherwise use one of: \
426 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
429 ['tk', 'gtk', 'qt', 'wx', 'payload-svg'].")
427 namespace = parser.parse_args()
430 namespace = parser.parse_args()
428
431
429 kernel_class = Kernel
432 kernel_class = Kernel
430
433
431 _kernel_classes = {
434 _kernel_classes = {
432 'qt' : QtKernel,
435 'qt' : QtKernel,
433 'qt4' : QtKernel,
436 'qt4' : QtKernel,
434 'payload-svg':Kernel,
437 'payload-svg':Kernel,
435 'wx' : WxKernel,
438 'wx' : WxKernel,
436 'tk' : TkKernel
439 'tk' : TkKernel
437 }
440 }
438 if namespace.pylab:
441 if namespace.pylab:
439 if namespace.pylab == 'auto':
442 if namespace.pylab == 'auto':
440 gui, backend = pylabtools.find_gui_and_backend()
443 gui, backend = pylabtools.find_gui_and_backend()
441 else:
444 else:
442 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
445 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
443 kernel_class = _kernel_classes.get(gui)
446 kernel_class = _kernel_classes.get(gui)
444 if kernel_class is None:
447 if kernel_class is None:
445 raise ValueError('GUI is not supported: %r' % gui)
448 raise ValueError('GUI is not supported: %r' % gui)
446 pylabtools.activate_matplotlib(backend)
449 pylabtools.activate_matplotlib(backend)
447
450
448 kernel = make_kernel(namespace, kernel_class, OutStream)
451 kernel = make_kernel(namespace, kernel_class, OutStream)
449
452
450 if namespace.pylab:
453 if namespace.pylab:
451 pylabtools.import_pylab(kernel.shell.user_ns)
454 pylabtools.import_pylab(kernel.shell.user_ns)
452
455
453 start_kernel(namespace, kernel)
456 start_kernel(namespace, kernel)
454
457
455 if __name__ == '__main__':
458 if __name__ == '__main__':
456 main()
459 main()
General Comments 0
You need to be logged in to leave comments. Login now