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