##// END OF EJS Templates
all sends/recvs now via Session.send/recv....
MinRK -
Show More
@@ -1,86 +1,87 b''
1 1 """Tab-completion over zmq"""
2 2
3 3 # Trying to get print statements to work during completion, not very
4 4 # successfully...
5 5 from __future__ import print_function
6 6
7 7 import itertools
8 8 import readline
9 9 import rlcompleter
10 10 import time
11 11
12 12 import session
13 13
14 14 class KernelCompleter(object):
15 15 """Kernel-side completion machinery."""
16 16 def __init__(self, namespace):
17 17 self.namespace = namespace
18 18 self.completer = rlcompleter.Completer(namespace)
19 19
20 20 def complete(self, line, text):
21 21 # We'll likely use linel later even if now it's not used for anything
22 22 matches = []
23 23 complete = self.completer.complete
24 24 for state in itertools.count():
25 25 comp = complete(text, state)
26 26 if comp is None:
27 27 break
28 28 matches.append(comp)
29 29 return matches
30 30
31 31
32 32 class ClientCompleter(object):
33 33 """Client-side completion machinery.
34 34
35 35 How it works: self.complete will be called multiple times, with
36 36 state=0,1,2,... When state=0 it should compute ALL the completion matches,
37 37 and then return them for each value of state."""
38 38
39 39 def __init__(self, client, session, socket):
40 40 # ugly, but we get called asynchronously and need access to some
41 41 # client state, like backgrounded code
42 42 self.client = client
43 43 self.session = session
44 44 self.socket = socket
45 45 self.matches = []
46 46
47 47 def request_completion(self, text):
48 48 # Get full line to give to the kernel in case it wants more info.
49 49 line = readline.get_line_buffer()
50 50 # send completion request to kernel
51 51 msg = self.session.send(self.socket,
52 52 'complete_request',
53 53 dict(text=text, line=line))
54 54
55 55 # Give the kernel up to 0.5s to respond
56 56 for i in range(5):
57 rep = self.session.recv(self.socket)
57 ident,rep = self.session.recv(self.socket)
58 rep = Message(rep)
58 59 if rep is not None and rep.msg_type == 'complete_reply':
59 60 matches = rep.content.matches
60 61 break
61 62 time.sleep(0.1)
62 63 else:
63 64 # timeout
64 65 print ('TIMEOUT') # Can't see this message...
65 66 matches = None
66 67 return matches
67 68
68 69 def complete(self, text, state):
69 70
70 71 if self.client.backgrounded > 0:
71 72 print("\n[Not completing, background tasks active]")
72 73 print(readline.get_line_buffer(), end='')
73 74 return None
74 75
75 76 if state==0:
76 77 matches = self.request_completion(text)
77 78 if matches is None:
78 79 self.matches = []
79 80 print('WARNING: Kernel timeout on tab completion.')
80 81 else:
81 82 self.matches = matches
82 83
83 84 try:
84 85 return self.matches[state]
85 86 except IndexError:
86 87 return None
@@ -1,22 +1,21 b''
1 1 import __builtin__
2 2
3 3 from session import extract_header
4 4
5 5 class DisplayHook(object):
6 6
7 7 def __init__(self, session, pub_socket):
8 8 self.session = session
9 9 self.pub_socket = pub_socket
10 10 self.parent_header = {}
11 11
12 12 def __call__(self, obj):
13 13 if obj is None:
14 14 return
15 15
16 16 __builtin__._ = obj
17 msg = self.session.msg(u'pyout', {u'data':repr(obj)},
17 msg = self.session.send(self.pub_socket, u'pyout', {u'data':repr(obj)},
18 18 parent=self.parent_header)
19 self.pub_socket.send_json(msg)
20 19
21 20 def set_parent(self, parent):
22 21 self.parent_header = extract_header(parent) No newline at end of file
@@ -1,195 +1,196 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive frontend that talks to a kernel over 0MQ.
3 3 """
4 4
5 5 #-----------------------------------------------------------------------------
6 6 # Imports
7 7 #-----------------------------------------------------------------------------
8 8 # stdlib
9 9 import cPickle as pickle
10 10 import code
11 11 import readline
12 12 import sys
13 13 import time
14 14 import uuid
15 15
16 16 # our own
17 17 import zmq
18 18 import session
19 19 import completer
20 20 from IPython.utils.localinterfaces import LOCALHOST
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Classes and functions
24 24 #-----------------------------------------------------------------------------
25 25
26 26 class Console(code.InteractiveConsole):
27 27
28 28 def __init__(self, locals=None, filename="<console>",
29 29 session = session,
30 30 request_socket=None,
31 31 sub_socket=None):
32 32 code.InteractiveConsole.__init__(self, locals, filename)
33 33 self.session = session
34 34 self.request_socket = request_socket
35 35 self.sub_socket = sub_socket
36 36 self.backgrounded = 0
37 37 self.messages = {}
38 38
39 39 # Set tab completion
40 40 self.completer = completer.ClientCompleter(self, session, request_socket)
41 41 readline.parse_and_bind('tab: complete')
42 42 readline.parse_and_bind('set show-all-if-ambiguous on')
43 43 readline.set_completer(self.completer.complete)
44 44
45 45 # Set system prompts
46 46 sys.ps1 = 'Py>>> '
47 47 sys.ps2 = ' ... '
48 48 sys.ps3 = 'Out : '
49 49 # Build dict of handlers for message types
50 50 self.handlers = {}
51 51 for msg_type in ['pyin', 'pyout', 'pyerr', 'stream']:
52 52 self.handlers[msg_type] = getattr(self, 'handle_%s' % msg_type)
53 53
54 54 def handle_pyin(self, omsg):
55 55 if omsg.parent_header.session == self.session.session:
56 56 return
57 57 c = omsg.content.code.rstrip()
58 58 if c:
59 59 print '[IN from %s]' % omsg.parent_header.username
60 60 print c
61 61
62 62 def handle_pyout(self, omsg):
63 63 #print omsg # dbg
64 64 if omsg.parent_header.session == self.session.session:
65 65 print "%s%s" % (sys.ps3, omsg.content.data)
66 66 else:
67 67 print '[Out from %s]' % omsg.parent_header.username
68 68 print omsg.content.data
69 69
70 70 def print_pyerr(self, err):
71 71 print >> sys.stderr, err.etype,':', err.evalue
72 72 print >> sys.stderr, ''.join(err.traceback)
73 73
74 74 def handle_pyerr(self, omsg):
75 75 if omsg.parent_header.session == self.session.session:
76 76 return
77 77 print >> sys.stderr, '[ERR from %s]' % omsg.parent_header.username
78 78 self.print_pyerr(omsg.content)
79 79
80 80 def handle_stream(self, omsg):
81 81 if omsg.content.name == 'stdout':
82 82 outstream = sys.stdout
83 83 else:
84 84 outstream = sys.stderr
85 85 print >> outstream, '*ERR*',
86 86 print >> outstream, omsg.content.data,
87 87
88 88 def handle_output(self, omsg):
89 89 handler = self.handlers.get(omsg.msg_type, None)
90 90 if handler is not None:
91 91 handler(omsg)
92 92
93 93 def recv_output(self):
94 94 while True:
95 omsg = self.session.recv(self.sub_socket)
96 if omsg is None:
95 ident,msg = self.session.recv(self.sub_socket)
96 if msg is None:
97 97 break
98 self.handle_output(omsg)
98 self.handle_output(Message(msg))
99 99
100 100 def handle_reply(self, rep):
101 101 # Handle any side effects on output channels
102 102 self.recv_output()
103 103 # Now, dispatch on the possible reply types we must handle
104 104 if rep is None:
105 105 return
106 106 if rep.content.status == 'error':
107 107 self.print_pyerr(rep.content)
108 108 elif rep.content.status == 'aborted':
109 109 print >> sys.stderr, "ERROR: ABORTED"
110 110 ab = self.messages[rep.parent_header.msg_id].content
111 111 if 'code' in ab:
112 112 print >> sys.stderr, ab.code
113 113 else:
114 114 print >> sys.stderr, ab
115 115
116 116 def recv_reply(self):
117 rep = self.session.recv(self.request_socket)
118 self.handle_reply(rep)
119 return rep
117 ident,rep = self.session.recv(self.request_socket)
118 mrep = Message(rep)
119 self.handle_reply(mrep)
120 return mrep
120 121
121 122 def runcode(self, code):
122 123 # We can't pickle code objects, so fetch the actual source
123 124 src = '\n'.join(self.buffer)
124 125
125 126 # for non-background inputs, if we do have previoiusly backgrounded
126 127 # jobs, check to see if they've produced results
127 128 if not src.endswith(';'):
128 129 while self.backgrounded > 0:
129 130 #print 'checking background'
130 131 rep = self.recv_reply()
131 132 if rep:
132 133 self.backgrounded -= 1
133 134 time.sleep(0.05)
134 135
135 136 # Send code execution message to kernel
136 137 omsg = self.session.send(self.request_socket,
137 138 'execute_request', dict(code=src))
138 139 self.messages[omsg.header.msg_id] = omsg
139 140
140 141 # Fake asynchronicity by letting the user put ';' at the end of the line
141 142 if src.endswith(';'):
142 143 self.backgrounded += 1
143 144 return
144 145
145 146 # For foreground jobs, wait for reply
146 147 while True:
147 148 rep = self.recv_reply()
148 149 if rep is not None:
149 150 break
150 151 self.recv_output()
151 152 time.sleep(0.05)
152 153 else:
153 154 # We exited without hearing back from the kernel!
154 155 print >> sys.stderr, 'ERROR!!! kernel never got back to us!!!'
155 156
156 157
157 158 class InteractiveClient(object):
158 159 def __init__(self, session, request_socket, sub_socket):
159 160 self.session = session
160 161 self.request_socket = request_socket
161 162 self.sub_socket = sub_socket
162 163 self.console = Console(None, '<zmq-console>',
163 164 session, request_socket, sub_socket)
164 165
165 166 def interact(self):
166 167 self.console.interact()
167 168
168 169
169 170 def main():
170 171 # Defaults
171 172 #ip = '192.168.2.109'
172 173 ip = LOCALHOST
173 174 #ip = '99.146.222.252'
174 175 port_base = 5575
175 176 connection = ('tcp://%s' % ip) + ':%i'
176 177 req_conn = connection % port_base
177 178 sub_conn = connection % (port_base+1)
178 179
179 180 # Create initial sockets
180 181 c = zmq.Context()
181 182 request_socket = c.socket(zmq.XREQ)
182 183 request_socket.connect(req_conn)
183 184
184 185 sub_socket = c.socket(zmq.SUB)
185 186 sub_socket.connect(sub_conn)
186 187 sub_socket.setsockopt(zmq.SUBSCRIBE, '')
187 188
188 189 # Make session and user-facing client
189 190 sess = session.Session()
190 191 client = InteractiveClient(sess, request_socket, sub_socket)
191 192 client.interact()
192 193
193 194
194 195 if __name__ == '__main__':
195 196 main()
@@ -1,85 +1,84 b''
1 1 import sys
2 2 import time
3 3 from cStringIO import StringIO
4 4
5 5 from session import extract_header, Message
6 6
7 7 from IPython.utils import io
8 8
9 9 #-----------------------------------------------------------------------------
10 10 # Stream classes
11 11 #-----------------------------------------------------------------------------
12 12
13 13 class OutStream(object):
14 14 """A file like object that publishes the stream to a 0MQ PUB socket."""
15 15
16 16 # The time interval between automatic flushes, in seconds.
17 17 flush_interval = 0.05
18 18
19 19 def __init__(self, session, pub_socket, name):
20 20 self.session = session
21 21 self.pub_socket = pub_socket
22 22 self.name = name
23 23 self.parent_header = {}
24 24 self._new_buffer()
25 25
26 26 def set_parent(self, parent):
27 27 self.parent_header = extract_header(parent)
28 28
29 29 def close(self):
30 30 self.pub_socket = None
31 31
32 32 def flush(self):
33 33 #io.rprint('>>>flushing output buffer: %s<<<' % self.name) # dbg
34 34 if self.pub_socket is None:
35 35 raise ValueError(u'I/O operation on closed file')
36 36 else:
37 37 data = self._buffer.getvalue()
38 38 if data:
39 39 content = {u'name':self.name, u'data':data}
40 msg = self.session.msg(u'stream', content=content,
40 msg = self.session.send(self.pub_socket, u'stream', content=content,
41 41 parent=self.parent_header)
42 42 io.raw_print(msg)
43 self.pub_socket.send_json(msg)
44 43
45 44 self._buffer.close()
46 45 self._new_buffer()
47 46
48 47 def isatty(self):
49 48 return False
50 49
51 50 def next(self):
52 51 raise IOError('Read not supported on a write only stream.')
53 52
54 53 def read(self, size=-1):
55 54 raise IOError('Read not supported on a write only stream.')
56 55
57 56 def readline(self, size=-1):
58 57 raise IOError('Read not supported on a write only stream.')
59 58
60 59 def write(self, string):
61 60 if self.pub_socket is None:
62 61 raise ValueError('I/O operation on closed file')
63 62 else:
64 63 # We can only send raw bytes, not unicode objects, so we encode
65 64 # into utf-8 for all frontends if we get unicode inputs.
66 65 if type(string) == unicode:
67 66 string = string.encode('utf-8')
68 67
69 68 self._buffer.write(string)
70 69 current_time = time.time()
71 70 if self._start <= 0:
72 71 self._start = current_time
73 72 elif current_time - self._start > self.flush_interval:
74 73 self.flush()
75 74
76 75 def writelines(self, sequence):
77 76 if self.pub_socket is None:
78 77 raise ValueError('I/O operation on closed file')
79 78 else:
80 79 for string in sequence:
81 80 self.write(string)
82 81
83 82 def _new_buffer(self):
84 83 self._buffer = StringIO()
85 84 self._start = -1
@@ -1,642 +1,629 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Standard library imports.
19 19 import __builtin__
20 20 import atexit
21 21 import sys
22 22 import time
23 23 import traceback
24 24
25 25 # System library imports.
26 26 import zmq
27 27
28 28 # Local imports.
29 29 from IPython.config.configurable import Configurable
30 30 from IPython.utils import io
31 31 from IPython.utils.jsonutil import json_clean
32 32 from IPython.lib import pylabtools
33 33 from IPython.utils.traitlets import Instance, Float
34 34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
35 35 start_kernel)
36 36 from iostream import OutStream
37 37 from session import Session, Message
38 38 from zmqshell import ZMQInteractiveShell
39 39
40 40 #-----------------------------------------------------------------------------
41 41 # Main kernel class
42 42 #-----------------------------------------------------------------------------
43 43
44 44 class Kernel(Configurable):
45 45
46 46 #---------------------------------------------------------------------------
47 47 # Kernel interface
48 48 #---------------------------------------------------------------------------
49 49
50 50 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
51 51 session = Instance(Session)
52 52 reply_socket = Instance('zmq.Socket')
53 53 pub_socket = Instance('zmq.Socket')
54 54 req_socket = Instance('zmq.Socket')
55 55
56 56 # Private interface
57 57
58 58 # Time to sleep after flushing the stdout/err buffers in each execute
59 59 # cycle. While this introduces a hard limit on the minimal latency of the
60 60 # execute cycle, it helps prevent output synchronization problems for
61 61 # clients.
62 62 # Units are in seconds. The minimum zmq latency on local host is probably
63 63 # ~150 microseconds, set this to 500us for now. We may need to increase it
64 64 # a little if it's not enough after more interactive testing.
65 65 _execute_sleep = Float(0.0005, config=True)
66 66
67 67 # Frequency of the kernel's event loop.
68 68 # Units are in seconds, kernel subclasses for GUI toolkits may need to
69 69 # adapt to milliseconds.
70 70 _poll_interval = Float(0.05, config=True)
71 71
72 72 # If the shutdown was requested over the network, we leave here the
73 73 # necessary reply message so it can be sent by our registered atexit
74 74 # handler. This ensures that the reply is only sent to clients truly at
75 75 # the end of our shutdown process (which happens after the underlying
76 76 # IPython shell's own shutdown).
77 77 _shutdown_message = None
78 78
79 79 # This is a dict of port number that the kernel is listening on. It is set
80 80 # by record_ports and used by connect_request.
81 81 _recorded_ports = None
82 82
83 83 def __init__(self, **kwargs):
84 84 super(Kernel, self).__init__(**kwargs)
85 85
86 86 # Before we even start up the shell, register *first* our exit handlers
87 87 # so they come before the shell's
88 88 atexit.register(self._at_shutdown)
89 89
90 90 # Initialize the InteractiveShell subclass
91 91 self.shell = ZMQInteractiveShell.instance()
92 92 self.shell.displayhook.session = self.session
93 93 self.shell.displayhook.pub_socket = self.pub_socket
94 94
95 95 # TMP - hack while developing
96 96 self.shell._reply_content = None
97 97
98 98 # Build dict of handlers for message types
99 99 msg_types = [ 'execute_request', 'complete_request',
100 100 'object_info_request', 'history_request',
101 101 'connect_request', 'shutdown_request']
102 102 self.handlers = {}
103 103 for msg_type in msg_types:
104 104 self.handlers[msg_type] = getattr(self, msg_type)
105 105
106 106 def do_one_iteration(self):
107 107 """Do one iteration of the kernel's evaluation loop.
108 108 """
109 try:
110 ident = self.reply_socket.recv(zmq.NOBLOCK)
111 except zmq.ZMQError, e:
112 if e.errno == zmq.EAGAIN:
113 return
114 else:
115 raise
109 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
110 if msg is None:
111 return
112
116 113 # This assert will raise in versions of zeromq 2.0.7 and lesser.
117 114 # We now require 2.0.8 or above, so we can uncomment for safety.
118 assert self.reply_socket.rcvmore(), "Missing message part."
119 msg = self.reply_socket.recv_json()
115 # print(ident,msg, file=sys.__stdout__)
116 assert ident is not None, "Missing message part."
120 117
121 118 # Print some info about this message and leave a '--->' marker, so it's
122 119 # easier to trace visually the message chain when debugging. Each
123 120 # handler prints its message at the end.
124 121 # Eventually we'll move these from stdout to a logger.
125 122 io.raw_print('\n*** MESSAGE TYPE:', msg['msg_type'], '***')
126 123 io.raw_print(' Content: ', msg['content'],
127 124 '\n --->\n ', sep='', end='')
128 125
129 126 # Find and call actual handler for message
130 127 handler = self.handlers.get(msg['msg_type'], None)
131 128 if handler is None:
132 129 io.raw_print_err("UNKNOWN MESSAGE TYPE:", msg)
133 130 else:
134 131 handler(ident, msg)
135 132
136 133 # Check whether we should exit, in case the incoming message set the
137 134 # exit flag on
138 135 if self.shell.exit_now:
139 136 io.raw_print('\nExiting IPython kernel...')
140 137 # We do a normal, clean exit, which allows any actions registered
141 138 # via atexit (such as history saving) to take place.
142 139 sys.exit(0)
143 140
144 141
145 142 def start(self):
146 143 """ Start the kernel main loop.
147 144 """
148 145 while True:
149 146 time.sleep(self._poll_interval)
150 147 self.do_one_iteration()
151 148
152 149 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
153 150 """Record the ports that this kernel is using.
154 151
155 152 The creator of the Kernel instance must call this methods if they
156 153 want the :meth:`connect_request` method to return the port numbers.
157 154 """
158 155 self._recorded_ports = {
159 156 'xrep_port' : xrep_port,
160 157 'pub_port' : pub_port,
161 158 'req_port' : req_port,
162 159 'hb_port' : hb_port
163 160 }
164 161
165 162 #---------------------------------------------------------------------------
166 163 # Kernel request handlers
167 164 #---------------------------------------------------------------------------
168 165
169 166 def _publish_pyin(self, code, parent):
170 167 """Publish the code request on the pyin stream."""
171 168
172 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
173 self.pub_socket.send_json(pyin_msg)
169 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
174 170
175 171 def execute_request(self, ident, parent):
176 172
177 status_msg = self.session.msg(
173 status_msg = self.session.send(self.pub_socket,
178 174 u'status',
179 175 {u'execution_state':u'busy'},
180 176 parent=parent
181 177 )
182 self.pub_socket.send_json(status_msg)
183 178
184 179 try:
185 180 content = parent[u'content']
186 181 code = content[u'code']
187 182 silent = content[u'silent']
188 183 except:
189 184 io.raw_print_err("Got bad msg: ")
190 185 io.raw_print_err(Message(parent))
191 186 return
192 187
193 188 shell = self.shell # we'll need this a lot here
194 189
195 190 # Replace raw_input. Note that is not sufficient to replace
196 191 # raw_input in the user namespace.
197 192 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
198 193 __builtin__.raw_input = raw_input
199 194
200 195 # Set the parent message of the display hook and out streams.
201 196 shell.displayhook.set_parent(parent)
202 197 sys.stdout.set_parent(parent)
203 198 sys.stderr.set_parent(parent)
204 199
205 200 # Re-broadcast our input for the benefit of listening clients, and
206 201 # start computing output
207 202 if not silent:
208 203 self._publish_pyin(code, parent)
209 204
210 205 reply_content = {}
211 206 try:
212 207 if silent:
213 208 # run_code uses 'exec' mode, so no displayhook will fire, and it
214 209 # doesn't call logging or history manipulations. Print
215 210 # statements in that code will obviously still execute.
216 211 shell.run_code(code)
217 212 else:
218 213 # FIXME: the shell calls the exception handler itself.
219 214 shell._reply_content = None
220 215 shell.run_cell(code)
221 216 except:
222 217 status = u'error'
223 218 # FIXME: this code right now isn't being used yet by default,
224 219 # because the runlines() call above directly fires off exception
225 220 # reporting. This code, therefore, is only active in the scenario
226 221 # where runlines itself has an unhandled exception. We need to
227 222 # uniformize this, for all exception construction to come from a
228 223 # single location in the codbase.
229 224 etype, evalue, tb = sys.exc_info()
230 225 tb_list = traceback.format_exception(etype, evalue, tb)
231 226 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
232 227 else:
233 228 status = u'ok'
234 229
235 230 reply_content[u'status'] = status
236 231
237 232 # Return the execution counter so clients can display prompts
238 233 reply_content['execution_count'] = shell.execution_count -1
239 234
240 235 # FIXME - fish exception info out of shell, possibly left there by
241 236 # runlines. We'll need to clean up this logic later.
242 237 if shell._reply_content is not None:
243 238 reply_content.update(shell._reply_content)
244 239
245 240 # At this point, we can tell whether the main code execution succeeded
246 241 # or not. If it did, we proceed to evaluate user_variables/expressions
247 242 if reply_content['status'] == 'ok':
248 243 reply_content[u'user_variables'] = \
249 244 shell.user_variables(content[u'user_variables'])
250 245 reply_content[u'user_expressions'] = \
251 246 shell.user_expressions(content[u'user_expressions'])
252 247 else:
253 248 # If there was an error, don't even try to compute variables or
254 249 # expressions
255 250 reply_content[u'user_variables'] = {}
256 251 reply_content[u'user_expressions'] = {}
257 252
258 253 # Payloads should be retrieved regardless of outcome, so we can both
259 254 # recover partial output (that could have been generated early in a
260 255 # block, before an error) and clear the payload system always.
261 256 reply_content[u'payload'] = shell.payload_manager.read_payload()
262 257 # Be agressive about clearing the payload because we don't want
263 258 # it to sit in memory until the next execute_request comes in.
264 259 shell.payload_manager.clear_payload()
265 260
266 261 # Send the reply.
267 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
262 reply_msg = self.session.send(self.reply_socket, u'execute_reply', reply_content, parent, ident=ident)
268 263 io.raw_print(reply_msg)
269 264
270 265 # Flush output before sending the reply.
271 266 sys.stdout.flush()
272 267 sys.stderr.flush()
273 268 # FIXME: on rare occasions, the flush doesn't seem to make it to the
274 269 # clients... This seems to mitigate the problem, but we definitely need
275 270 # to better understand what's going on.
276 271 if self._execute_sleep:
277 272 time.sleep(self._execute_sleep)
278 273
279 self.reply_socket.send(ident, zmq.SNDMORE)
280 self.reply_socket.send_json(reply_msg)
281 274 if reply_msg['content']['status'] == u'error':
282 275 self._abort_queue()
283 276
284 status_msg = self.session.msg(
277 status_msg = self.session.send(self.pub_socket,
285 278 u'status',
286 279 {u'execution_state':u'idle'},
287 280 parent=parent
288 281 )
289 self.pub_socket.send_json(status_msg)
290 282
291 283 def complete_request(self, ident, parent):
292 284 txt, matches = self._complete(parent)
293 285 matches = {'matches' : matches,
294 286 'matched_text' : txt,
295 287 'status' : 'ok'}
296 288 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
297 289 matches, parent, ident)
298 290 io.raw_print(completion_msg)
299 291
300 292 def object_info_request(self, ident, parent):
301 293 object_info = self.shell.object_inspect(parent['content']['oname'])
302 294 # Before we send this object over, we scrub it for JSON usage
303 295 oinfo = json_clean(object_info)
304 296 msg = self.session.send(self.reply_socket, 'object_info_reply',
305 297 oinfo, parent, ident)
306 298 io.raw_print(msg)
307 299
308 300 def history_request(self, ident, parent):
309 301 output = parent['content']['output']
310 302 index = parent['content']['index']
311 303 raw = parent['content']['raw']
312 304 hist = self.shell.get_history(index=index, raw=raw, output=output)
313 305 content = {'history' : hist}
314 306 msg = self.session.send(self.reply_socket, 'history_reply',
315 307 content, parent, ident)
316 308 io.raw_print(msg)
317 309
318 310 def connect_request(self, ident, parent):
319 311 if self._recorded_ports is not None:
320 312 content = self._recorded_ports.copy()
321 313 else:
322 314 content = {}
323 315 msg = self.session.send(self.reply_socket, 'connect_reply',
324 316 content, parent, ident)
325 317 io.raw_print(msg)
326 318
327 319 def shutdown_request(self, ident, parent):
328 320 self.shell.exit_now = True
329 321 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
330 322 sys.exit(0)
331 323
332 324 #---------------------------------------------------------------------------
333 325 # Protected interface
334 326 #---------------------------------------------------------------------------
335 327
336 328 def _abort_queue(self):
337 329 while True:
338 try:
339 ident = self.reply_socket.recv(zmq.NOBLOCK)
340 except zmq.ZMQError, e:
341 if e.errno == zmq.EAGAIN:
342 break
330 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
331 if msg is None:
332 break
343 333 else:
344 assert self.reply_socket.rcvmore(), \
334 assert ident is not None, \
345 335 "Unexpected missing message part."
346 msg = self.reply_socket.recv_json()
347 336 io.raw_print("Aborting:\n", Message(msg))
348 337 msg_type = msg['msg_type']
349 338 reply_type = msg_type.split('_')[0] + '_reply'
350 reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg)
339 reply_msg = self.session.send(self.reply_socket, reply_type,
340 {'status' : 'aborted'}, msg, ident=ident)
351 341 io.raw_print(reply_msg)
352 self.reply_socket.send(ident,zmq.SNDMORE)
353 self.reply_socket.send_json(reply_msg)
354 342 # We need to wait a bit for requests to come in. This can probably
355 343 # be set shorter for true asynchronous clients.
356 344 time.sleep(0.1)
357 345
358 346 def _raw_input(self, prompt, ident, parent):
359 347 # Flush output before making the request.
360 348 sys.stderr.flush()
361 349 sys.stdout.flush()
362 350
363 351 # Send the input request.
364 352 content = dict(prompt=prompt)
365 msg = self.session.msg(u'input_request', content, parent)
366 self.req_socket.send_json(msg)
353 msg = self.session.send(self.req_socket, u'input_request', content, parent)
367 354
368 355 # Await a response.
369 reply = self.req_socket.recv_json()
356 ident, reply = self.session.recv(self.req_socket, 0)
370 357 try:
371 358 value = reply['content']['value']
372 359 except:
373 360 io.raw_print_err("Got bad raw_input reply: ")
374 361 io.raw_print_err(Message(parent))
375 362 value = ''
376 363 return value
377 364
378 365 def _complete(self, msg):
379 366 c = msg['content']
380 367 try:
381 368 cpos = int(c['cursor_pos'])
382 369 except:
383 370 # If we don't get something that we can convert to an integer, at
384 371 # least attempt the completion guessing the cursor is at the end of
385 372 # the text, if there's any, and otherwise of the line
386 373 cpos = len(c['text'])
387 374 if cpos==0:
388 375 cpos = len(c['line'])
389 376 return self.shell.complete(c['text'], c['line'], cpos)
390 377
391 378 def _object_info(self, context):
392 379 symbol, leftover = self._symbol_from_context(context)
393 380 if symbol is not None and not leftover:
394 381 doc = getattr(symbol, '__doc__', '')
395 382 else:
396 383 doc = ''
397 384 object_info = dict(docstring = doc)
398 385 return object_info
399 386
400 387 def _symbol_from_context(self, context):
401 388 if not context:
402 389 return None, context
403 390
404 391 base_symbol_string = context[0]
405 392 symbol = self.shell.user_ns.get(base_symbol_string, None)
406 393 if symbol is None:
407 394 symbol = __builtin__.__dict__.get(base_symbol_string, None)
408 395 if symbol is None:
409 396 return None, context
410 397
411 398 context = context[1:]
412 399 for i, name in enumerate(context):
413 400 new_symbol = getattr(symbol, name, None)
414 401 if new_symbol is None:
415 402 return symbol, context[i:]
416 403 else:
417 404 symbol = new_symbol
418 405
419 406 return symbol, []
420 407
421 408 def _at_shutdown(self):
422 409 """Actions taken at shutdown by the kernel, called by python's atexit.
423 410 """
424 411 # io.rprint("Kernel at_shutdown") # dbg
425 412 if self._shutdown_message is not None:
426 self.reply_socket.send_json(self._shutdown_message)
427 self.pub_socket.send_json(self._shutdown_message)
413 self.session.send(self.reply_socket, self._shutdown_message)
414 self.session.send(self.pub_socket, self._shutdown_message)
428 415 io.raw_print(self._shutdown_message)
429 416 # A very short sleep to give zmq time to flush its message buffers
430 417 # before Python truly shuts down.
431 418 time.sleep(0.01)
432 419
433 420
434 421 class QtKernel(Kernel):
435 422 """A Kernel subclass with Qt support."""
436 423
437 424 def start(self):
438 425 """Start a kernel with QtPy4 event loop integration."""
439 426
440 427 from PyQt4 import QtCore
441 428 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
442 429
443 430 self.app = get_app_qt4([" "])
444 431 self.app.setQuitOnLastWindowClosed(False)
445 432 self.timer = QtCore.QTimer()
446 433 self.timer.timeout.connect(self.do_one_iteration)
447 434 # Units for the timer are in milliseconds
448 435 self.timer.start(1000*self._poll_interval)
449 436 start_event_loop_qt4(self.app)
450 437
451 438
452 439 class WxKernel(Kernel):
453 440 """A Kernel subclass with Wx support."""
454 441
455 442 def start(self):
456 443 """Start a kernel with wx event loop support."""
457 444
458 445 import wx
459 446 from IPython.lib.guisupport import start_event_loop_wx
460 447
461 448 doi = self.do_one_iteration
462 449 # Wx uses milliseconds
463 450 poll_interval = int(1000*self._poll_interval)
464 451
465 452 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
466 453 # We make the Frame hidden when we create it in the main app below.
467 454 class TimerFrame(wx.Frame):
468 455 def __init__(self, func):
469 456 wx.Frame.__init__(self, None, -1)
470 457 self.timer = wx.Timer(self)
471 458 # Units for the timer are in milliseconds
472 459 self.timer.Start(poll_interval)
473 460 self.Bind(wx.EVT_TIMER, self.on_timer)
474 461 self.func = func
475 462
476 463 def on_timer(self, event):
477 464 self.func()
478 465
479 466 # We need a custom wx.App to create our Frame subclass that has the
480 467 # wx.Timer to drive the ZMQ event loop.
481 468 class IPWxApp(wx.App):
482 469 def OnInit(self):
483 470 self.frame = TimerFrame(doi)
484 471 self.frame.Show(False)
485 472 return True
486 473
487 474 # The redirect=False here makes sure that wx doesn't replace
488 475 # sys.stdout/stderr with its own classes.
489 476 self.app = IPWxApp(redirect=False)
490 477 start_event_loop_wx(self.app)
491 478
492 479
493 480 class TkKernel(Kernel):
494 481 """A Kernel subclass with Tk support."""
495 482
496 483 def start(self):
497 484 """Start a Tk enabled event loop."""
498 485
499 486 import Tkinter
500 487 doi = self.do_one_iteration
501 488 # Tk uses milliseconds
502 489 poll_interval = int(1000*self._poll_interval)
503 490 # For Tkinter, we create a Tk object and call its withdraw method.
504 491 class Timer(object):
505 492 def __init__(self, func):
506 493 self.app = Tkinter.Tk()
507 494 self.app.withdraw()
508 495 self.func = func
509 496
510 497 def on_timer(self):
511 498 self.func()
512 499 self.app.after(poll_interval, self.on_timer)
513 500
514 501 def start(self):
515 502 self.on_timer() # Call it once to get things going.
516 503 self.app.mainloop()
517 504
518 505 self.timer = Timer(doi)
519 506 self.timer.start()
520 507
521 508
522 509 class GTKKernel(Kernel):
523 510 """A Kernel subclass with GTK support."""
524 511
525 512 def start(self):
526 513 """Start the kernel, coordinating with the GTK event loop"""
527 514 from .gui.gtkembed import GTKEmbed
528 515
529 516 gtk_kernel = GTKEmbed(self)
530 517 gtk_kernel.start()
531 518
532 519
533 520 #-----------------------------------------------------------------------------
534 521 # Kernel main and launch functions
535 522 #-----------------------------------------------------------------------------
536 523
537 524 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
538 525 independent=False, pylab=False, colors=None):
539 526 """Launches a localhost kernel, binding to the specified ports.
540 527
541 528 Parameters
542 529 ----------
543 530 ip : str, optional
544 531 The ip address the kernel will bind to.
545 532
546 533 xrep_port : int, optional
547 534 The port to use for XREP channel.
548 535
549 536 pub_port : int, optional
550 537 The port to use for the SUB channel.
551 538
552 539 req_port : int, optional
553 540 The port to use for the REQ (raw input) channel.
554 541
555 542 hb_port : int, optional
556 543 The port to use for the hearbeat REP channel.
557 544
558 545 independent : bool, optional (default False)
559 546 If set, the kernel process is guaranteed to survive if this process
560 547 dies. If not set, an effort is made to ensure that the kernel is killed
561 548 when this process dies. Note that in this case it is still good practice
562 549 to kill kernels manually before exiting.
563 550
564 551 pylab : bool or string, optional (default False)
565 552 If not False, the kernel will be launched with pylab enabled. If a
566 553 string is passed, matplotlib will use the specified backend. Otherwise,
567 554 matplotlib's default backend will be used.
568 555
569 556 colors : None or string, optional (default None)
570 557 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
571 558
572 559 Returns
573 560 -------
574 561 A tuple of form:
575 562 (kernel_process, xrep_port, pub_port, req_port)
576 563 where kernel_process is a Popen object and the ports are integers.
577 564 """
578 565 extra_arguments = []
579 566 if pylab:
580 567 extra_arguments.append('--pylab')
581 568 if isinstance(pylab, basestring):
582 569 extra_arguments.append(pylab)
583 570 if ip is not None:
584 571 extra_arguments.append('--ip')
585 572 if isinstance(ip, basestring):
586 573 extra_arguments.append(ip)
587 574 if colors is not None:
588 575 extra_arguments.append('--colors')
589 576 extra_arguments.append(colors)
590 577 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
591 578 xrep_port, pub_port, req_port, hb_port,
592 579 independent, extra_arguments)
593 580
594 581
595 582 def main():
596 583 """ The IPython kernel main entry point.
597 584 """
598 585 parser = make_argument_parser()
599 586 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
600 587 const='auto', help = \
601 588 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
602 589 given, the GUI backend is matplotlib's, otherwise use one of: \
603 590 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
604 591 parser.add_argument('--colors',
605 592 type=str, dest='colors',
606 593 help="Set the color scheme (NoColor, Linux, and LightBG).",
607 594 metavar='ZMQInteractiveShell.colors')
608 595 namespace = parser.parse_args()
609 596
610 597 kernel_class = Kernel
611 598
612 599 kernel_classes = {
613 600 'qt' : QtKernel,
614 601 'qt4': QtKernel,
615 602 'inline': Kernel,
616 603 'wx' : WxKernel,
617 604 'tk' : TkKernel,
618 605 'gtk': GTKKernel,
619 606 }
620 607 if namespace.pylab:
621 608 if namespace.pylab == 'auto':
622 609 gui, backend = pylabtools.find_gui_and_backend()
623 610 else:
624 611 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
625 612 kernel_class = kernel_classes.get(gui)
626 613 if kernel_class is None:
627 614 raise ValueError('GUI is not supported: %r' % gui)
628 615 pylabtools.activate_matplotlib(backend)
629 616 if namespace.colors:
630 617 ZMQInteractiveShell.colors=namespace.colors
631 618
632 619 kernel = make_kernel(namespace, kernel_class, OutStream)
633 620
634 621 if namespace.pylab:
635 622 pylabtools.import_pylab(kernel.shell.user_ns, backend,
636 623 shell=kernel.shell)
637 624
638 625 start_kernel(namespace, kernel)
639 626
640 627
641 628 if __name__ == '__main__':
642 629 main()
@@ -1,906 +1,908 b''
1 1 """Base classes to manage the interaction with a running kernel.
2 2
3 3 TODO
4 4 * Create logger to handle debugging and console messages.
5 5 """
6 6
7 7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008-2010 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17
18 18 # Standard library imports.
19 19 import atexit
20 20 from Queue import Queue, Empty
21 21 from subprocess import Popen
22 22 import signal
23 23 import sys
24 24 from threading import Thread
25 25 import time
26 26
27 27 # System library imports.
28 28 import zmq
29 29 from zmq import POLLIN, POLLOUT, POLLERR
30 30 from zmq.eventloop import ioloop
31 31
32 32 # Local imports.
33 33 from IPython.utils import io
34 34 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
35 35 from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress
36 from session import Session
36 from session import Session, Message
37 37
38 38 #-----------------------------------------------------------------------------
39 39 # Constants and exceptions
40 40 #-----------------------------------------------------------------------------
41 41
42 42 class InvalidPortNumber(Exception):
43 43 pass
44 44
45 45 #-----------------------------------------------------------------------------
46 46 # Utility functions
47 47 #-----------------------------------------------------------------------------
48 48
49 49 # some utilities to validate message structure, these might get moved elsewhere
50 50 # if they prove to have more generic utility
51 51
52 52 def validate_string_list(lst):
53 53 """Validate that the input is a list of strings.
54 54
55 55 Raises ValueError if not."""
56 56 if not isinstance(lst, list):
57 57 raise ValueError('input %r must be a list' % lst)
58 58 for x in lst:
59 59 if not isinstance(x, basestring):
60 60 raise ValueError('element %r in list must be a string' % x)
61 61
62 62
63 63 def validate_string_dict(dct):
64 64 """Validate that the input is a dict with string keys and values.
65 65
66 66 Raises ValueError if not."""
67 67 for k,v in dct.iteritems():
68 68 if not isinstance(k, basestring):
69 69 raise ValueError('key %r in dict must be a string' % k)
70 70 if not isinstance(v, basestring):
71 71 raise ValueError('value %r in dict must be a string' % v)
72 72
73 73
74 74 #-----------------------------------------------------------------------------
75 75 # ZMQ Socket Channel classes
76 76 #-----------------------------------------------------------------------------
77 77
78 78 class ZmqSocketChannel(Thread):
79 79 """The base class for the channels that use ZMQ sockets.
80 80 """
81 81 context = None
82 82 session = None
83 83 socket = None
84 84 ioloop = None
85 85 iostate = None
86 86 _address = None
87 87
88 88 def __init__(self, context, session, address):
89 89 """Create a channel
90 90
91 91 Parameters
92 92 ----------
93 93 context : :class:`zmq.Context`
94 94 The ZMQ context to use.
95 95 session : :class:`session.Session`
96 96 The session to use.
97 97 address : tuple
98 98 Standard (ip, port) tuple that the kernel is listening on.
99 99 """
100 100 super(ZmqSocketChannel, self).__init__()
101 101 self.daemon = True
102 102
103 103 self.context = context
104 104 self.session = session
105 105 if address[1] == 0:
106 106 message = 'The port number for a channel cannot be 0.'
107 107 raise InvalidPortNumber(message)
108 108 self._address = address
109 109
110 110 def stop(self):
111 111 """Stop the channel's activity.
112 112
113 113 This calls :method:`Thread.join` and returns when the thread
114 114 terminates. :class:`RuntimeError` will be raised if
115 115 :method:`self.start` is called again.
116 116 """
117 117 self.join()
118 118
119 119 @property
120 120 def address(self):
121 121 """Get the channel's address as an (ip, port) tuple.
122 122
123 123 By the default, the address is (localhost, 0), where 0 means a random
124 124 port.
125 125 """
126 126 return self._address
127 127
128 128 def add_io_state(self, state):
129 129 """Add IO state to the eventloop.
130 130
131 131 Parameters
132 132 ----------
133 133 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
134 134 The IO state flag to set.
135 135
136 136 This is thread safe as it uses the thread safe IOLoop.add_callback.
137 137 """
138 138 def add_io_state_callback():
139 139 if not self.iostate & state:
140 140 self.iostate = self.iostate | state
141 141 self.ioloop.update_handler(self.socket, self.iostate)
142 142 self.ioloop.add_callback(add_io_state_callback)
143 143
144 144 def drop_io_state(self, state):
145 145 """Drop IO state from the eventloop.
146 146
147 147 Parameters
148 148 ----------
149 149 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
150 150 The IO state flag to set.
151 151
152 152 This is thread safe as it uses the thread safe IOLoop.add_callback.
153 153 """
154 154 def drop_io_state_callback():
155 155 if self.iostate & state:
156 156 self.iostate = self.iostate & (~state)
157 157 self.ioloop.update_handler(self.socket, self.iostate)
158 158 self.ioloop.add_callback(drop_io_state_callback)
159 159
160 160
161 161 class XReqSocketChannel(ZmqSocketChannel):
162 162 """The XREQ channel for issues request/replies to the kernel.
163 163 """
164 164
165 165 command_queue = None
166 166
167 167 def __init__(self, context, session, address):
168 168 super(XReqSocketChannel, self).__init__(context, session, address)
169 169 self.command_queue = Queue()
170 170 self.ioloop = ioloop.IOLoop()
171 171
172 172 def run(self):
173 173 """The thread's main activity. Call start() instead."""
174 174 self.socket = self.context.socket(zmq.XREQ)
175 175 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
176 176 self.socket.connect('tcp://%s:%i' % self.address)
177 177 self.iostate = POLLERR|POLLIN
178 178 self.ioloop.add_handler(self.socket, self._handle_events,
179 179 self.iostate)
180 180 self.ioloop.start()
181 181
182 182 def stop(self):
183 183 self.ioloop.stop()
184 184 super(XReqSocketChannel, self).stop()
185 185
186 186 def call_handlers(self, msg):
187 187 """This method is called in the ioloop thread when a message arrives.
188 188
189 189 Subclasses should override this method to handle incoming messages.
190 190 It is important to remember that this method is called in the thread
191 191 so that some logic must be done to ensure that the application leve
192 192 handlers are called in the application thread.
193 193 """
194 194 raise NotImplementedError('call_handlers must be defined in a subclass.')
195 195
196 196 def execute(self, code, silent=False,
197 197 user_variables=None, user_expressions=None):
198 198 """Execute code in the kernel.
199 199
200 200 Parameters
201 201 ----------
202 202 code : str
203 203 A string of Python code.
204 204
205 205 silent : bool, optional (default False)
206 206 If set, the kernel will execute the code as quietly possible.
207 207
208 208 user_variables : list, optional
209 209 A list of variable names to pull from the user's namespace. They
210 210 will come back as a dict with these names as keys and their
211 211 :func:`repr` as values.
212 212
213 213 user_expressions : dict, optional
214 214 A dict with string keys and to pull from the user's
215 215 namespace. They will come back as a dict with these names as keys
216 216 and their :func:`repr` as values.
217 217
218 218 Returns
219 219 -------
220 220 The msg_id of the message sent.
221 221 """
222 222 if user_variables is None:
223 223 user_variables = []
224 224 if user_expressions is None:
225 225 user_expressions = {}
226 226
227 227 # Don't waste network traffic if inputs are invalid
228 228 if not isinstance(code, basestring):
229 229 raise ValueError('code %r must be a string' % code)
230 230 validate_string_list(user_variables)
231 231 validate_string_dict(user_expressions)
232 232
233 233 # Create class for content/msg creation. Related to, but possibly
234 234 # not in Session.
235 235 content = dict(code=code, silent=silent,
236 236 user_variables=user_variables,
237 237 user_expressions=user_expressions)
238 238 msg = self.session.msg('execute_request', content)
239 239 self._queue_request(msg)
240 240 return msg['header']['msg_id']
241 241
242 242 def complete(self, text, line, cursor_pos, block=None):
243 243 """Tab complete text in the kernel's namespace.
244 244
245 245 Parameters
246 246 ----------
247 247 text : str
248 248 The text to complete.
249 249 line : str
250 250 The full line of text that is the surrounding context for the
251 251 text to complete.
252 252 cursor_pos : int
253 253 The position of the cursor in the line where the completion was
254 254 requested.
255 255 block : str, optional
256 256 The full block of code in which the completion is being requested.
257 257
258 258 Returns
259 259 -------
260 260 The msg_id of the message sent.
261 261 """
262 262 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
263 263 msg = self.session.msg('complete_request', content)
264 264 self._queue_request(msg)
265 265 return msg['header']['msg_id']
266 266
267 267 def object_info(self, oname):
268 268 """Get metadata information about an object.
269 269
270 270 Parameters
271 271 ----------
272 272 oname : str
273 273 A string specifying the object name.
274 274
275 275 Returns
276 276 -------
277 277 The msg_id of the message sent.
278 278 """
279 279 content = dict(oname=oname)
280 280 msg = self.session.msg('object_info_request', content)
281 281 self._queue_request(msg)
282 282 return msg['header']['msg_id']
283 283
284 284 def history(self, index=None, raw=False, output=True):
285 285 """Get the history list.
286 286
287 287 Parameters
288 288 ----------
289 289 index : n or (n1, n2) or None
290 290 If n, then the last entries. If a tuple, then all in
291 291 range(n1, n2). If None, then all entries. Raises IndexError if
292 292 the format of index is incorrect.
293 293 raw : bool
294 294 If True, return the raw input.
295 295 output : bool
296 296 If True, then return the output as well.
297 297
298 298 Returns
299 299 -------
300 300 The msg_id of the message sent.
301 301 """
302 302 content = dict(index=index, raw=raw, output=output)
303 303 msg = self.session.msg('history_request', content)
304 304 self._queue_request(msg)
305 305 return msg['header']['msg_id']
306 306
307 307 def shutdown(self, restart=False):
308 308 """Request an immediate kernel shutdown.
309 309
310 310 Upon receipt of the (empty) reply, client code can safely assume that
311 311 the kernel has shut down and it's safe to forcefully terminate it if
312 312 it's still alive.
313 313
314 314 The kernel will send the reply via a function registered with Python's
315 315 atexit module, ensuring it's truly done as the kernel is done with all
316 316 normal operation.
317 317 """
318 318 # Send quit message to kernel. Once we implement kernel-side setattr,
319 319 # this should probably be done that way, but for now this will do.
320 320 msg = self.session.msg('shutdown_request', {'restart':restart})
321 321 self._queue_request(msg)
322 322 return msg['header']['msg_id']
323 323
324 324 def _handle_events(self, socket, events):
325 325 if events & POLLERR:
326 326 self._handle_err()
327 327 if events & POLLOUT:
328 328 self._handle_send()
329 329 if events & POLLIN:
330 330 self._handle_recv()
331 331
332 332 def _handle_recv(self):
333 msg = self.socket.recv_json()
333 ident,msg = self.session.recv(self.socket, 0)
334 334 self.call_handlers(msg)
335 335
336 336 def _handle_send(self):
337 337 try:
338 338 msg = self.command_queue.get(False)
339 339 except Empty:
340 340 pass
341 341 else:
342 self.socket.send_json(msg)
342 self.session.send(self.socket,msg)
343 343 if self.command_queue.empty():
344 344 self.drop_io_state(POLLOUT)
345 345
346 346 def _handle_err(self):
347 347 # We don't want to let this go silently, so eventually we should log.
348 348 raise zmq.ZMQError()
349 349
350 350 def _queue_request(self, msg):
351 351 self.command_queue.put(msg)
352 352 self.add_io_state(POLLOUT)
353 353
354 354
355 355 class SubSocketChannel(ZmqSocketChannel):
356 356 """The SUB channel which listens for messages that the kernel publishes.
357 357 """
358 358
359 359 def __init__(self, context, session, address):
360 360 super(SubSocketChannel, self).__init__(context, session, address)
361 361 self.ioloop = ioloop.IOLoop()
362 362
363 363 def run(self):
364 364 """The thread's main activity. Call start() instead."""
365 365 self.socket = self.context.socket(zmq.SUB)
366 366 self.socket.setsockopt(zmq.SUBSCRIBE,'')
367 367 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
368 368 self.socket.connect('tcp://%s:%i' % self.address)
369 369 self.iostate = POLLIN|POLLERR
370 370 self.ioloop.add_handler(self.socket, self._handle_events,
371 371 self.iostate)
372 372 self.ioloop.start()
373 373
374 374 def stop(self):
375 375 self.ioloop.stop()
376 376 super(SubSocketChannel, self).stop()
377 377
378 378 def call_handlers(self, msg):
379 379 """This method is called in the ioloop thread when a message arrives.
380 380
381 381 Subclasses should override this method to handle incoming messages.
382 382 It is important to remember that this method is called in the thread
383 383 so that some logic must be done to ensure that the application leve
384 384 handlers are called in the application thread.
385 385 """
386 386 raise NotImplementedError('call_handlers must be defined in a subclass.')
387 387
388 388 def flush(self, timeout=1.0):
389 389 """Immediately processes all pending messages on the SUB channel.
390 390
391 391 Callers should use this method to ensure that :method:`call_handlers`
392 392 has been called for all messages that have been received on the
393 393 0MQ SUB socket of this channel.
394 394
395 395 This method is thread safe.
396 396
397 397 Parameters
398 398 ----------
399 399 timeout : float, optional
400 400 The maximum amount of time to spend flushing, in seconds. The
401 401 default is one second.
402 402 """
403 403 # We do the IOLoop callback process twice to ensure that the IOLoop
404 404 # gets to perform at least one full poll.
405 405 stop_time = time.time() + timeout
406 406 for i in xrange(2):
407 407 self._flushed = False
408 408 self.ioloop.add_callback(self._flush)
409 409 while not self._flushed and time.time() < stop_time:
410 410 time.sleep(0.01)
411 411
412 412 def _handle_events(self, socket, events):
413 413 # Turn on and off POLLOUT depending on if we have made a request
414 414 if events & POLLERR:
415 415 self._handle_err()
416 416 if events & POLLIN:
417 417 self._handle_recv()
418 418
419 419 def _handle_err(self):
420 420 # We don't want to let this go silently, so eventually we should log.
421 421 raise zmq.ZMQError()
422 422
423 423 def _handle_recv(self):
424 424 # Get all of the messages we can
425 425 while True:
426 426 try:
427 msg = self.socket.recv_json(zmq.NOBLOCK)
427 ident,msg = self.session.recv(self.socket)
428 428 except zmq.ZMQError:
429 429 # Check the errno?
430 430 # Will this trigger POLLERR?
431 431 break
432 432 else:
433 if msg is None:
434 break
433 435 self.call_handlers(msg)
434 436
435 437 def _flush(self):
436 438 """Callback for :method:`self.flush`."""
437 439 self._flushed = True
438 440
439 441
440 442 class RepSocketChannel(ZmqSocketChannel):
441 443 """A reply channel to handle raw_input requests that the kernel makes."""
442 444
443 445 msg_queue = None
444 446
445 447 def __init__(self, context, session, address):
446 448 super(RepSocketChannel, self).__init__(context, session, address)
447 449 self.ioloop = ioloop.IOLoop()
448 450 self.msg_queue = Queue()
449 451
450 452 def run(self):
451 453 """The thread's main activity. Call start() instead."""
452 454 self.socket = self.context.socket(zmq.XREQ)
453 455 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
454 456 self.socket.connect('tcp://%s:%i' % self.address)
455 457 self.iostate = POLLERR|POLLIN
456 458 self.ioloop.add_handler(self.socket, self._handle_events,
457 459 self.iostate)
458 460 self.ioloop.start()
459 461
460 462 def stop(self):
461 463 self.ioloop.stop()
462 464 super(RepSocketChannel, self).stop()
463 465
464 466 def call_handlers(self, msg):
465 467 """This method is called in the ioloop thread when a message arrives.
466 468
467 469 Subclasses should override this method to handle incoming messages.
468 470 It is important to remember that this method is called in the thread
469 471 so that some logic must be done to ensure that the application leve
470 472 handlers are called in the application thread.
471 473 """
472 474 raise NotImplementedError('call_handlers must be defined in a subclass.')
473 475
474 476 def input(self, string):
475 477 """Send a string of raw input to the kernel."""
476 478 content = dict(value=string)
477 479 msg = self.session.msg('input_reply', content)
478 480 self._queue_reply(msg)
479 481
480 482 def _handle_events(self, socket, events):
481 483 if events & POLLERR:
482 484 self._handle_err()
483 485 if events & POLLOUT:
484 486 self._handle_send()
485 487 if events & POLLIN:
486 488 self._handle_recv()
487 489
488 490 def _handle_recv(self):
489 msg = self.socket.recv_json()
491 ident,msg = self.session.recv(self.socket, 0)
490 492 self.call_handlers(msg)
491 493
492 494 def _handle_send(self):
493 495 try:
494 496 msg = self.msg_queue.get(False)
495 497 except Empty:
496 498 pass
497 499 else:
498 self.socket.send_json(msg)
500 self.session.send(self.socket,msg)
499 501 if self.msg_queue.empty():
500 502 self.drop_io_state(POLLOUT)
501 503
502 504 def _handle_err(self):
503 505 # We don't want to let this go silently, so eventually we should log.
504 506 raise zmq.ZMQError()
505 507
506 508 def _queue_reply(self, msg):
507 509 self.msg_queue.put(msg)
508 510 self.add_io_state(POLLOUT)
509 511
510 512
511 513 class HBSocketChannel(ZmqSocketChannel):
512 514 """The heartbeat channel which monitors the kernel heartbeat.
513 515
514 516 Note that the heartbeat channel is paused by default. As long as you start
515 517 this channel, the kernel manager will ensure that it is paused and un-paused
516 518 as appropriate.
517 519 """
518 520
519 521 time_to_dead = 3.0
520 522 socket = None
521 523 poller = None
522 524 _running = None
523 525 _pause = None
524 526
525 527 def __init__(self, context, session, address):
526 528 super(HBSocketChannel, self).__init__(context, session, address)
527 529 self._running = False
528 530 self._pause = True
529 531
530 532 def _create_socket(self):
531 533 self.socket = self.context.socket(zmq.REQ)
532 534 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
533 535 self.socket.connect('tcp://%s:%i' % self.address)
534 536 self.poller = zmq.Poller()
535 537 self.poller.register(self.socket, zmq.POLLIN)
536 538
537 539 def run(self):
538 540 """The thread's main activity. Call start() instead."""
539 541 self._create_socket()
540 542 self._running = True
541 543 while self._running:
542 544 if self._pause:
543 545 time.sleep(self.time_to_dead)
544 546 else:
545 547 since_last_heartbeat = 0.0
546 548 request_time = time.time()
547 549 try:
548 550 #io.rprint('Ping from HB channel') # dbg
549 self.socket.send_json('ping')
551 self.socket.send(b'ping')
550 552 except zmq.ZMQError, e:
551 553 #io.rprint('*** HB Error:', e) # dbg
552 554 if e.errno == zmq.EFSM:
553 555 #io.rprint('sleep...', self.time_to_dead) # dbg
554 556 time.sleep(self.time_to_dead)
555 557 self._create_socket()
556 558 else:
557 559 raise
558 560 else:
559 561 while True:
560 562 try:
561 self.socket.recv_json(zmq.NOBLOCK)
563 self.socket.recv(zmq.NOBLOCK)
562 564 except zmq.ZMQError, e:
563 565 #io.rprint('*** HB Error 2:', e) # dbg
564 566 if e.errno == zmq.EAGAIN:
565 567 before_poll = time.time()
566 568 until_dead = self.time_to_dead - (before_poll -
567 569 request_time)
568 570
569 571 # When the return value of poll() is an empty
570 572 # list, that is when things have gone wrong
571 573 # (zeromq bug). As long as it is not an empty
572 574 # list, poll is working correctly even if it
573 575 # returns quickly. Note: poll timeout is in
574 576 # milliseconds.
575 577 self.poller.poll(1000*until_dead)
576 578
577 579 since_last_heartbeat = time.time()-request_time
578 580 if since_last_heartbeat > self.time_to_dead:
579 581 self.call_handlers(since_last_heartbeat)
580 582 break
581 583 else:
582 584 # FIXME: We should probably log this instead.
583 585 raise
584 586 else:
585 587 until_dead = self.time_to_dead - (time.time() -
586 588 request_time)
587 589 if until_dead > 0.0:
588 590 #io.rprint('sleep...', self.time_to_dead) # dbg
589 591 time.sleep(until_dead)
590 592 break
591 593
592 594 def pause(self):
593 595 """Pause the heartbeat."""
594 596 self._pause = True
595 597
596 598 def unpause(self):
597 599 """Unpause the heartbeat."""
598 600 self._pause = False
599 601
600 602 def is_beating(self):
601 603 """Is the heartbeat running and not paused."""
602 604 if self.is_alive() and not self._pause:
603 605 return True
604 606 else:
605 607 return False
606 608
607 609 def stop(self):
608 610 self._running = False
609 611 super(HBSocketChannel, self).stop()
610 612
611 613 def call_handlers(self, since_last_heartbeat):
612 614 """This method is called in the ioloop thread when a message arrives.
613 615
614 616 Subclasses should override this method to handle incoming messages.
615 617 It is important to remember that this method is called in the thread
616 618 so that some logic must be done to ensure that the application leve
617 619 handlers are called in the application thread.
618 620 """
619 621 raise NotImplementedError('call_handlers must be defined in a subclass.')
620 622
621 623
622 624 #-----------------------------------------------------------------------------
623 625 # Main kernel manager class
624 626 #-----------------------------------------------------------------------------
625 627
626 628 class KernelManager(HasTraits):
627 629 """ Manages a kernel for a frontend.
628 630
629 631 The SUB channel is for the frontend to receive messages published by the
630 632 kernel.
631 633
632 634 The REQ channel is for the frontend to make requests of the kernel.
633 635
634 636 The REP channel is for the kernel to request stdin (raw_input) from the
635 637 frontend.
636 638 """
637 639 # The PyZMQ Context to use for communication with the kernel.
638 640 context = Instance(zmq.Context,(),{})
639 641
640 642 # The Session to use for communication with the kernel.
641 643 session = Instance(Session,(),{})
642 644
643 645 # The kernel process with which the KernelManager is communicating.
644 646 kernel = Instance(Popen)
645 647
646 648 # The addresses for the communication channels.
647 649 xreq_address = TCPAddress((LOCALHOST, 0))
648 650 sub_address = TCPAddress((LOCALHOST, 0))
649 651 rep_address = TCPAddress((LOCALHOST, 0))
650 652 hb_address = TCPAddress((LOCALHOST, 0))
651 653
652 654 # The classes to use for the various channels.
653 655 xreq_channel_class = Type(XReqSocketChannel)
654 656 sub_channel_class = Type(SubSocketChannel)
655 657 rep_channel_class = Type(RepSocketChannel)
656 658 hb_channel_class = Type(HBSocketChannel)
657 659
658 660 # Protected traits.
659 661 _launch_args = Any
660 662 _xreq_channel = Any
661 663 _sub_channel = Any
662 664 _rep_channel = Any
663 665 _hb_channel = Any
664 666
665 667 def __init__(self, **kwargs):
666 668 super(KernelManager, self).__init__(**kwargs)
667 669 # Uncomment this to try closing the context.
668 670 # atexit.register(self.context.close)
669 671
670 672 #--------------------------------------------------------------------------
671 673 # Channel management methods:
672 674 #--------------------------------------------------------------------------
673 675
674 676 def start_channels(self, xreq=True, sub=True, rep=True, hb=True):
675 677 """Starts the channels for this kernel.
676 678
677 679 This will create the channels if they do not exist and then start
678 680 them. If port numbers of 0 are being used (random ports) then you
679 681 must first call :method:`start_kernel`. If the channels have been
680 682 stopped and you call this, :class:`RuntimeError` will be raised.
681 683 """
682 684 if xreq:
683 685 self.xreq_channel.start()
684 686 if sub:
685 687 self.sub_channel.start()
686 688 if rep:
687 689 self.rep_channel.start()
688 690 if hb:
689 691 self.hb_channel.start()
690 692
691 693 def stop_channels(self):
692 694 """Stops all the running channels for this kernel.
693 695 """
694 696 if self.xreq_channel.is_alive():
695 697 self.xreq_channel.stop()
696 698 if self.sub_channel.is_alive():
697 699 self.sub_channel.stop()
698 700 if self.rep_channel.is_alive():
699 701 self.rep_channel.stop()
700 702 if self.hb_channel.is_alive():
701 703 self.hb_channel.stop()
702 704
703 705 @property
704 706 def channels_running(self):
705 707 """Are any of the channels created and running?"""
706 708 return (self.xreq_channel.is_alive() or self.sub_channel.is_alive() or
707 709 self.rep_channel.is_alive() or self.hb_channel.is_alive())
708 710
709 711 #--------------------------------------------------------------------------
710 712 # Kernel process management methods:
711 713 #--------------------------------------------------------------------------
712 714
713 715 def start_kernel(self, **kw):
714 716 """Starts a kernel process and configures the manager to use it.
715 717
716 718 If random ports (port=0) are being used, this method must be called
717 719 before the channels are created.
718 720
719 721 Parameters:
720 722 -----------
721 723 ipython : bool, optional (default True)
722 724 Whether to use an IPython kernel instead of a plain Python kernel.
723 725 """
724 726 xreq, sub, rep, hb = self.xreq_address, self.sub_address, \
725 727 self.rep_address, self.hb_address
726 728 if xreq[0] not in LOCAL_IPS or sub[0] not in LOCAL_IPS or \
727 729 rep[0] not in LOCAL_IPS or hb[0] not in LOCAL_IPS:
728 730 raise RuntimeError("Can only launch a kernel on a local interface. "
729 731 "Make sure that the '*_address' attributes are "
730 732 "configured properly. "
731 733 "Currently valid addresses are: %s"%LOCAL_IPS
732 734 )
733 735
734 736 self._launch_args = kw.copy()
735 737 if kw.pop('ipython', True):
736 738 from ipkernel import launch_kernel
737 739 else:
738 740 from pykernel import launch_kernel
739 741 self.kernel, xrep, pub, req, _hb = launch_kernel(
740 742 xrep_port=xreq[1], pub_port=sub[1],
741 743 req_port=rep[1], hb_port=hb[1], **kw)
742 744 self.xreq_address = (xreq[0], xrep)
743 745 self.sub_address = (sub[0], pub)
744 746 self.rep_address = (rep[0], req)
745 747 self.hb_address = (hb[0], _hb)
746 748
747 749 def shutdown_kernel(self, restart=False):
748 750 """ Attempts to the stop the kernel process cleanly. If the kernel
749 751 cannot be stopped, it is killed, if possible.
750 752 """
751 753 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
752 754 if sys.platform == 'win32':
753 755 self.kill_kernel()
754 756 return
755 757
756 758 # Pause the heart beat channel if it exists.
757 759 if self._hb_channel is not None:
758 760 self._hb_channel.pause()
759 761
760 762 # Don't send any additional kernel kill messages immediately, to give
761 763 # the kernel a chance to properly execute shutdown actions. Wait for at
762 764 # most 1s, checking every 0.1s.
763 765 self.xreq_channel.shutdown(restart=restart)
764 766 for i in range(10):
765 767 if self.is_alive:
766 768 time.sleep(0.1)
767 769 else:
768 770 break
769 771 else:
770 772 # OK, we've waited long enough.
771 773 if self.has_kernel:
772 774 self.kill_kernel()
773 775
774 776 def restart_kernel(self, now=False):
775 777 """Restarts a kernel with the same arguments that were used to launch
776 778 it. If the old kernel was launched with random ports, the same ports
777 779 will be used for the new kernel.
778 780
779 781 Parameters
780 782 ----------
781 783 now : bool, optional
782 784 If True, the kernel is forcefully restarted *immediately*, without
783 785 having a chance to do any cleanup action. Otherwise the kernel is
784 786 given 1s to clean up before a forceful restart is issued.
785 787
786 788 In all cases the kernel is restarted, the only difference is whether
787 789 it is given a chance to perform a clean shutdown or not.
788 790 """
789 791 if self._launch_args is None:
790 792 raise RuntimeError("Cannot restart the kernel. "
791 793 "No previous call to 'start_kernel'.")
792 794 else:
793 795 if self.has_kernel:
794 796 if now:
795 797 self.kill_kernel()
796 798 else:
797 799 self.shutdown_kernel(restart=True)
798 800 self.start_kernel(**self._launch_args)
799 801
800 802 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
801 803 # unless there is some delay here.
802 804 if sys.platform == 'win32':
803 805 time.sleep(0.2)
804 806
805 807 @property
806 808 def has_kernel(self):
807 809 """Returns whether a kernel process has been specified for the kernel
808 810 manager.
809 811 """
810 812 return self.kernel is not None
811 813
812 814 def kill_kernel(self):
813 815 """ Kill the running kernel. """
814 816 if self.has_kernel:
815 817 # Pause the heart beat channel if it exists.
816 818 if self._hb_channel is not None:
817 819 self._hb_channel.pause()
818 820
819 821 # Attempt to kill the kernel.
820 822 try:
821 823 self.kernel.kill()
822 824 except OSError, e:
823 825 # In Windows, we will get an Access Denied error if the process
824 826 # has already terminated. Ignore it.
825 827 if not (sys.platform == 'win32' and e.winerror == 5):
826 828 raise
827 829 self.kernel = None
828 830 else:
829 831 raise RuntimeError("Cannot kill kernel. No kernel is running!")
830 832
831 833 def interrupt_kernel(self):
832 834 """ Interrupts the kernel. Unlike ``signal_kernel``, this operation is
833 835 well supported on all platforms.
834 836 """
835 837 if self.has_kernel:
836 838 if sys.platform == 'win32':
837 839 from parentpoller import ParentPollerWindows as Poller
838 840 Poller.send_interrupt(self.kernel.win32_interrupt_event)
839 841 else:
840 842 self.kernel.send_signal(signal.SIGINT)
841 843 else:
842 844 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
843 845
844 846 def signal_kernel(self, signum):
845 847 """ Sends a signal to the kernel. Note that since only SIGTERM is
846 848 supported on Windows, this function is only useful on Unix systems.
847 849 """
848 850 if self.has_kernel:
849 851 self.kernel.send_signal(signum)
850 852 else:
851 853 raise RuntimeError("Cannot signal kernel. No kernel is running!")
852 854
853 855 @property
854 856 def is_alive(self):
855 857 """Is the kernel process still running?"""
856 858 # FIXME: not using a heartbeat means this method is broken for any
857 859 # remote kernel, it's only capable of handling local kernels.
858 860 if self.has_kernel:
859 861 if self.kernel.poll() is None:
860 862 return True
861 863 else:
862 864 return False
863 865 else:
864 866 # We didn't start the kernel with this KernelManager so we don't
865 867 # know if it is running. We should use a heartbeat for this case.
866 868 return True
867 869
868 870 #--------------------------------------------------------------------------
869 871 # Channels used for communication with the kernel:
870 872 #--------------------------------------------------------------------------
871 873
872 874 @property
873 875 def xreq_channel(self):
874 876 """Get the REQ socket channel object to make requests of the kernel."""
875 877 if self._xreq_channel is None:
876 878 self._xreq_channel = self.xreq_channel_class(self.context,
877 879 self.session,
878 880 self.xreq_address)
879 881 return self._xreq_channel
880 882
881 883 @property
882 884 def sub_channel(self):
883 885 """Get the SUB socket channel object."""
884 886 if self._sub_channel is None:
885 887 self._sub_channel = self.sub_channel_class(self.context,
886 888 self.session,
887 889 self.sub_address)
888 890 return self._sub_channel
889 891
890 892 @property
891 893 def rep_channel(self):
892 894 """Get the REP socket channel object to handle stdin (raw_input)."""
893 895 if self._rep_channel is None:
894 896 self._rep_channel = self.rep_channel_class(self.context,
895 897 self.session,
896 898 self.rep_address)
897 899 return self._rep_channel
898 900
899 901 @property
900 902 def hb_channel(self):
901 903 """Get the REP socket channel object to handle stdin (raw_input)."""
902 904 if self._hb_channel is None:
903 905 self._hb_channel = self.hb_channel_class(self.context,
904 906 self.session,
905 907 self.hb_address)
906 908 return self._hb_channel
@@ -1,305 +1,296 b''
1 1 #!/usr/bin/env python
2 2 """A simple interactive kernel that talks to a frontend over 0MQ.
3 3
4 4 Things to do:
5 5
6 6 * Implement `set_parent` logic. Right before doing exec, the Kernel should
7 7 call set_parent on all the PUB objects with the message about to be executed.
8 8 * Implement random port and security key logic.
9 9 * Implement control messages.
10 10 * Implement event loop and poll version.
11 11 """
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 # Standard library imports.
18 18 import __builtin__
19 19 from code import CommandCompiler
20 20 import sys
21 21 import time
22 22 import traceback
23 23
24 24 # System library imports.
25 25 import zmq
26 26
27 27 # Local imports.
28 28 from IPython.utils.traitlets import HasTraits, Instance
29 29 from completer import KernelCompleter
30 30 from entry_point import base_launch_kernel, make_default_main
31 31 from session import Session, Message
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Main kernel class
35 35 #-----------------------------------------------------------------------------
36 36
37 37 class Kernel(HasTraits):
38 38
39 39 # Private interface
40 40
41 41 # This is a dict of port number that the kernel is listening on. It is set
42 42 # by record_ports and used by connect_request.
43 43 _recorded_ports = None
44 44
45 45 #---------------------------------------------------------------------------
46 46 # Kernel interface
47 47 #---------------------------------------------------------------------------
48 48
49 49 session = Instance(Session)
50 50 reply_socket = Instance('zmq.Socket')
51 51 pub_socket = Instance('zmq.Socket')
52 52 req_socket = Instance('zmq.Socket')
53 53
54 54 def __init__(self, **kwargs):
55 55 super(Kernel, self).__init__(**kwargs)
56 56 self.user_ns = {}
57 57 self.history = []
58 58 self.compiler = CommandCompiler()
59 59 self.completer = KernelCompleter(self.user_ns)
60 60
61 61 # Build dict of handlers for message types
62 62 msg_types = [ 'execute_request', 'complete_request',
63 63 'object_info_request', 'shutdown_request' ]
64 64 self.handlers = {}
65 65 for msg_type in msg_types:
66 66 self.handlers[msg_type] = getattr(self, msg_type)
67 67
68 68 def start(self):
69 69 """ Start the kernel main loop.
70 70 """
71 71 while True:
72 ident = self.reply_socket.recv()
73 assert self.reply_socket.rcvmore(), "Missing message part."
74 msg = self.reply_socket.recv_json()
72 ident,msg = self.session.recv(self.reply_socket,0)
73 assert ident is not None, "Missing message part."
75 74 omsg = Message(msg)
76 75 print>>sys.__stdout__
77 76 print>>sys.__stdout__, omsg
78 77 handler = self.handlers.get(omsg.msg_type, None)
79 78 if handler is None:
80 79 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
81 80 else:
82 81 handler(ident, omsg)
83 82
84 83 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
85 84 """Record the ports that this kernel is using.
86 85
87 86 The creator of the Kernel instance must call this methods if they
88 87 want the :meth:`connect_request` method to return the port numbers.
89 88 """
90 89 self._recorded_ports = {
91 90 'xrep_port' : xrep_port,
92 91 'pub_port' : pub_port,
93 92 'req_port' : req_port,
94 93 'hb_port' : hb_port
95 94 }
96 95
97 96 #---------------------------------------------------------------------------
98 97 # Kernel request handlers
99 98 #---------------------------------------------------------------------------
100 99
101 100 def execute_request(self, ident, parent):
102 101 try:
103 102 code = parent[u'content'][u'code']
104 103 except:
105 104 print>>sys.__stderr__, "Got bad msg: "
106 105 print>>sys.__stderr__, Message(parent)
107 106 return
108 pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent)
109 self.pub_socket.send_json(pyin_msg)
107 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
110 108
111 109 try:
112 110 comp_code = self.compiler(code, '<zmq-kernel>')
113 111
114 112 # Replace raw_input. Note that is not sufficient to replace
115 113 # raw_input in the user namespace.
116 114 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
117 115 __builtin__.raw_input = raw_input
118 116
119 117 # Set the parent message of the display hook and out streams.
120 118 sys.displayhook.set_parent(parent)
121 119 sys.stdout.set_parent(parent)
122 120 sys.stderr.set_parent(parent)
123 121
124 122 exec comp_code in self.user_ns, self.user_ns
125 123 except:
126 124 etype, evalue, tb = sys.exc_info()
127 125 tb = traceback.format_exception(etype, evalue, tb)
128 126 exc_content = {
129 127 u'status' : u'error',
130 128 u'traceback' : tb,
131 129 u'ename' : unicode(etype.__name__),
132 130 u'evalue' : unicode(evalue)
133 131 }
134 exc_msg = self.session.msg(u'pyerr', exc_content, parent)
135 self.pub_socket.send_json(exc_msg)
132 exc_msg = self.session.send(self.pub_socket, u'pyerr', exc_content, parent)
136 133 reply_content = exc_content
137 134 else:
138 135 reply_content = { 'status' : 'ok', 'payload' : {} }
139 136
140 137 # Flush output before sending the reply.
141 138 sys.stderr.flush()
142 139 sys.stdout.flush()
143 140
144 141 # Send the reply.
145 reply_msg = self.session.msg(u'execute_reply', reply_content, parent)
142 reply_msg = self.session.send(self.reply_socket, u'execute_reply', reply_content, parent, ident=ident)
146 143 print>>sys.__stdout__, Message(reply_msg)
147 self.reply_socket.send(ident, zmq.SNDMORE)
148 self.reply_socket.send_json(reply_msg)
149 144 if reply_msg['content']['status'] == u'error':
150 145 self._abort_queue()
151 146
152 147 def complete_request(self, ident, parent):
153 148 matches = {'matches' : self._complete(parent),
154 149 'status' : 'ok'}
155 150 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
156 151 matches, parent, ident)
157 152 print >> sys.__stdout__, completion_msg
158 153
159 154 def object_info_request(self, ident, parent):
160 155 context = parent['content']['oname'].split('.')
161 156 object_info = self._object_info(context)
162 157 msg = self.session.send(self.reply_socket, 'object_info_reply',
163 158 object_info, parent, ident)
164 159 print >> sys.__stdout__, msg
165 160
166 161 def shutdown_request(self, ident, parent):
167 162 content = dict(parent['content'])
168 163 msg = self.session.send(self.reply_socket, 'shutdown_reply',
169 164 content, parent, ident)
170 165 msg = self.session.send(self.pub_socket, 'shutdown_reply',
171 166 content, parent, ident)
172 167 print >> sys.__stdout__, msg
173 168 time.sleep(0.1)
174 169 sys.exit(0)
175 170
176 171 #---------------------------------------------------------------------------
177 172 # Protected interface
178 173 #---------------------------------------------------------------------------
179 174
180 175 def _abort_queue(self):
181 176 while True:
182 177 try:
183 ident = self.reply_socket.recv(zmq.NOBLOCK)
178 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
184 179 except zmq.ZMQError, e:
185 180 if e.errno == zmq.EAGAIN:
186 181 break
187 182 else:
188 assert self.reply_socket.rcvmore(), "Missing message part."
189 msg = self.reply_socket.recv_json()
183 assert ident is not None, "Missing message part."
190 184 print>>sys.__stdout__, "Aborting:"
191 185 print>>sys.__stdout__, Message(msg)
192 186 msg_type = msg['msg_type']
193 187 reply_type = msg_type.split('_')[0] + '_reply'
194 reply_msg = self.session.msg(reply_type, {'status':'aborted'}, msg)
188 reply_msg = self.session.send(self.reply_socket, reply_type, {'status':'aborted'}, msg, ident=ident)
195 189 print>>sys.__stdout__, Message(reply_msg)
196 self.reply_socket.send(ident,zmq.SNDMORE)
197 self.reply_socket.send_json(reply_msg)
198 190 # We need to wait a bit for requests to come in. This can probably
199 191 # be set shorter for true asynchronous clients.
200 192 time.sleep(0.1)
201 193
202 194 def _raw_input(self, prompt, ident, parent):
203 195 # Flush output before making the request.
204 196 sys.stderr.flush()
205 197 sys.stdout.flush()
206 198
207 199 # Send the input request.
208 200 content = dict(prompt=prompt)
209 msg = self.session.msg(u'input_request', content, parent)
210 self.req_socket.send_json(msg)
201 msg = self.session.send(self.req_socket, u'input_request', content, parent)
211 202
212 203 # Await a response.
213 reply = self.req_socket.recv_json()
204 ident,reply = self.session.recv(self.req_socket, 0)
214 205 try:
215 206 value = reply['content']['value']
216 207 except:
217 208 print>>sys.__stderr__, "Got bad raw_input reply: "
218 209 print>>sys.__stderr__, Message(parent)
219 210 value = ''
220 211 return value
221 212
222 213 def _complete(self, msg):
223 214 return self.completer.complete(msg.content.line, msg.content.text)
224 215
225 216 def _object_info(self, context):
226 217 symbol, leftover = self._symbol_from_context(context)
227 218 if symbol is not None and not leftover:
228 219 doc = getattr(symbol, '__doc__', '')
229 220 else:
230 221 doc = ''
231 222 object_info = dict(docstring = doc)
232 223 return object_info
233 224
234 225 def _symbol_from_context(self, context):
235 226 if not context:
236 227 return None, context
237 228
238 229 base_symbol_string = context[0]
239 230 symbol = self.user_ns.get(base_symbol_string, None)
240 231 if symbol is None:
241 232 symbol = __builtin__.__dict__.get(base_symbol_string, None)
242 233 if symbol is None:
243 234 return None, context
244 235
245 236 context = context[1:]
246 237 for i, name in enumerate(context):
247 238 new_symbol = getattr(symbol, name, None)
248 239 if new_symbol is None:
249 240 return symbol, context[i:]
250 241 else:
251 242 symbol = new_symbol
252 243
253 244 return symbol, []
254 245
255 246 #-----------------------------------------------------------------------------
256 247 # Kernel main and launch functions
257 248 #-----------------------------------------------------------------------------
258 249
259 250 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
260 251 independent=False):
261 252 """ Launches a localhost kernel, binding to the specified ports.
262 253
263 254 Parameters
264 255 ----------
265 256 ip : str, optional
266 257 The ip address the kernel will bind to.
267 258
268 259 xrep_port : int, optional
269 260 The port to use for XREP channel.
270 261
271 262 pub_port : int, optional
272 263 The port to use for the SUB channel.
273 264
274 265 req_port : int, optional
275 266 The port to use for the REQ (raw input) channel.
276 267
277 268 hb_port : int, optional
278 269 The port to use for the hearbeat REP channel.
279 270
280 271 independent : bool, optional (default False)
281 272 If set, the kernel process is guaranteed to survive if this process
282 273 dies. If not set, an effort is made to ensure that the kernel is killed
283 274 when this process dies. Note that in this case it is still good practice
284 275 to kill kernels manually before exiting.
285 276
286 277 Returns
287 278 -------
288 279 A tuple of form:
289 280 (kernel_process, xrep_port, pub_port, req_port)
290 281 where kernel_process is a Popen object and the ports are integers.
291 282 """
292 283 extra_arguments = []
293 284 if ip is not None:
294 285 extra_arguments.append('--ip')
295 286 if isinstance(ip, basestring):
296 287 extra_arguments.append(ip)
297 288
298 289 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
299 290 xrep_port, pub_port, req_port, hb_port,
300 291 independent, extra_arguments=extra_arguments)
301 292
302 293 main = make_default_main(Kernel)
303 294
304 295 if __name__ == '__main__':
305 296 main()
@@ -1,122 +1,135 b''
1 1 import os
2 2 import uuid
3 3 import pprint
4 4
5 5 import zmq
6 6
7 from zmq.utils import jsonapi as json
8
7 9 class Message(object):
8 10 """A simple message object that maps dict keys to attributes.
9 11
10 12 A Message can be created from a dict and a dict from a Message instance
11 13 simply by calling dict(msg_obj)."""
12 14
13 15 def __init__(self, msg_dict):
14 16 dct = self.__dict__
15 17 for k, v in msg_dict.iteritems():
16 18 if isinstance(v, dict):
17 19 v = Message(v)
18 20 dct[k] = v
19 21
20 22 # Having this iterator lets dict(msg_obj) work out of the box.
21 23 def __iter__(self):
22 24 return iter(self.__dict__.iteritems())
23 25
24 26 def __repr__(self):
25 27 return repr(self.__dict__)
26 28
27 29 def __str__(self):
28 30 return pprint.pformat(self.__dict__)
29 31
30 32 def __contains__(self, k):
31 33 return k in self.__dict__
32 34
33 35 def __getitem__(self, k):
34 36 return self.__dict__[k]
35 37
36 38
37 39 def msg_header(msg_id, username, session):
38 40 return {
39 41 'msg_id' : msg_id,
40 42 'username' : username,
41 43 'session' : session
42 44 }
43 45
44 46
45 47 def extract_header(msg_or_header):
46 48 """Given a message or header, return the header."""
47 49 if not msg_or_header:
48 50 return {}
49 51 try:
50 52 # See if msg_or_header is the entire message.
51 53 h = msg_or_header['header']
52 54 except KeyError:
53 55 try:
54 56 # See if msg_or_header is just the header
55 57 h = msg_or_header['msg_id']
56 58 except KeyError:
57 59 raise
58 60 else:
59 61 h = msg_or_header
60 62 if not isinstance(h, dict):
61 63 h = dict(h)
62 64 return h
63 65
64 66
65 67 class Session(object):
66 68
67 69 def __init__(self, username=os.environ.get('USER','username'), session=None):
68 70 self.username = username
69 71 if session is None:
70 72 self.session = str(uuid.uuid4())
71 73 else:
72 74 self.session = session
73 75 self.msg_id = 0
74 76
75 77 def msg_header(self):
76 78 h = msg_header(self.msg_id, self.username, self.session)
77 79 self.msg_id += 1
78 80 return h
79 81
80 82 def msg(self, msg_type, content=None, parent=None):
81 83 msg = {}
82 84 msg['header'] = self.msg_header()
83 85 msg['parent_header'] = {} if parent is None else extract_header(parent)
84 86 msg['msg_type'] = msg_type
85 87 msg['content'] = {} if content is None else content
86 88 return msg
87 89
88 90 def send(self, socket, msg_type, content=None, parent=None, ident=None):
89 msg = self.msg(msg_type, content, parent)
91 if isinstance(msg_type, (Message, dict)):
92 msg = dict(msg_type)
93 else:
94 msg = self.msg(msg_type, content, parent)
90 95 if ident is not None:
91 96 socket.send(ident, zmq.SNDMORE)
92 97 socket.send_json(msg)
93 omsg = Message(msg)
94 return omsg
95
98 # omsg = Message(msg)
99 return msg
100
96 101 def recv(self, socket, mode=zmq.NOBLOCK):
97 102 try:
98 msg = socket.recv_json(mode)
103 msg = socket.recv_multipart(mode)
99 104 except zmq.ZMQError, e:
100 105 if e.errno == zmq.EAGAIN:
101 106 # We can convert EAGAIN to None as we know in this case
102 107 # recv_json won't return None.
103 return None
108 return None,None
104 109 else:
105 110 raise
106 return Message(msg)
111 if len(msg) == 1:
112 ident=None
113 msg = msg[0]
114 elif len(msg) == 2:
115 ident, msg = msg
116 else:
117 raise ValueError("Got message with length > 2, which is invalid")
118
119 return ident, json.loads(msg)
107 120
108 121 def test_msg2obj():
109 122 am = dict(x=1)
110 123 ao = Message(am)
111 124 assert ao.x == am['x']
112 125
113 126 am['y'] = dict(z=1)
114 127 ao = Message(am)
115 128 assert ao.y.z == am['y']['z']
116 129
117 130 k1, k2 = 'y', 'z'
118 131 assert ao[k1][k2] == am[k1][k2]
119 132
120 133 am2 = dict(ao)
121 134 assert am['x'] == am2['x']
122 135 assert am['y']['z'] == am2['y']['z']
@@ -1,581 +1,580 b''
1 1 """A ZMQ-based subclass of InteractiveShell.
2 2
3 3 This code is meant to ease the refactoring of the base InteractiveShell into
4 4 something with a cleaner architecture for 2-process use, without actually
5 5 breaking InteractiveShell itself. So we're doing something a bit ugly, where
6 6 we subclass and override what we want to fix. Once this is working well, we
7 7 can go back to the base class and refactor the code for a cleaner inheritance
8 8 implementation that doesn't rely on so much monkeypatching.
9 9
10 10 But this lets us maintain a fully working IPython as we develop the new
11 11 machinery. This should thus be thought of as scaffolding.
12 12 """
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from __future__ import print_function
17 17
18 18 # Stdlib
19 19 import inspect
20 20 import os
21 21 import re
22 22
23 23 # Our own
24 24 from IPython.core.interactiveshell import (
25 25 InteractiveShell, InteractiveShellABC
26 26 )
27 27 from IPython.core import page
28 28 from IPython.core.displayhook import DisplayHook
29 29 from IPython.core.macro import Macro
30 30 from IPython.core.payloadpage import install_payload_page
31 31 from IPython.utils import io
32 32 from IPython.utils.path import get_py_filename
33 33 from IPython.utils.text import StringTypes
34 34 from IPython.utils.traitlets import Instance, Type, Dict
35 35 from IPython.utils.warn import warn
36 36 from IPython.zmq.session import extract_header
37 37 from session import Session
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # Globals and side-effects
41 41 #-----------------------------------------------------------------------------
42 42
43 43 # Install the payload version of page.
44 44 install_payload_page()
45 45
46 46 #-----------------------------------------------------------------------------
47 47 # Functions and classes
48 48 #-----------------------------------------------------------------------------
49 49
50 50 class ZMQDisplayHook(DisplayHook):
51 51
52 52 session = Instance(Session)
53 53 pub_socket = Instance('zmq.Socket')
54 54 parent_header = Dict({})
55 55
56 56 def set_parent(self, parent):
57 57 """Set the parent for outbound messages."""
58 58 self.parent_header = extract_header(parent)
59 59
60 60 def start_displayhook(self):
61 61 self.msg = self.session.msg(u'pyout', {}, parent=self.parent_header)
62 62
63 63 def write_output_prompt(self):
64 64 """Write the output prompt."""
65 65 if self.do_full_cache:
66 66 self.msg['content']['execution_count'] = self.prompt_count
67 67
68 68 def write_result_repr(self, result_repr, extra_formats):
69 69 self.msg['content']['data'] = result_repr
70 70 self.msg['content']['extra_formats'] = extra_formats
71 71
72 72 def finish_displayhook(self):
73 73 """Finish up all displayhook activities."""
74 self.pub_socket.send_json(self.msg)
74 self.session.send(self.pub_socket, self.msg)
75 75 self.msg = None
76 76
77 77
78 78 class ZMQInteractiveShell(InteractiveShell):
79 79 """A subclass of InteractiveShell for ZMQ."""
80 80
81 81 displayhook_class = Type(ZMQDisplayHook)
82 82 keepkernel_on_exit = None
83 83
84 84 def init_environment(self):
85 85 """Configure the user's environment.
86 86
87 87 """
88 88 env = os.environ
89 89 # These two ensure 'ls' produces nice coloring on BSD-derived systems
90 90 env['TERM'] = 'xterm-color'
91 91 env['CLICOLOR'] = '1'
92 92 # Since normal pagers don't work at all (over pexpect we don't have
93 93 # single-key control of the subprocess), try to disable paging in
94 94 # subprocesses as much as possible.
95 95 env['PAGER'] = 'cat'
96 96 env['GIT_PAGER'] = 'cat'
97 97
98 98 def auto_rewrite_input(self, cmd):
99 99 """Called to show the auto-rewritten input for autocall and friends.
100 100
101 101 FIXME: this payload is currently not correctly processed by the
102 102 frontend.
103 103 """
104 104 new = self.displayhook.prompt1.auto_rewrite() + cmd
105 105 payload = dict(
106 106 source='IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input',
107 107 transformed_input=new,
108 108 )
109 109 self.payload_manager.write_payload(payload)
110 110
111 111 def ask_exit(self):
112 112 """Engage the exit actions."""
113 113 payload = dict(
114 114 source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit',
115 115 exit=True,
116 116 keepkernel=self.keepkernel_on_exit,
117 117 )
118 118 self.payload_manager.write_payload(payload)
119 119
120 120 def _showtraceback(self, etype, evalue, stb):
121 121
122 122 exc_content = {
123 123 u'traceback' : stb,
124 124 u'ename' : unicode(etype.__name__),
125 125 u'evalue' : unicode(evalue)
126 126 }
127 127
128 128 dh = self.displayhook
129 exc_msg = dh.session.msg(u'pyerr', exc_content, dh.parent_header)
130 129 # Send exception info over pub socket for other clients than the caller
131 130 # to pick up
132 dh.pub_socket.send_json(exc_msg)
131 exc_msg = dh.session.send(dh.pub_socket, u'pyerr', exc_content, dh.parent_header)
133 132
134 133 # FIXME - Hack: store exception info in shell object. Right now, the
135 134 # caller is reading this info after the fact, we need to fix this logic
136 135 # to remove this hack. Even uglier, we need to store the error status
137 136 # here, because in the main loop, the logic that sets it is being
138 137 # skipped because runlines swallows the exceptions.
139 138 exc_content[u'status'] = u'error'
140 139 self._reply_content = exc_content
141 140 # /FIXME
142 141
143 142 return exc_content
144 143
145 144 #------------------------------------------------------------------------
146 145 # Magic overrides
147 146 #------------------------------------------------------------------------
148 147 # Once the base class stops inheriting from magic, this code needs to be
149 148 # moved into a separate machinery as well. For now, at least isolate here
150 149 # the magics which this class needs to implement differently from the base
151 150 # class, or that are unique to it.
152 151
153 152 def magic_doctest_mode(self,parameter_s=''):
154 153 """Toggle doctest mode on and off.
155 154
156 155 This mode is intended to make IPython behave as much as possible like a
157 156 plain Python shell, from the perspective of how its prompts, exceptions
158 157 and output look. This makes it easy to copy and paste parts of a
159 158 session into doctests. It does so by:
160 159
161 160 - Changing the prompts to the classic ``>>>`` ones.
162 161 - Changing the exception reporting mode to 'Plain'.
163 162 - Disabling pretty-printing of output.
164 163
165 164 Note that IPython also supports the pasting of code snippets that have
166 165 leading '>>>' and '...' prompts in them. This means that you can paste
167 166 doctests from files or docstrings (even if they have leading
168 167 whitespace), and the code will execute correctly. You can then use
169 168 '%history -t' to see the translated history; this will give you the
170 169 input after removal of all the leading prompts and whitespace, which
171 170 can be pasted back into an editor.
172 171
173 172 With these features, you can switch into this mode easily whenever you
174 173 need to do testing and changes to doctests, without having to leave
175 174 your existing IPython session.
176 175 """
177 176
178 177 from IPython.utils.ipstruct import Struct
179 178
180 179 # Shorthands
181 180 shell = self.shell
182 181 # dstore is a data store kept in the instance metadata bag to track any
183 182 # changes we make, so we can undo them later.
184 183 dstore = shell.meta.setdefault('doctest_mode', Struct())
185 184 save_dstore = dstore.setdefault
186 185
187 186 # save a few values we'll need to recover later
188 187 mode = save_dstore('mode', False)
189 188 save_dstore('rc_pprint', shell.pprint)
190 189 save_dstore('xmode', shell.InteractiveTB.mode)
191 190
192 191 if mode == False:
193 192 # turn on
194 193 shell.pprint = False
195 194 shell.magic_xmode('Plain')
196 195 else:
197 196 # turn off
198 197 shell.pprint = dstore.rc_pprint
199 198 shell.magic_xmode(dstore.xmode)
200 199
201 200 # Store new mode and inform on console
202 201 dstore.mode = bool(1-int(mode))
203 202 mode_label = ['OFF','ON'][dstore.mode]
204 203 print('Doctest mode is:', mode_label)
205 204
206 205 # Send the payload back so that clients can modify their prompt display
207 206 payload = dict(
208 207 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode',
209 208 mode=dstore.mode)
210 209 self.payload_manager.write_payload(payload)
211 210
212 211 def magic_edit(self,parameter_s='',last_call=['','']):
213 212 """Bring up an editor and execute the resulting code.
214 213
215 214 Usage:
216 215 %edit [options] [args]
217 216
218 217 %edit runs IPython's editor hook. The default version of this hook is
219 218 set to call the __IPYTHON__.rc.editor command. This is read from your
220 219 environment variable $EDITOR. If this isn't found, it will default to
221 220 vi under Linux/Unix and to notepad under Windows. See the end of this
222 221 docstring for how to change the editor hook.
223 222
224 223 You can also set the value of this editor via the command line option
225 224 '-editor' or in your ipythonrc file. This is useful if you wish to use
226 225 specifically for IPython an editor different from your typical default
227 226 (and for Windows users who typically don't set environment variables).
228 227
229 228 This command allows you to conveniently edit multi-line code right in
230 229 your IPython session.
231 230
232 231 If called without arguments, %edit opens up an empty editor with a
233 232 temporary file and will execute the contents of this file when you
234 233 close it (don't forget to save it!).
235 234
236 235
237 236 Options:
238 237
239 238 -n <number>: open the editor at a specified line number. By default,
240 239 the IPython editor hook uses the unix syntax 'editor +N filename', but
241 240 you can configure this by providing your own modified hook if your
242 241 favorite editor supports line-number specifications with a different
243 242 syntax.
244 243
245 244 -p: this will call the editor with the same data as the previous time
246 245 it was used, regardless of how long ago (in your current session) it
247 246 was.
248 247
249 248 -r: use 'raw' input. This option only applies to input taken from the
250 249 user's history. By default, the 'processed' history is used, so that
251 250 magics are loaded in their transformed version to valid Python. If
252 251 this option is given, the raw input as typed as the command line is
253 252 used instead. When you exit the editor, it will be executed by
254 253 IPython's own processor.
255 254
256 255 -x: do not execute the edited code immediately upon exit. This is
257 256 mainly useful if you are editing programs which need to be called with
258 257 command line arguments, which you can then do using %run.
259 258
260 259
261 260 Arguments:
262 261
263 262 If arguments are given, the following possibilites exist:
264 263
265 264 - The arguments are numbers or pairs of colon-separated numbers (like
266 265 1 4:8 9). These are interpreted as lines of previous input to be
267 266 loaded into the editor. The syntax is the same of the %macro command.
268 267
269 268 - If the argument doesn't start with a number, it is evaluated as a
270 269 variable and its contents loaded into the editor. You can thus edit
271 270 any string which contains python code (including the result of
272 271 previous edits).
273 272
274 273 - If the argument is the name of an object (other than a string),
275 274 IPython will try to locate the file where it was defined and open the
276 275 editor at the point where it is defined. You can use `%edit function`
277 276 to load an editor exactly at the point where 'function' is defined,
278 277 edit it and have the file be executed automatically.
279 278
280 279 If the object is a macro (see %macro for details), this opens up your
281 280 specified editor with a temporary file containing the macro's data.
282 281 Upon exit, the macro is reloaded with the contents of the file.
283 282
284 283 Note: opening at an exact line is only supported under Unix, and some
285 284 editors (like kedit and gedit up to Gnome 2.8) do not understand the
286 285 '+NUMBER' parameter necessary for this feature. Good editors like
287 286 (X)Emacs, vi, jed, pico and joe all do.
288 287
289 288 - If the argument is not found as a variable, IPython will look for a
290 289 file with that name (adding .py if necessary) and load it into the
291 290 editor. It will execute its contents with execfile() when you exit,
292 291 loading any code in the file into your interactive namespace.
293 292
294 293 After executing your code, %edit will return as output the code you
295 294 typed in the editor (except when it was an existing file). This way
296 295 you can reload the code in further invocations of %edit as a variable,
297 296 via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of
298 297 the output.
299 298
300 299 Note that %edit is also available through the alias %ed.
301 300
302 301 This is an example of creating a simple function inside the editor and
303 302 then modifying it. First, start up the editor:
304 303
305 304 In [1]: ed
306 305 Editing... done. Executing edited code...
307 306 Out[1]: 'def foo():n print "foo() was defined in an editing session"n'
308 307
309 308 We can then call the function foo():
310 309
311 310 In [2]: foo()
312 311 foo() was defined in an editing session
313 312
314 313 Now we edit foo. IPython automatically loads the editor with the
315 314 (temporary) file where foo() was previously defined:
316 315
317 316 In [3]: ed foo
318 317 Editing... done. Executing edited code...
319 318
320 319 And if we call foo() again we get the modified version:
321 320
322 321 In [4]: foo()
323 322 foo() has now been changed!
324 323
325 324 Here is an example of how to edit a code snippet successive
326 325 times. First we call the editor:
327 326
328 327 In [5]: ed
329 328 Editing... done. Executing edited code...
330 329 hello
331 330 Out[5]: "print 'hello'n"
332 331
333 332 Now we call it again with the previous output (stored in _):
334 333
335 334 In [6]: ed _
336 335 Editing... done. Executing edited code...
337 336 hello world
338 337 Out[6]: "print 'hello world'n"
339 338
340 339 Now we call it with the output #8 (stored in _8, also as Out[8]):
341 340
342 341 In [7]: ed _8
343 342 Editing... done. Executing edited code...
344 343 hello again
345 344 Out[7]: "print 'hello again'n"
346 345
347 346
348 347 Changing the default editor hook:
349 348
350 349 If you wish to write your own editor hook, you can put it in a
351 350 configuration file which you load at startup time. The default hook
352 351 is defined in the IPython.core.hooks module, and you can use that as a
353 352 starting example for further modifications. That file also has
354 353 general instructions on how to set a new hook for use once you've
355 354 defined it."""
356 355
357 356 # FIXME: This function has become a convoluted mess. It needs a
358 357 # ground-up rewrite with clean, simple logic.
359 358
360 359 def make_filename(arg):
361 360 "Make a filename from the given args"
362 361 try:
363 362 filename = get_py_filename(arg)
364 363 except IOError:
365 364 if args.endswith('.py'):
366 365 filename = arg
367 366 else:
368 367 filename = None
369 368 return filename
370 369
371 370 # custom exceptions
372 371 class DataIsObject(Exception): pass
373 372
374 373 opts,args = self.parse_options(parameter_s,'prn:')
375 374 # Set a few locals from the options for convenience:
376 375 opts_p = opts.has_key('p')
377 376 opts_r = opts.has_key('r')
378 377
379 378 # Default line number value
380 379 lineno = opts.get('n',None)
381 380 if lineno is not None:
382 381 try:
383 382 lineno = int(lineno)
384 383 except:
385 384 warn("The -n argument must be an integer.")
386 385 return
387 386
388 387 if opts_p:
389 388 args = '_%s' % last_call[0]
390 389 if not self.shell.user_ns.has_key(args):
391 390 args = last_call[1]
392 391
393 392 # use last_call to remember the state of the previous call, but don't
394 393 # let it be clobbered by successive '-p' calls.
395 394 try:
396 395 last_call[0] = self.shell.displayhook.prompt_count
397 396 if not opts_p:
398 397 last_call[1] = parameter_s
399 398 except:
400 399 pass
401 400
402 401 # by default this is done with temp files, except when the given
403 402 # arg is a filename
404 403 use_temp = 1
405 404
406 405 if re.match(r'\d',args):
407 406 # Mode where user specifies ranges of lines, like in %macro.
408 407 # This means that you can't edit files whose names begin with
409 408 # numbers this way. Tough.
410 409 ranges = args.split()
411 410 data = ''.join(self.extract_input_slices(ranges,opts_r))
412 411 elif args.endswith('.py'):
413 412 filename = make_filename(args)
414 413 data = ''
415 414 use_temp = 0
416 415 elif args:
417 416 try:
418 417 # Load the parameter given as a variable. If not a string,
419 418 # process it as an object instead (below)
420 419
421 420 #print '*** args',args,'type',type(args) # dbg
422 421 data = eval(args,self.shell.user_ns)
423 422 if not type(data) in StringTypes:
424 423 raise DataIsObject
425 424
426 425 except (NameError,SyntaxError):
427 426 # given argument is not a variable, try as a filename
428 427 filename = make_filename(args)
429 428 if filename is None:
430 429 warn("Argument given (%s) can't be found as a variable "
431 430 "or as a filename." % args)
432 431 return
433 432
434 433 data = ''
435 434 use_temp = 0
436 435 except DataIsObject:
437 436
438 437 # macros have a special edit function
439 438 if isinstance(data,Macro):
440 439 self._edit_macro(args,data)
441 440 return
442 441
443 442 # For objects, try to edit the file where they are defined
444 443 try:
445 444 filename = inspect.getabsfile(data)
446 445 if 'fakemodule' in filename.lower() and inspect.isclass(data):
447 446 # class created by %edit? Try to find source
448 447 # by looking for method definitions instead, the
449 448 # __module__ in those classes is FakeModule.
450 449 attrs = [getattr(data, aname) for aname in dir(data)]
451 450 for attr in attrs:
452 451 if not inspect.ismethod(attr):
453 452 continue
454 453 filename = inspect.getabsfile(attr)
455 454 if filename and 'fakemodule' not in filename.lower():
456 455 # change the attribute to be the edit target instead
457 456 data = attr
458 457 break
459 458
460 459 datafile = 1
461 460 except TypeError:
462 461 filename = make_filename(args)
463 462 datafile = 1
464 463 warn('Could not find file where `%s` is defined.\n'
465 464 'Opening a file named `%s`' % (args,filename))
466 465 # Now, make sure we can actually read the source (if it was in
467 466 # a temp file it's gone by now).
468 467 if datafile:
469 468 try:
470 469 if lineno is None:
471 470 lineno = inspect.getsourcelines(data)[1]
472 471 except IOError:
473 472 filename = make_filename(args)
474 473 if filename is None:
475 474 warn('The file `%s` where `%s` was defined cannot '
476 475 'be read.' % (filename,data))
477 476 return
478 477 use_temp = 0
479 478 else:
480 479 data = ''
481 480
482 481 if use_temp:
483 482 filename = self.shell.mktempfile(data)
484 483 print('IPython will make a temporary file named:', filename)
485 484
486 485 # Make sure we send to the client an absolute path, in case the working
487 486 # directory of client and kernel don't match
488 487 filename = os.path.abspath(filename)
489 488
490 489 payload = {
491 490 'source' : 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic',
492 491 'filename' : filename,
493 492 'line_number' : lineno
494 493 }
495 494 self.payload_manager.write_payload(payload)
496 495
497 496 def magic_gui(self, *args, **kwargs):
498 497 raise NotImplementedError(
499 498 'GUI support must be enabled in command line options.')
500 499
501 500 def magic_pylab(self, *args, **kwargs):
502 501 raise NotImplementedError(
503 502 'pylab support must be enabled in command line options.')
504 503
505 504 # A few magics that are adapted to the specifics of using pexpect and a
506 505 # remote terminal
507 506
508 507 def magic_clear(self, arg_s):
509 508 """Clear the terminal."""
510 509 if os.name == 'posix':
511 510 self.shell.system("clear")
512 511 else:
513 512 self.shell.system("cls")
514 513
515 514 if os.name == 'nt':
516 515 # This is the usual name in windows
517 516 magic_cls = magic_clear
518 517
519 518 # Terminal pagers won't work over pexpect, but we do have our own pager
520 519
521 520 def magic_less(self, arg_s):
522 521 """Show a file through the pager.
523 522
524 523 Files ending in .py are syntax-highlighted."""
525 524 cont = open(arg_s).read()
526 525 if arg_s.endswith('.py'):
527 526 cont = self.shell.pycolorize(cont)
528 527 page.page(cont)
529 528
530 529 magic_more = magic_less
531 530
532 531 # Man calls a pager, so we also need to redefine it
533 532 if os.name == 'posix':
534 533 def magic_man(self, arg_s):
535 534 """Find the man page for the given command and display in pager."""
536 535 page.page(self.shell.getoutput('man %s | col -b' % arg_s,
537 536 split=False))
538 537
539 538 # FIXME: this is specific to the GUI, so we should let the gui app load
540 539 # magics at startup that are only for the gui. Once the gui app has proper
541 540 # profile and configuration management, we can have it initialize a kernel
542 541 # with a special config file that provides these.
543 542 def magic_guiref(self, arg_s):
544 543 """Show a basic reference about the GUI console."""
545 544 from IPython.core.usage import gui_reference
546 545 page.page(gui_reference, auto_html=True)
547 546
548 547 def magic_loadpy(self, arg_s):
549 548 """Load a .py python script into the GUI console.
550 549
551 550 This magic command can either take a local filename or a url::
552 551
553 552 %loadpy myscript.py
554 553 %loadpy http://www.example.com/myscript.py
555 554 """
556 555 if not arg_s.endswith('.py'):
557 556 raise ValueError('%%load only works with .py files: %s' % arg_s)
558 557 if arg_s.startswith('http'):
559 558 import urllib2
560 559 response = urllib2.urlopen(arg_s)
561 560 content = response.read()
562 561 else:
563 562 content = open(arg_s).read()
564 563 payload = dict(
565 564 source='IPython.zmq.zmqshell.ZMQInteractiveShell.magic_loadpy',
566 565 text=content
567 566 )
568 567 self.payload_manager.write_payload(payload)
569 568
570 569 def magic_Exit(self, parameter_s=''):
571 570 """Exit IPython. If the -k option is provided, the kernel will be left
572 571 running. Otherwise, it will shutdown without prompting.
573 572 """
574 573 opts,args = self.parse_options(parameter_s,'k')
575 574 self.shell.keepkernel_on_exit = opts.has_key('k')
576 575 self.shell.ask_exit()
577 576
578 577 # Add aliases as magics so all common forms work: exit, quit, Exit, Quit.
579 578 magic_exit = magic_quit = magic_Quit = magic_Exit
580 579
581 580 InteractiveShellABC.register(ZMQInteractiveShell)
General Comments 0
You need to be logged in to leave comments. Login now