##// END OF EJS Templates
Simplify handling of messaging in zmqterminal.
Thomas Kluyver -
Show More
@@ -1,42 +1,41 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 import readline
2 import readline
3 from Queue import Empty
3 from Queue import Empty
4
4
5 class ClientCompleter2p(object):
5 class ClientCompleter2p(object):
6 """Client-side completion machinery.
6 """Client-side completion machinery.
7
7
8 How it works: self.complete will be called multiple times, with
8 How it works: self.complete will be called multiple times, with
9 state=0,1,2,... When state=0 it should compute ALL the completion matches,
9 state=0,1,2,... When state=0 it should compute ALL the completion matches,
10 and then return them for each value of state."""
10 and then return them for each value of state."""
11
11
12 def __init__(self,client, km):
12 def __init__(self,client, km):
13 self.km = km
13 self.km = km
14 self.matches = []
14 self.matches = []
15 self.client = client
15 self.client = client
16
16
17 def complete_request(self,text):
17 def complete_request(self,text):
18 line = readline.get_line_buffer()
18 line = readline.get_line_buffer()
19 #msg_id = self.km.xreq_channel.complete(text=text,line=line)#this method is not working, the code not continue
19 cursor_pos = readline.get_endidx()
20 msg = self.km.session.send(self.km.xreq_channel.socket,
20
21 'complete_request',
22 dict(text=text, line=line))
23 # send completion request to kernel
21 # send completion request to kernel
24 # Give the kernel up to 0.5s to respond
22 # Give the kernel up to 0.5s to respond
23 msg_id = self.km.xreq_channel.complete(text=text, line=line,
24 cursor_pos=cursor_pos)
25
25 msg_xreq = self.km.xreq_channel.get_msg(timeout=0.5)
26 msg_xreq = self.km.xreq_channel.get_msg(timeout=0.5)
26 if msg["header"]['session'] == msg_xreq["parent_header"]['session'] and \
27 if msg_xreq['parent_header']['msg_id'] == msg_id:
27 msg_xreq["content"]["status"] == 'ok' and \
28 msg_xreq["msg_type"] == "complete_reply" :
29 return msg_xreq["content"]["matches"]
28 return msg_xreq["content"]["matches"]
30 return []
29 return []
31
30
32 def complete(self, text, state):
31 def complete(self, text, state):
33 if state == 0:
32 if state == 0:
34 try:
33 try:
35 self.matches = self.complete_request(text)
34 self.matches = self.complete_request(text)
36 except Empty:
35 except Empty:
37 print('WARNING: Kernel timeout on tab completion.')
36 print('WARNING: Kernel timeout on tab completion.')
38
37
39 try:
38 try:
40 return self.matches[state]
39 return self.matches[state]
41 except IndexError:
40 except IndexError:
42 return None
41 return None
@@ -1,266 +1,260 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Frontend of ipython working with python-zmq
2 """Frontend of ipython working with python-zmq
3
3
4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
5
5
6 For more details, see the ipython-zmq design
6 For more details, see the ipython-zmq design
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2010 The IPython Development Team
9 # Copyright (C) 2010 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import __builtin__
19 import __builtin__
20 import sys
20 import sys
21 import os
21 import os
22 from Queue import Empty
22 from Queue import Empty
23 import readline
23 import readline
24 import rlcompleter
24 import rlcompleter
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports from ipython
27 # Imports from ipython
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 from IPython.external.argparse import ArgumentParser
29 from IPython.external.argparse import ArgumentParser
30 from IPython.core.inputsplitter import IPythonInputSplitter
30 from IPython.core.inputsplitter import IPythonInputSplitter
31 from IPython.zmq.blockingkernelmanager import BlockingKernelManager as KernelManager
31 from IPython.zmq.blockingkernelmanager import BlockingKernelManager as KernelManager
32 from IPython.frontend.zmqterminal.completer import ClientCompleter2p
32 from IPython.frontend.zmqterminal.completer import ClientCompleter2p
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Network Constants
35 # Network Constants
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
38 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
39 class Frontend(object):
39 class Frontend(object):
40 """This class is a simple frontend to ipython-zmq
40 """This class is a simple frontend to ipython-zmq
41
41
42 NOTE: this class uses kernelmanager to manipulate sockets
42 NOTE: this class uses kernelmanager to manipulate sockets
43
43
44 Parameters:
44 Parameters:
45 -----------
45 -----------
46 kernelmanager : object
46 kernelmanager : object
47 instantiated object from class KernelManager in module kernelmanager
47 instantiated object from class KernelManager in module kernelmanager
48
48
49 """
49 """
50
50
51 def __init__(self, kernelmanager):
51 def __init__(self, kernelmanager):
52 self.km = kernelmanager
52 self.km = kernelmanager
53 self.session = kernelmanager.session
53 self.session_id = self.km.session.session
54 self.request_socket = self.km.xreq_channel.socket
55 self.sub_socket = self.km.sub_channel.socket
56 self.reply_socket = self.km.rep_channel.socket
57 self.msg_header = self.km.session.msg_header()
58 self.completer = ClientCompleter2p(self, self.km)
54 self.completer = ClientCompleter2p(self, self.km)
59 readline.parse_and_bind("tab: complete")
55 readline.parse_and_bind("tab: complete")
60 readline.parse_and_bind('set show-all-if-ambiguous on')
56 readline.parse_and_bind('set show-all-if-ambiguous on')
61 readline.set_completer(self.completer.complete)
57 readline.set_completer(self.completer.complete)
62
58
63 history_path = os.path.expanduser('~/.ipython/history')
59 history_path = os.path.expanduser('~/.ipython/history')
64 if os.path.isfile(history_path):
60 if os.path.isfile(history_path):
65 rlcompleter.readline.read_history_file(history_path)
61 rlcompleter.readline.read_history_file(history_path)
66 else:
62 else:
67 print("history file cannot be read.")
63 print("history file cannot be read.")
68
64
69 self.messages = {}
65 self.messages = {}
70
66
71 self._splitter = IPythonInputSplitter()
67 self._splitter = IPythonInputSplitter()
72 self.code = ""
68 self.code = ""
73
69
74 self.prompt_count = 0
70 self.prompt_count = 0
75 self._get_initial_prompt()
71 self._get_initial_prompt()
76
72
77 def _get_initial_prompt(self):
73 def _get_initial_prompt(self):
78 self._execute('', hidden=True)
74 self._execute('', hidden=True)
79
75
80 def interact(self):
76 def interact(self):
81 """Gets input from console using inputsplitter, then
77 """Gets input from console using inputsplitter, then
82 while you enter code it can indent and set index id to any input
78 while you enter code it can indent and set index id to any input
83 """
79 """
84
80
85 try:
81 try:
86 self._splitter.push(raw_input('In[%i]:'%self.prompt_count+self.code))
82 self._splitter.push(raw_input('In[%i]:'%self.prompt_count+self.code))
87 while self._splitter.push_accepts_more():
83 while self._splitter.push_accepts_more():
88 self.code = raw_input('.....:'+' '*self._splitter.indent_spaces)
84 self.code = raw_input('.....:'+' '*self._splitter.indent_spaces)
89 self._splitter.push(' '*self._splitter.indent_spaces+self.code)
85 self._splitter.push(' '*self._splitter.indent_spaces+self.code)
90 self._execute(self._splitter.source,False)
86 self._execute(self._splitter.source,False)
91 self._splitter.reset()
87 self._splitter.reset()
92 except KeyboardInterrupt:
88 except KeyboardInterrupt:
93 print('\nKeyboardInterrupt\n')
89 print('\nKeyboardInterrupt\n')
94 pass
90 pass
95
91
96
92
97 def start(self):
93 def start(self):
98 """Start the interaction loop, calling the .interact() method for each
94 """Start the interaction loop, calling the .interact() method for each
99 input cell.
95 input cell.
100 """
96 """
101 while True:
97 while True:
102 try:
98 try:
103 self.interact()
99 self.interact()
104 except KeyboardInterrupt:
100 except KeyboardInterrupt:
105 print('\nKeyboardInterrupt\n')
101 print('\nKeyboardInterrupt\n')
106 pass
102 pass
107 except EOFError:
103 except EOFError:
108 answer = ''
104 answer = ''
109 while True:
105 while True:
110 answer = raw_input('\nDo you really want to exit ([y]/n)?')
106 answer = raw_input('\nDo you really want to exit ([y]/n)?')
111 if answer == 'y' or answer == '' :
107 if answer == 'y' or answer == '' :
112 self.km.shutdown_kernel()
108 self.km.shutdown_kernel()
113 sys.exit()
109 sys.exit()
114 elif answer == 'n':
110 elif answer == 'n':
115 break
111 break
116
112
117 def _execute(self, source, hidden = True):
113 def _execute(self, source, hidden = True):
118 """ Execute 'source'. If 'hidden', do not show any output.
114 """ Execute 'source'. If 'hidden', do not show any output.
119
115
120 See parent class :meth:`execute` docstring for full details.
116 See parent class :meth:`execute` docstring for full details.
121 """
117 """
122 self.km.xreq_channel.execute(source, hidden)
118 msg_id = self.km.xreq_channel.execute(source, hidden)
123 while not self.km.xreq_channel.msg_ready():
119 while not self.km.xreq_channel.msg_ready():
124 try:
120 try:
125 self.handle_rep_channel(timeout=0.1)
121 self.handle_rep_channel(timeout=0.1)
126 except Empty:
122 except Empty:
127 pass
123 pass
128 self.handle_xreq_channel()
124 self.handle_execute_reply(msg_id)
129
125
130 def handle_xreq_channel(self):
126 def handle_execute_reply(self, msg_id):
131 self.msg_xreq = self.km.xreq_channel.get_msg()
127 msg_xreq = self.km.xreq_channel.get_msg()
132 if self.msg_header["session"] == self.msg_xreq["parent_header"]["session"]:
128 if msg_xreq["parent_header"]["msg_id"] == msg_id:
133 if self.msg_xreq["content"]["status"] == 'ok' :
129 if msg_xreq["content"]["status"] == 'ok' :
134 if self.msg_xreq["msg_type"] == "execute_reply" :
130 self.handle_sub_channel()
135 self.handle_sub_channel()
131 self.prompt_count = msg_xreq["content"]["execution_count"] + 1
136 self.prompt_count = self.msg_xreq["content"]["execution_count"]+1
137
132
138 else:
133 else:
139 etb = self.msg_xreq["content"]["traceback"]
134 etb = msg_xreq["content"]["traceback"]
140 print >> sys.stderr, etb[0]
135 print >> sys.stderr, etb[0]
141 try: # These bits aren't there for a SyntaxError
136 try: # These bits aren't there for a SyntaxError
142 print >> sys.stderr, etb[1]
137 print >> sys.stderr, etb[1]
143 print >> sys.stderr, etb[2]
138 print >> sys.stderr, etb[2]
144 except IndexError:
139 except IndexError:
145 pass
140 pass
146 self.prompt_count = self.msg_xreq["content"]["execution_count"]+1
141 self.prompt_count = msg_xreq["content"]["execution_count"] + 1
147
142
148
143
149 def handle_sub_channel(self):
144 def handle_sub_channel(self):
150 """ Method to procces subscribe channel's messages
145 """ Method to procces subscribe channel's messages
151
146
152 This method reads a message and processes the content in different
147 This method reads a message and processes the content in different
153 outputs like stdout, stderr, pyout and status
148 outputs like stdout, stderr, pyout and status
154
149
155 Arguments:
150 Arguments:
156 sub_msg: message receive from kernel in the sub socket channel
151 sub_msg: message receive from kernel in the sub socket channel
157 capture by kernel manager.
152 capture by kernel manager.
158 """
153 """
159 while self.km.sub_channel.msg_ready():
154 while self.km.sub_channel.msg_ready():
160 sub_msg = self.km.sub_channel.get_msg()
155 sub_msg = self.km.sub_channel.get_msg()
161 if self.msg_header["username"] == sub_msg['parent_header']['username'] and \
156 if self.session_id == sub_msg['parent_header']['session']:
162 self.km.session.session == sub_msg['parent_header']['session']:
163 if sub_msg['msg_type'] == 'status' :
157 if sub_msg['msg_type'] == 'status' :
164 if sub_msg["content"]["execution_state"] == "busy" :
158 if sub_msg["content"]["execution_state"] == "busy" :
165 pass
159 pass
166
160
167 if sub_msg['msg_type'] == 'stream' :
161 elif sub_msg['msg_type'] == 'stream' :
168 if sub_msg["content"]["name"] == "stdout":
162 if sub_msg["content"]["name"] == "stdout":
169 print >> sys.stdout,sub_msg["content"]["data"]
163 print >> sys.stdout, sub_msg["content"]["data"]
170 sys.stdout.flush()
164 sys.stdout.flush()
171 if sub_msg["content"]["name"] == "stderr" :
165 elif sub_msg["content"]["name"] == "stderr" :
172 print >> sys.stderr,sub_msg["content"]["data"]
166 print >> sys.stderr, sub_msg["content"]["data"]
173 sys.stderr.flush()
167 sys.stderr.flush()
174
168
175 if sub_msg['msg_type'] == 'pyout' :
169 elif sub_msg['msg_type'] == 'pyout' :
176 print >> sys.stdout,"Out[%i]:"%sub_msg["content"]["execution_count"], sub_msg["content"]["data"]["text/plain"]
170 print >> sys.stdout,"Out[%i]:"%sub_msg["content"]["execution_count"], sub_msg["content"]["data"]["text/plain"]
177 sys.stdout.flush()
171 sys.stdout.flush()
178
172
179 def handle_rep_channel(self, timeout=0.1):
173 def handle_rep_channel(self, timeout=0.1):
180 """ Method to capture raw_input
174 """ Method to capture raw_input
181 """
175 """
182 self.msg_rep = self.km.rep_channel.get_msg(timeout=timeout)
176 msg_rep = self.km.rep_channel.get_msg(timeout=timeout)
183 if self.msg_header["session"] == self.msg_rep["parent_header"]["session"] :
177 if self.session_id == msg_rep["parent_header"]["session"] :
184 raw_data = raw_input(self.msg_rep["content"]["prompt"])
178 raw_data = raw_input(msg_rep["content"]["prompt"])
185 self.km.rep_channel.input(raw_data)
179 self.km.rep_channel.input(raw_data)
186
180
187
181
188
182
189
183
190 def start_frontend():
184 def start_frontend():
191 """ Entry point for application.
185 """ Entry point for application.
192
186
193 """
187 """
194 # Parse command line arguments.
188 # Parse command line arguments.
195 parser = ArgumentParser()
189 parser = ArgumentParser()
196 kgroup = parser.add_argument_group('kernel options')
190 kgroup = parser.add_argument_group('kernel options')
197 kgroup.add_argument('-e', '--existing', action='store_true',
191 kgroup.add_argument('-e', '--existing', action='store_true',
198 help='connect to an existing kernel')
192 help='connect to an existing kernel')
199 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
193 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
200 help=\
194 help=\
201 "set the kernel\'s IP address [default localhost].\
195 "set the kernel\'s IP address [default localhost].\
202 If the IP address is something other than localhost, then \
196 If the IP address is something other than localhost, then \
203 Consoles on other machines will be able to connect\
197 Consoles on other machines will be able to connect\
204 to the Kernel, so be careful!")
198 to the Kernel, so be careful!")
205 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
199 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
206 help='set the XREQ channel port [default random]')
200 help='set the XREQ channel port [default random]')
207 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
201 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
208 help='set the SUB channel port [default random]')
202 help='set the SUB channel port [default random]')
209 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
203 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
210 help='set the REP channel port [default random]')
204 help='set the REP channel port [default random]')
211 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
205 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
212 help='set the heartbeat port [default random]')
206 help='set the heartbeat port [default random]')
213
207
214 egroup = kgroup.add_mutually_exclusive_group()
208 egroup = kgroup.add_mutually_exclusive_group()
215 egroup.add_argument('--pure', action='store_true', help = \
209 egroup.add_argument('--pure', action='store_true', help = \
216 'use a pure Python kernel instead of an IPython kernel')
210 'use a pure Python kernel instead of an IPython kernel')
217 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
211 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
218 const='auto', help = \
212 const='auto', help = \
219 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
213 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
220 given, the GUI backend is matplotlib's, otherwise use one of: \
214 given, the GUI backend is matplotlib's, otherwise use one of: \
221 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
215 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
222 egroup.add_argument('--colors', type=str,
216 egroup.add_argument('--colors', type=str,
223 help="Set the color scheme (LightBG,Linux,NoColor). This is guessed\
217 help="Set the color scheme (LightBG,Linux,NoColor). This is guessed\
224 based on the pygments style if not set.")
218 based on the pygments style if not set.")
225
219
226 args = parser.parse_args()
220 args = parser.parse_args()
227
221
228 # parse the colors arg down to current known labels
222 # parse the colors arg down to current known labels
229 if args.colors:
223 if args.colors:
230 colors=args.colors.lower()
224 colors=args.colors.lower()
231 if colors in ('lightbg', 'light'):
225 if colors in ('lightbg', 'light'):
232 colors='lightbg'
226 colors='lightbg'
233 elif colors in ('dark', 'linux'):
227 elif colors in ('dark', 'linux'):
234 colors='linux'
228 colors='linux'
235 else:
229 else:
236 colors='nocolor'
230 colors='nocolor'
237 else:
231 else:
238 colors=None
232 colors=None
239
233
240 # Create a KernelManager and start a kernel.
234 # Create a KernelManager and start a kernel.
241 kernel_manager = KernelManager(xreq_address=(args.ip, args.xreq),
235 kernel_manager = KernelManager(xreq_address=(args.ip, args.xreq),
242 sub_address=(args.ip, args.sub),
236 sub_address=(args.ip, args.sub),
243 rep_address=(args.ip, args.rep),
237 rep_address=(args.ip, args.rep),
244 hb_address=(args.ip, args.hb))
238 hb_address=(args.ip, args.hb))
245 if not args.existing:
239 if not args.existing:
246 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
240 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
247 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
241 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
248
242
249 kwargs = dict(ip=args.ip)
243 kwargs = dict(ip=args.ip)
250 if args.pure:
244 if args.pure:
251 kwargs['ipython']=False
245 kwargs['ipython']=False
252 else:
246 else:
253 kwargs['colors']=colors
247 kwargs['colors']=colors
254 if args.pylab:
248 if args.pylab:
255 kwargs['pylab']=args.pylab
249 kwargs['pylab']=args.pylab
256 kernel_manager.start_kernel(**kwargs)
250 kernel_manager.start_kernel(**kwargs)
257
251
258
252
259 kernel_manager.start_channels()
253 kernel_manager.start_channels()
260
254
261 frontend=Frontend(kernel_manager)
255 frontend=Frontend(kernel_manager)
262 return frontend
256 return frontend
263
257
264 if __name__ == "__main__" :
258 if __name__ == "__main__" :
265 frontend=start_frontend()
259 frontend=start_frontend()
266 frontend.start()
260 frontend.start()
General Comments 0
You need to be logged in to leave comments. Login now