##// END OF EJS Templates
zmqterminal subclasses TerminalInteractiveShell/IPApp...
MinRK -
Show More
@@ -0,0 +1,161 b''
1 from __future__ import print_function
2
3 import signal
4 import sys
5 import time
6
7 from IPython.frontend.terminal.ipapp import TerminalIPythonApp
8
9 from IPython.utils.traitlets import (
10 Dict, List, Unicode, Int, CaselessStrEnum, CBool, Any
11 )
12 from IPython.zmq.ipkernel import (
13 flags as ipkernel_flags,
14 aliases as ipkernel_aliases,
15 IPKernelApp
16 )
17 from IPython.zmq.session import Session
18 from IPython.zmq.zmqshell import ZMQInteractiveShell
19 from IPython.zmq.blockingkernelmanager import BlockingKernelManager
20 from IPython.zmq.ipkernel import (
21 flags as ipkernel_flags,
22 aliases as ipkernel_aliases,
23 IPKernelApp
24 )
25 from IPython.frontend.zmqterminal.interactiveshell import ZMQTerminalInteractiveShell
26
27 #-----------------------------------------------------------------------------
28 # Network Constants
29 #-----------------------------------------------------------------------------
30
31 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
32
33 #-----------------------------------------------------------------------------
34 # Flags and Aliases
35 #-----------------------------------------------------------------------------
36
37
38 flags = dict(ipkernel_flags)
39 frontend_flags = {
40 'existing' : ({'ZMQTerminalIPythonApp' : {'existing' : True}},
41 "Connect to an existing kernel."),
42 }
43 flags.update(frontend_flags)
44 # the flags that are specific to the frontend
45 # these must be scrubbed before being passed to the kernel,
46 # or it will raise an error on unrecognized flags
47 frontend_flags = frontend_flags.keys()
48
49 aliases = dict(ipkernel_aliases)
50
51 frontend_aliases = dict(
52 hb = 'ZMQTerminalIPythonApp.hb_port',
53 shell = 'ZMQTerminalIPythonApp.shell_port',
54 iopub = 'ZMQTerminalIPythonApp.iopub_port',
55 stdin = 'ZMQTerminalIPythonApp.stdin_port',
56 ip = 'ZMQTerminalIPythonApp.ip',
57 )
58 aliases.update(frontend_aliases)
59 # also scrub aliases from the frontend
60 frontend_flags.extend(frontend_aliases.keys())
61
62 #-----------------------------------------------------------------------------
63 # Classes
64 #-----------------------------------------------------------------------------
65
66
67 class ZMQTerminalIPythonApp(TerminalIPythonApp):
68 """Start a terminal frontend to the IPython zmq kernel."""
69
70 kernel_argv = List(Unicode)
71 flags = Dict(flags)
72 aliases = Dict(aliases)
73 classes = List([IPKernelApp, ZMQTerminalInteractiveShell])
74
75 # connection info:
76 ip = Unicode(LOCALHOST, config=True,
77 help="""Set the kernel\'s IP address [default localhost].
78 If the IP address is something other than localhost, then
79 Consoles on other machines will be able to connect
80 to the Kernel, so be careful!"""
81 )
82 pure = False
83 hb_port = Int(0, config=True,
84 help="set the heartbeat port [default: random]")
85 shell_port = Int(0, config=True,
86 help="set the shell (XREP) port [default: random]")
87 iopub_port = Int(0, config=True,
88 help="set the iopub (PUB) port [default: random]")
89 stdin_port = Int(0, config=True,
90 help="set the stdin (XREQ) port [default: random]")
91
92 existing = CBool(False, config=True,
93 help="Whether to connect to an already running Kernel.")
94
95 # from qtconsoleapp:
96 def parse_command_line(self, argv=None):
97 super(ZMQTerminalIPythonApp, self).parse_command_line(argv)
98 if argv is None:
99 argv = sys.argv[1:]
100
101 self.kernel_argv = list(argv) # copy
102 # kernel should inherit default config file from frontend
103 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
104 # scrub frontend-specific flags
105 for a in argv:
106
107 if a.startswith('-'):
108 key = a.lstrip('-').split('=')[0]
109 if key in frontend_flags:
110 self.kernel_argv.remove(a)
111
112 def init_kernel_manager(self):
113 """init kernel manager (from qtconsole)"""
114 # Don't let Qt or ZMQ swallow KeyboardInterupts.
115 # signal.signal(signal.SIGINT, signal.SIG_DFL)
116
117 # Create a KernelManager and start a kernel.
118 self.kernel_manager = BlockingKernelManager(
119 shell_address=(self.ip, self.shell_port),
120 sub_address=(self.ip, self.iopub_port),
121 stdin_address=(self.ip, self.stdin_port),
122 hb_address=(self.ip, self.hb_port),
123 config=self.config
124 )
125 # start the kernel
126 if not self.existing:
127 kwargs = dict(ip=self.ip, ipython=not self.pure)
128 kwargs['extra_arguments'] = self.kernel_argv
129 self.kernel_manager.start_kernel(**kwargs)
130 # wait for kernel to start
131 time.sleep(0.5)
132 self.kernel_manager.start_channels()
133 # relay sigint to kernel
134 signal.signal(signal.SIGINT, self.handle_sigint)
135
136 def init_shell(self):
137 self.init_kernel_manager()
138 self.shell = ZMQTerminalInteractiveShell.instance(config=self.config,
139 display_banner=False, profile_dir=self.profile_dir,
140 ipython_dir=self.ipython_dir, kernel_manager=self.kernel_manager)
141
142 def handle_sigint(self, *args):
143 # FIXME: this doesn't work, the kernel just dies every time
144 self.shell.write('KeyboardInterrupt\n')
145 self.kernel_manager.interrupt_kernel()
146
147 def init_code(self):
148 # no-op in the frontend, code gets run in the backend
149 pass
150
151
152 def launch_new_instance():
153 """Create and run a full blown IPython instance"""
154 app = ZMQTerminalIPythonApp.instance()
155 app.initialize()
156 app.start()
157
158
159 if __name__ == '__main__':
160 launch_new_instance()
161
@@ -0,0 +1,252 b''
1 # -*- coding: utf-8 -*-
2 """Frontend of ipython working with python-zmq
3
4 Ipython's frontend, is a ipython interface that send request to kernel and proccess the kernel's outputs.
5
6 For more details, see the ipython-zmq design
7 """
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18 from __future__ import print_function
19
20 import bdb
21 import sys
22
23 from Queue import Empty
24
25 from IPython.core.alias import AliasManager, AliasError
26 from IPython.utils.warn import warn, error, fatal
27 from IPython.utils import io
28
29 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
30 from IPython.frontend.zmqterminal.completer import ZMQCompleter
31
32
33 class ZMQTerminalInteractiveShell(TerminalInteractiveShell):
34 """A subclass of TerminalInteractiveShell that """
35
36 def __init__(self, *args, **kwargs):
37 self.km = kwargs.pop('kernel_manager')
38 self.session_id = self.km.session.session
39 super(ZMQTerminalInteractiveShell, self).__init__(*args, **kwargs)
40
41 def init_completer(self):
42 """Initialize the completion machinery.
43
44 This creates completion machinery that can be used by client code,
45 either interactively in-process (typically triggered by the readline
46 library), programatically (such as in test suites) or out-of-prcess
47 (typically over the network by remote frontends).
48 """
49 from IPython.core.completerlib import (module_completer,
50 magic_run_completer, cd_completer)
51
52 self.Completer = ZMQCompleter(self, self.km)
53
54
55 self.set_hook('complete_command', module_completer, str_key = 'import')
56 self.set_hook('complete_command', module_completer, str_key = 'from')
57 self.set_hook('complete_command', magic_run_completer, str_key = '%run')
58 self.set_hook('complete_command', cd_completer, str_key = '%cd')
59
60 # Only configure readline if we truly are using readline. IPython can
61 # do tab-completion over the network, in GUIs, etc, where readline
62 # itself may be absent
63 if self.has_readline:
64 self.set_readline_completer()
65
66 def run_cell(self, cell, store_history=True):
67 """Run a complete IPython cell.
68
69 Parameters
70 ----------
71 cell : str
72 The code (including IPython code such as %magic functions) to run.
73 store_history : bool
74 If True, the raw and translated cell will be stored in IPython's
75 history. For user code calling back into IPython's machinery, this
76 should be set to False.
77 """
78 if (not cell) or cell.isspace():
79 return
80
81 # shell_channel.execute takes 'hidden', which is the inverse of store_hist
82 msg_id = self.km.shell_channel.execute(cell, not store_history)
83 while not self.km.shell_channel.msg_ready():
84 try:
85 self.handle_stdin_request(timeout=0.05)
86 except Empty:
87 pass
88 self.handle_execute_reply(msg_id)
89
90 #-----------------
91 # message handlers
92 #-----------------
93
94 def handle_execute_reply(self, msg_id):
95 msg = self.km.shell_channel.get_msg()
96 if msg["parent_header"]["msg_id"] == msg_id:
97 if msg["content"]["status"] == 'ok' :
98 self.handle_iopub()
99
100 elif msg["content"]["status"] == 'error':
101 for frame in msg["content"]["traceback"]:
102 print(frame, file=io.stderr)
103
104 self.execution_count = msg["content"]["execution_count"] + 1
105
106
107 def handle_iopub(self):
108 """ Method to procces subscribe channel's messages
109
110 This method reads a message and processes the content in different
111 outputs like stdout, stderr, pyout and status
112
113 Arguments:
114 sub_msg: message receive from kernel in the sub socket channel
115 capture by kernel manager.
116 """
117 while self.km.sub_channel.msg_ready():
118 sub_msg = self.km.sub_channel.get_msg()
119 msg_type = sub_msg['header']['msg_type']
120 if self.session_id == sub_msg['parent_header']['session']:
121 if msg_type == 'status' :
122 if sub_msg["content"]["execution_state"] == "busy" :
123 pass
124
125 elif msg_type == 'stream' :
126 if sub_msg["content"]["name"] == "stdout":
127 print(sub_msg["content"]["data"], file=io.stdout, end="")
128 io.stdout.flush()
129 elif sub_msg["content"]["name"] == "stderr" :
130 print(sub_msg["content"]["data"], file=io.stderr, end="")
131 io.stderr.flush()
132
133 elif msg_type == 'pyout':
134 format_dict = sub_msg["content"]["data"]
135 # taken from DisplayHook.__call__:
136 hook = self.displayhook
137 hook.start_displayhook()
138 hook.write_output_prompt()
139 hook.write_format_data(format_dict)
140 hook.log_output(format_dict)
141 hook.finish_displayhook()
142
143 def handle_stdin_request(self, timeout=0.1):
144 """ Method to capture raw_input
145 """
146 msg_rep = self.km.stdin_channel.get_msg(timeout=timeout)
147 if self.session_id == msg_rep["parent_header"]["session"] :
148 raw_data = raw_input(msg_rep["content"]["prompt"])
149 self.km.stdin_channel.input(raw_data)
150
151 def mainloop(self, display_banner=False):
152 while True:
153 try:
154 self.interact(display_banner=display_banner)
155 #self.interact_with_readline()
156 # XXX for testing of a readline-decoupled repl loop, call
157 # interact_with_readline above
158 break
159 except KeyboardInterrupt:
160 # this should not be necessary, but KeyboardInterrupt
161 # handling seems rather unpredictable...
162 self.write("\nKeyboardInterrupt in interact()\n")
163
164 def interact(self, display_banner=None):
165 """Closely emulate the interactive Python console."""
166
167 # batch run -> do not interact
168 if self.exit_now:
169 return
170
171 if display_banner is None:
172 display_banner = self.display_banner
173
174 if isinstance(display_banner, basestring):
175 self.show_banner(display_banner)
176 elif display_banner:
177 self.show_banner()
178
179 more = False
180
181 if self.has_readline:
182 self.readline_startup_hook(self.pre_readline)
183 # exit_now is set by a call to %Exit or %Quit, through the
184 # ask_exit callback.
185
186 while not self.exit_now:
187 if not self.km.is_alive:
188 ans = self.raw_input("kernel died, restart (y/n)?")
189 if not ans.lower().startswith('n'):
190 self.km.restart_kernel(True)
191 else:
192 self.exit_now=True
193 continue
194 self.hooks.pre_prompt_hook()
195 if more:
196 try:
197 prompt = self.hooks.generate_prompt(True)
198 except:
199 self.showtraceback()
200 if self.autoindent:
201 self.rl_do_indent = True
202
203 else:
204 try:
205 prompt = self.hooks.generate_prompt(False)
206 except:
207 self.showtraceback()
208 try:
209 line = self.raw_input(prompt)
210 if self.exit_now:
211 # quick exit on sys.std[in|out] close
212 break
213 if self.autoindent:
214 self.rl_do_indent = False
215
216 except KeyboardInterrupt:
217 #double-guard against keyboardinterrupts during kbdint handling
218 try:
219 self.write('\nKeyboardInterrupt\n')
220 self.input_splitter.reset()
221 more = False
222 except KeyboardInterrupt:
223 pass
224 except EOFError:
225 if self.autoindent:
226 self.rl_do_indent = False
227 if self.has_readline:
228 self.readline_startup_hook(None)
229 self.write('\n')
230 self.exit()
231 except bdb.BdbQuit:
232 warn('The Python debugger has exited with a BdbQuit exception.\n'
233 'Because of how pdb handles the stack, it is impossible\n'
234 'for IPython to properly format this particular exception.\n'
235 'IPython will resume normal operation.')
236 except:
237 # exceptions here are VERY RARE, but they can be triggered
238 # asynchronously by signal handlers, for example.
239 self.showtraceback()
240 else:
241 self.input_splitter.push(line)
242 more = self.input_splitter.push_accepts_more()
243 if (self.SyntaxTB.last_syntax_error and
244 self.autoedit_syntax):
245 self.edit_syntax_error()
246 if not more:
247 source_raw = self.input_splitter.source_reset()
248 self.run_cell(source_raw)
249
250
251 # Turn off the exit flag, so the mainloop can be restarted if desired
252 self.exit_now = False
@@ -217,6 +217,9 b' class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):'
217 kernel = ("IPython.zmq.ipkernel.IPKernelApp",
217 kernel = ("IPython.zmq.ipkernel.IPKernelApp",
218 "Start a kernel without an attached frontend."
218 "Start a kernel without an attached frontend."
219 ),
219 ),
220 zmq=('IPython.frontend.zmqterminal.app.ZMQTerminalIPythonApp',
221 """Launch two-process Terminal session with 0MQ."""
222 ),
220 ))
223 ))
221
224
222 # *do* autocreate requested profile, but don't create the config file.
225 # *do* autocreate requested profile, but don't create the config file.
@@ -2,17 +2,17 b''
2 import readline
2 import readline
3 from Queue import Empty
3 from Queue import Empty
4
4
5 class ClientCompleter2p(object):
5 class ZMQCompleter(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, shell, km):
13 self.shell = shell
13 self.km = km
14 self.km = km
14 self.matches = []
15 self.matches = []
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()
@@ -20,15 +20,15 b' class ClientCompleter2p(object):'
20
20
21 # send completion request to kernel
21 # send completion request to kernel
22 # 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,
23 msg_id = self.km.shell_channel.complete(text=text, line=line,
24 cursor_pos=cursor_pos)
24 cursor_pos=cursor_pos)
25
25
26 msg_xreq = self.km.xreq_channel.get_msg(timeout=0.5)
26 msg = self.km.shell_channel.get_msg(timeout=0.5)
27 if msg_xreq['parent_header']['msg_id'] == msg_id:
27 if msg['parent_header']['msg_id'] == msg_id:
28 return msg_xreq["content"]["matches"]
28 return msg["content"]["matches"]
29 return []
29 return []
30
30
31 def complete(self, text, state):
31 def rlcomplete(self, text, state):
32 if state == 0:
32 if state == 0:
33 try:
33 try:
34 self.matches = self.complete_request(text)
34 self.matches = self.complete_request(text)
@@ -39,3 +39,6 b' class ClientCompleter2p(object):'
39 return self.matches[state]
39 return self.matches[state]
40 except IndexError:
40 except IndexError:
41 return None
41 return None
42
43 def complete(self, text, line, cursor_pos=None):
44 return self.rlcomplete(text, 0)
General Comments 0
You need to be logged in to leave comments. Login now