##// END OF EJS Templates
zmq kernels now started via newapp
MinRK -
Show More
@@ -0,0 +1,213 b''
1 #!/usr/bin/env python
2 """An Application for launching a kernel
3
4 Authors
5 -------
6 * MinRK
7 """
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING.txt, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
19 # Standard library imports.
20 import os
21 import sys
22
23 # System library imports.
24 import zmq
25
26 # IPython imports.
27 from IPython.core.ultratb import FormattedTB
28 from IPython.core.newapplication import (
29 BaseIPythonApplication, base_flags, base_aliases
30 )
31 from IPython.utils import io
32 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.traitlets import Any, Instance, Dict, Unicode, Int, Bool
34 from IPython.utils.importstring import import_item
35 # local imports
36 from IPython.zmq.heartbeat import Heartbeat
37 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
38 from IPython.zmq.session import Session
39
40
41 #-----------------------------------------------------------------------------
42 # Flags and Aliases
43 #-----------------------------------------------------------------------------
44
45 kernel_aliases = dict(base_aliases)
46 kernel_aliases.update({
47 'ip' : 'KernelApp.ip',
48 'hb' : 'KernelApp.hb_port',
49 'shell' : 'KernelApp.shell_port',
50 'iopub' : 'KernelApp.iopub_port',
51 'stdin' : 'KernelApp.stdin_port',
52 'parent': 'KernelApp.parent',
53 })
54 if sys.platform.startswith('win'):
55 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
56
57 kernel_flags = dict(base_flags)
58 kernel_flags.update({
59 'no-stdout' : (
60 {'KernelApp' : {'no_stdout' : True}},
61 "redirect stdout to the null device"),
62 'no-stderr' : (
63 {'KernelApp' : {'no_stderr' : True}},
64 "redirect stderr to the null device"),
65 })
66
67
68 #-----------------------------------------------------------------------------
69 # Application class for starting a Kernel
70 #-----------------------------------------------------------------------------
71
72 class KernelApp(BaseIPythonApplication):
73 name='pykernel'
74 aliases = Dict(kernel_aliases)
75 flags = Dict(kernel_flags)
76
77 # the kernel class, as an importstring
78 kernel_class = Unicode('IPython.zmq.pykernel.Kernel')
79 kernel = Any()
80 poller = Any() # don't restrict this even though current pollers are all Threads
81 heartbeat = Instance(Heartbeat)
82 session = Instance('IPython.zmq.session.Session')
83 ports = Dict()
84
85 # connection info:
86 ip = Unicode(LOCALHOST, config=True,
87 help="Set the IP or interface on which the kernel will listen.")
88 hb_port = Int(0, config=True, help="set the heartbeat port [default: random]")
89 shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]")
90 iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]")
91 stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]")
92
93 # streams, etc.
94 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
95 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
96 outstream_class = Unicode('IPython.zmq.iostream.OutStream', config=True,
97 help="The importstring for the OutStream factory")
98 displayhook_class = Unicode('IPython.zmq.displayhook.DisplayHook', config=True,
99 help="The importstring for the DisplayHook factory")
100
101 # polling
102 parent = Int(0, config=True,
103 help="""kill this process if its parent dies. On Windows, the argument
104 specifies the HANDLE of the parent process, otherwise it is simply boolean.
105 """)
106 interrupt = Int(0, config=True,
107 help="""ONLY USED ON WINDOWS
108 Interrupt this process when the parent is signalled.
109 """)
110
111 def init_crash_handler(self):
112 # Install minimal exception handling
113 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
114 ostream=sys.__stdout__)
115
116 def init_poller(self):
117 if sys.platform == 'win32':
118 if self.interrupt or self.parent:
119 self.poller = ParentPollerWindows(self.interrupt, self.parent)
120 elif self.parent:
121 self.poller = ParentPollerUnix()
122
123 def _bind_socket(self, s, port):
124 iface = 'tcp://%s' % self.ip
125 if port <= 0:
126 port = s.bind_to_random_port(iface)
127 else:
128 s.bind(iface + ':%i'%port)
129 return port
130
131 def init_sockets(self):
132 # Create a context, a session, and the kernel sockets.
133 io.raw_print("Starting the kernel at pid:", os.getpid())
134 context = zmq.Context.instance()
135 # Uncomment this to try closing the context.
136 # atexit.register(context.term)
137
138 self.shell_socket = context.socket(zmq.XREP)
139 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
140 self.log.debug("shell XREP Channel on port: %i"%self.shell_port)
141
142 self.iopub_socket = context.socket(zmq.PUB)
143 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
144 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
145
146 self.stdin_socket = context.socket(zmq.XREQ)
147 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
148 self.log.debug("stdin XREQ Channel on port: %i"%self.stdin_port)
149
150 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
151 self.hb_port = self.heartbeat.port
152 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
153
154 # Helper to make it easier to connect to an existing kernel, until we have
155 # single-port connection negotiation fully implemented.
156 self.log.info("To connect another client to this kernel, use:")
157 self.log.info("--external shell={0} iopub={1} stdin={2} hb={3}".format(
158 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port))
159
160
161 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
162 stdin=self.stdin_port, hb=self.hb_port)
163
164 def init_session(self):
165 """create our session object"""
166 self.session = Session(username=u'kernel')
167
168 def init_io(self):
169 """redirects stdout/stderr, and installs a display hook"""
170 # Re-direct stdout/stderr, if necessary.
171 if self.no_stdout or self.no_stderr:
172 blackhole = file(os.devnull, 'w')
173 if self.no_stdout:
174 sys.stdout = sys.__stdout__ = blackhole
175 if self.no_stderr:
176 sys.stderr = sys.__stderr__ = blackhole
177
178 # Redirect input streams and set a display hook.
179
180 if self.outstream_class:
181 outstream_factory = import_item(str(self.outstream_class))
182 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
183 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
184 if self.displayhook_class:
185 displayhook_factory = import_item(str(self.displayhook_class))
186 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
187
188 def init_kernel(self):
189 """Create the Kernel object itself"""
190 kernel_factory = import_item(str(self.kernel_class))
191 self.kernel = kernel_factory(config=self.config, session=self.session,
192 shell_socket=self.shell_socket,
193 iopub_socket=self.iopub_socket,
194 stdin_socket=self.stdin_socket,
195 )
196 self.kernel.record_ports(self.ports)
197
198 def initialize(self, argv=None):
199 super(KernelApp, self).initialize(argv)
200 self.init_session()
201 self.init_poller()
202 self.init_sockets()
203 self.init_io()
204 self.init_kernel()
205
206 def start(self):
207 self.heartbeat.start()
208 if self.poller is not None:
209 self.poller.start()
210 try:
211 self.kernel.start()
212 except KeyboardInterrupt:
213 pass
@@ -0,0 +1,13 b''
1 =======================
2 Log Topic Specification
3 =======================
4
5 we use pyzmq to broadcast log events over a PUB socket. Engines, Controllers, etc. can all
6 broadcast. SUB sockets can be used to view the logs, and ZMQ topics are used to help
7 select out what to follow.
8
9 the PUBHandler object that emits the logs can ascribe topics to log messages. The order is:
10
11 <root_topic>.<loglevel>.<subtopic>[.<etc>]
12
13 root_topic is specified as an attribute
@@ -0,0 +1,111 b''
1 """test building messages with streamsession"""
2
3 #-------------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
5 #
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING, distributed as part of this software.
8 #-------------------------------------------------------------------------------
9
10 #-------------------------------------------------------------------------------
11 # Imports
12 #-------------------------------------------------------------------------------
13
14 import os
15 import uuid
16 import zmq
17
18 from zmq.tests import BaseZMQTestCase
19 from zmq.eventloop.zmqstream import ZMQStream
20 # from IPython.zmq.tests import SessionTestCase
21 from IPython.parallel import streamsession as ss
22
23 class SessionTestCase(BaseZMQTestCase):
24
25 def setUp(self):
26 BaseZMQTestCase.setUp(self)
27 self.session = ss.StreamSession()
28
29 class TestSession(SessionTestCase):
30
31 def test_msg(self):
32 """message format"""
33 msg = self.session.msg('execute')
34 thekeys = set('header msg_id parent_header msg_type content'.split())
35 s = set(msg.keys())
36 self.assertEquals(s, thekeys)
37 self.assertTrue(isinstance(msg['content'],dict))
38 self.assertTrue(isinstance(msg['header'],dict))
39 self.assertTrue(isinstance(msg['parent_header'],dict))
40 self.assertEquals(msg['msg_type'], 'execute')
41
42
43
44 def test_args(self):
45 """initialization arguments for StreamSession"""
46 s = self.session
47 self.assertTrue(s.pack is ss.default_packer)
48 self.assertTrue(s.unpack is ss.default_unpacker)
49 self.assertEquals(s.username, os.environ.get('USER', 'username'))
50
51 s = ss.StreamSession()
52 self.assertEquals(s.username, os.environ.get('USER', 'username'))
53
54 self.assertRaises(TypeError, ss.StreamSession, pack='hi')
55 self.assertRaises(TypeError, ss.StreamSession, unpack='hi')
56 u = str(uuid.uuid4())
57 s = ss.StreamSession(username='carrot', session=u)
58 self.assertEquals(s.session, u)
59 self.assertEquals(s.username, 'carrot')
60
61 def test_tracking(self):
62 """test tracking messages"""
63 a,b = self.create_bound_pair(zmq.PAIR, zmq.PAIR)
64 s = self.session
65 stream = ZMQStream(a)
66 msg = s.send(a, 'hello', track=False)
67 self.assertTrue(msg['tracker'] is None)
68 msg = s.send(a, 'hello', track=True)
69 self.assertTrue(isinstance(msg['tracker'], zmq.MessageTracker))
70 M = zmq.Message(b'hi there', track=True)
71 msg = s.send(a, 'hello', buffers=[M], track=True)
72 t = msg['tracker']
73 self.assertTrue(isinstance(t, zmq.MessageTracker))
74 self.assertRaises(zmq.NotDone, t.wait, .1)
75 del M
76 t.wait(1) # this will raise
77
78
79 # def test_rekey(self):
80 # """rekeying dict around json str keys"""
81 # d = {'0': uuid.uuid4(), 0:uuid.uuid4()}
82 # self.assertRaises(KeyError, ss.rekey, d)
83 #
84 # d = {'0': uuid.uuid4(), 1:uuid.uuid4(), 'asdf':uuid.uuid4()}
85 # d2 = {0:d['0'],1:d[1],'asdf':d['asdf']}
86 # rd = ss.rekey(d)
87 # self.assertEquals(d2,rd)
88 #
89 # d = {'1.5':uuid.uuid4(),'1':uuid.uuid4()}
90 # d2 = {1.5:d['1.5'],1:d['1']}
91 # rd = ss.rekey(d)
92 # self.assertEquals(d2,rd)
93 #
94 # d = {'1.0':uuid.uuid4(),'1':uuid.uuid4()}
95 # self.assertRaises(KeyError, ss.rekey, d)
96 #
97 def test_unique_msg_ids(self):
98 """test that messages receive unique ids"""
99 ids = set()
100 for i in range(2**12):
101 h = self.session.msg_header('test')
102 msg_id = h['msg_id']
103 self.assertTrue(msg_id not in ids)
104 ids.add(msg_id)
105
106 def test_feed_identities(self):
107 """scrub the front for zmq IDENTITIES"""
108 theids = "engine client other".split()
109 content = dict(code='whoda',stuff=object())
110 themsg = self.session.msg('execute',content=content)
111 pmsg = theids
@@ -1,277 +1,280 b''
1 """ A minimal application using the Qt console-style IPython frontend.
1 """ A minimal application using the Qt console-style IPython frontend.
2 """
2 """
3
3
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Imports
5 # Imports
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7
7
8 # Systemm library imports
8 # Systemm library imports
9 from IPython.external.qt import QtGui
9 from IPython.external.qt import QtGui
10 from pygments.styles import get_all_styles
10 from pygments.styles import get_all_styles
11
11
12 # Local imports
12 # Local imports
13 from IPython.external.argparse import ArgumentParser
13 from IPython.external.argparse import ArgumentParser
14 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
14 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
15 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
15 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
16 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
16 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
17 from IPython.frontend.qt.console import styles
17 from IPython.frontend.qt.console import styles
18 from IPython.frontend.qt.kernelmanager import QtKernelManager
18 from IPython.frontend.qt.kernelmanager import QtKernelManager
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Network Constants
21 # Network Constants
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
24 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Classes
27 # Classes
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 class MainWindow(QtGui.QMainWindow):
30 class MainWindow(QtGui.QMainWindow):
31
31
32 #---------------------------------------------------------------------------
32 #---------------------------------------------------------------------------
33 # 'object' interface
33 # 'object' interface
34 #---------------------------------------------------------------------------
34 #---------------------------------------------------------------------------
35
35
36 def __init__(self, app, frontend, existing=False, may_close=True):
36 def __init__(self, app, frontend, existing=False, may_close=True):
37 """ Create a MainWindow for the specified FrontendWidget.
37 """ Create a MainWindow for the specified FrontendWidget.
38
38
39 The app is passed as an argument to allow for different
39 The app is passed as an argument to allow for different
40 closing behavior depending on whether we are the Kernel's parent.
40 closing behavior depending on whether we are the Kernel's parent.
41
41
42 If existing is True, then this Console does not own the Kernel.
42 If existing is True, then this Console does not own the Kernel.
43
43
44 If may_close is True, then this Console is permitted to close the kernel
44 If may_close is True, then this Console is permitted to close the kernel
45 """
45 """
46 super(MainWindow, self).__init__()
46 super(MainWindow, self).__init__()
47 self._app = app
47 self._app = app
48 self._frontend = frontend
48 self._frontend = frontend
49 self._existing = existing
49 self._existing = existing
50 if existing:
50 if existing:
51 self._may_close = may_close
51 self._may_close = may_close
52 else:
52 else:
53 self._may_close = True
53 self._may_close = True
54 self._frontend.exit_requested.connect(self.close)
54 self._frontend.exit_requested.connect(self.close)
55 self.setCentralWidget(frontend)
55 self.setCentralWidget(frontend)
56
56
57 #---------------------------------------------------------------------------
57 #---------------------------------------------------------------------------
58 # QWidget interface
58 # QWidget interface
59 #---------------------------------------------------------------------------
59 #---------------------------------------------------------------------------
60
60
61 def closeEvent(self, event):
61 def closeEvent(self, event):
62 """ Close the window and the kernel (if necessary).
62 """ Close the window and the kernel (if necessary).
63
63
64 This will prompt the user if they are finished with the kernel, and if
64 This will prompt the user if they are finished with the kernel, and if
65 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
65 so, closes the kernel cleanly. Alternatively, if the exit magic is used,
66 it closes without prompt.
66 it closes without prompt.
67 """
67 """
68 keepkernel = None #Use the prompt by default
68 keepkernel = None #Use the prompt by default
69 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
69 if hasattr(self._frontend,'_keep_kernel_on_exit'): #set by exit magic
70 keepkernel = self._frontend._keep_kernel_on_exit
70 keepkernel = self._frontend._keep_kernel_on_exit
71
71
72 kernel_manager = self._frontend.kernel_manager
72 kernel_manager = self._frontend.kernel_manager
73
73
74 if keepkernel is None: #show prompt
74 if keepkernel is None: #show prompt
75 if kernel_manager and kernel_manager.channels_running:
75 if kernel_manager and kernel_manager.channels_running:
76 title = self.window().windowTitle()
76 title = self.window().windowTitle()
77 cancel = QtGui.QMessageBox.Cancel
77 cancel = QtGui.QMessageBox.Cancel
78 okay = QtGui.QMessageBox.Ok
78 okay = QtGui.QMessageBox.Ok
79 if self._may_close:
79 if self._may_close:
80 msg = "You are closing this Console window."
80 msg = "You are closing this Console window."
81 info = "Would you like to quit the Kernel and all attached Consoles as well?"
81 info = "Would you like to quit the Kernel and all attached Consoles as well?"
82 justthis = QtGui.QPushButton("&No, just this Console", self)
82 justthis = QtGui.QPushButton("&No, just this Console", self)
83 justthis.setShortcut('N')
83 justthis.setShortcut('N')
84 closeall = QtGui.QPushButton("&Yes, quit everything", self)
84 closeall = QtGui.QPushButton("&Yes, quit everything", self)
85 closeall.setShortcut('Y')
85 closeall.setShortcut('Y')
86 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
86 box = QtGui.QMessageBox(QtGui.QMessageBox.Question,
87 title, msg)
87 title, msg)
88 box.setInformativeText(info)
88 box.setInformativeText(info)
89 box.addButton(cancel)
89 box.addButton(cancel)
90 box.addButton(justthis, QtGui.QMessageBox.NoRole)
90 box.addButton(justthis, QtGui.QMessageBox.NoRole)
91 box.addButton(closeall, QtGui.QMessageBox.YesRole)
91 box.addButton(closeall, QtGui.QMessageBox.YesRole)
92 box.setDefaultButton(closeall)
92 box.setDefaultButton(closeall)
93 box.setEscapeButton(cancel)
93 box.setEscapeButton(cancel)
94 reply = box.exec_()
94 reply = box.exec_()
95 if reply == 1: # close All
95 if reply == 1: # close All
96 kernel_manager.shutdown_kernel()
96 kernel_manager.shutdown_kernel()
97 #kernel_manager.stop_channels()
97 #kernel_manager.stop_channels()
98 event.accept()
98 event.accept()
99 elif reply == 0: # close Console
99 elif reply == 0: # close Console
100 if not self._existing:
100 if not self._existing:
101 # Have kernel: don't quit, just close the window
101 # Have kernel: don't quit, just close the window
102 self._app.setQuitOnLastWindowClosed(False)
102 self._app.setQuitOnLastWindowClosed(False)
103 self.deleteLater()
103 self.deleteLater()
104 event.accept()
104 event.accept()
105 else:
105 else:
106 event.ignore()
106 event.ignore()
107 else:
107 else:
108 reply = QtGui.QMessageBox.question(self, title,
108 reply = QtGui.QMessageBox.question(self, title,
109 "Are you sure you want to close this Console?"+
109 "Are you sure you want to close this Console?"+
110 "\nThe Kernel and other Consoles will remain active.",
110 "\nThe Kernel and other Consoles will remain active.",
111 okay|cancel,
111 okay|cancel,
112 defaultButton=okay
112 defaultButton=okay
113 )
113 )
114 if reply == okay:
114 if reply == okay:
115 event.accept()
115 event.accept()
116 else:
116 else:
117 event.ignore()
117 event.ignore()
118 elif keepkernel: #close console but leave kernel running (no prompt)
118 elif keepkernel: #close console but leave kernel running (no prompt)
119 if kernel_manager and kernel_manager.channels_running:
119 if kernel_manager and kernel_manager.channels_running:
120 if not self._existing:
120 if not self._existing:
121 # I have the kernel: don't quit, just close the window
121 # I have the kernel: don't quit, just close the window
122 self._app.setQuitOnLastWindowClosed(False)
122 self._app.setQuitOnLastWindowClosed(False)
123 event.accept()
123 event.accept()
124 else: #close console and kernel (no prompt)
124 else: #close console and kernel (no prompt)
125 if kernel_manager and kernel_manager.channels_running:
125 if kernel_manager and kernel_manager.channels_running:
126 kernel_manager.shutdown_kernel()
126 kernel_manager.shutdown_kernel()
127 event.accept()
127 event.accept()
128
128
129 #-----------------------------------------------------------------------------
129 #-----------------------------------------------------------------------------
130 # Main entry point
130 # Main entry point
131 #-----------------------------------------------------------------------------
131 #-----------------------------------------------------------------------------
132
132
133 def main():
133 def main():
134 """ Entry point for application.
134 """ Entry point for application.
135 """
135 """
136 # Parse command line arguments.
136 # Parse command line arguments.
137 parser = ArgumentParser()
137 parser = ArgumentParser()
138 kgroup = parser.add_argument_group('kernel options')
138 kgroup = parser.add_argument_group('kernel options')
139 kgroup.add_argument('-e', '--existing', action='store_true',
139 kgroup.add_argument('-e', '--existing', action='store_true',
140 help='connect to an existing kernel')
140 help='connect to an existing kernel')
141 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
141 kgroup.add_argument('--ip', type=str, default=LOCALHOST,
142 help=\
142 help=\
143 "set the kernel\'s IP address [default localhost].\
143 "set the kernel\'s IP address [default localhost].\
144 If the IP address is something other than localhost, then \
144 If the IP address is something other than localhost, then \
145 Consoles on other machines will be able to connect\
145 Consoles on other machines will be able to connect\
146 to the Kernel, so be careful!")
146 to the Kernel, so be careful!")
147 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
147 kgroup.add_argument('--xreq', type=int, metavar='PORT', default=0,
148 help='set the XREQ channel port [default random]')
148 help='set the XREQ channel port [default random]')
149 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
149 kgroup.add_argument('--sub', type=int, metavar='PORT', default=0,
150 help='set the SUB channel port [default random]')
150 help='set the SUB channel port [default random]')
151 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
151 kgroup.add_argument('--rep', type=int, metavar='PORT', default=0,
152 help='set the REP channel port [default random]')
152 help='set the REP channel port [default random]')
153 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
153 kgroup.add_argument('--hb', type=int, metavar='PORT', default=0,
154 help='set the heartbeat port [default random]')
154 help='set the heartbeat port [default random]')
155
155
156 egroup = kgroup.add_mutually_exclusive_group()
156 egroup = kgroup.add_mutually_exclusive_group()
157 egroup.add_argument('--pure', action='store_true', help = \
157 egroup.add_argument('--pure', action='store_true', help = \
158 'use a pure Python kernel instead of an IPython kernel')
158 'use a pure Python kernel instead of an IPython kernel')
159 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
159 egroup.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
160 const='auto', help = \
160 const='auto', help = \
161 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
161 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
162 given, the GUI backend is matplotlib's, otherwise use one of: \
162 given, the GUI backend is matplotlib's, otherwise use one of: \
163 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
163 ['tk', 'gtk', 'qt', 'wx', 'inline'].")
164
164
165 wgroup = parser.add_argument_group('widget options')
165 wgroup = parser.add_argument_group('widget options')
166 wgroup.add_argument('--paging', type=str, default='inside',
166 wgroup.add_argument('--paging', type=str, default='inside',
167 choices = ['inside', 'hsplit', 'vsplit', 'none'],
167 choices = ['inside', 'hsplit', 'vsplit', 'none'],
168 help='set the paging style [default inside]')
168 help='set the paging style [default inside]')
169 wgroup.add_argument('--plain', action='store_true',
169 wgroup.add_argument('--plain', action='store_true',
170 help='disable rich text support')
170 help='disable rich text support')
171 wgroup.add_argument('--gui-completion', action='store_true',
171 wgroup.add_argument('--gui-completion', action='store_true',
172 help='use a GUI widget for tab completion')
172 help='use a GUI widget for tab completion')
173 wgroup.add_argument('--style', type=str,
173 wgroup.add_argument('--style', type=str,
174 choices = list(get_all_styles()),
174 choices = list(get_all_styles()),
175 help='specify a pygments style for by name')
175 help='specify a pygments style for by name')
176 wgroup.add_argument('--stylesheet', type=str,
176 wgroup.add_argument('--stylesheet', type=str,
177 help='path to a custom CSS stylesheet')
177 help='path to a custom CSS stylesheet')
178 wgroup.add_argument('--colors', type=str, help = \
178 wgroup.add_argument('--colors', type=str, help = \
179 "Set the color scheme (LightBG,Linux,NoColor). This is guessed \
179 "Set the color scheme (LightBG,Linux,NoColor). This is guessed \
180 based on the pygments style if not set.")
180 based on the pygments style if not set.")
181
181
182 args = parser.parse_args()
182 args = parser.parse_args()
183
183
184 # parse the colors arg down to current known labels
184 # parse the colors arg down to current known labels
185 if args.colors:
185 if args.colors:
186 colors=args.colors.lower()
186 colors=args.colors.lower()
187 if colors in ('lightbg', 'light'):
187 if colors in ('lightbg', 'light'):
188 colors='lightbg'
188 colors='lightbg'
189 elif colors in ('dark', 'linux'):
189 elif colors in ('dark', 'linux'):
190 colors='linux'
190 colors='linux'
191 else:
191 else:
192 colors='nocolor'
192 colors='nocolor'
193 elif args.style:
193 elif args.style:
194 if args.style=='bw':
194 if args.style=='bw':
195 colors='nocolor'
195 colors='nocolor'
196 elif styles.dark_style(args.style):
196 elif styles.dark_style(args.style):
197 colors='linux'
197 colors='linux'
198 else:
198 else:
199 colors='lightbg'
199 colors='lightbg'
200 else:
200 else:
201 colors=None
201 colors=None
202
202
203 # Don't let Qt or ZMQ swallow KeyboardInterupts.
203 # Don't let Qt or ZMQ swallow KeyboardInterupts.
204 import signal
204 import signal
205 signal.signal(signal.SIGINT, signal.SIG_DFL)
205 signal.signal(signal.SIGINT, signal.SIG_DFL)
206
206
207 # Create a KernelManager and start a kernel.
207 # Create a KernelManager and start a kernel.
208 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
208 kernel_manager = QtKernelManager(xreq_address=(args.ip, args.xreq),
209 sub_address=(args.ip, args.sub),
209 sub_address=(args.ip, args.sub),
210 rep_address=(args.ip, args.rep),
210 rep_address=(args.ip, args.rep),
211 hb_address=(args.ip, args.hb))
211 hb_address=(args.ip, args.hb))
212 if not args.existing:
212 if not args.existing:
213 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
213 # if not args.ip in LOCAL_IPS+ALL_ALIAS:
214 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
214 # raise ValueError("Must bind a local ip, such as: %s"%LOCAL_IPS)
215
215
216 kwargs = dict(ip=args.ip)
216 kwargs = dict(ip=args.ip)
217 if args.pure:
217 if args.pure:
218 kwargs['ipython']=False
218 kwargs['ipython']=False
219 else:
219 else:
220 kwargs['colors']=colors
220 extra = []
221 if colors:
222 extra.append("colors=%s"%colors)
221 if args.pylab:
223 if args.pylab:
222 kwargs['pylab']=args.pylab
224 extra.append("pylab=%s"%args.pylab)
225 kwargs['extra_arguments'] = extra
223
226
224 kernel_manager.start_kernel(**kwargs)
227 kernel_manager.start_kernel(**kwargs)
225 kernel_manager.start_channels()
228 kernel_manager.start_channels()
226
229
227 # Create the widget.
230 # Create the widget.
228 app = QtGui.QApplication([])
231 app = QtGui.QApplication([])
229 local_kernel = (not args.existing) or args.ip in LOCAL_IPS
232 local_kernel = (not args.existing) or args.ip in LOCAL_IPS
230 if args.pure:
233 if args.pure:
231 kind = 'plain' if args.plain else 'rich'
234 kind = 'plain' if args.plain else 'rich'
232 widget = FrontendWidget(kind=kind, paging=args.paging,
235 widget = FrontendWidget(kind=kind, paging=args.paging,
233 local_kernel=local_kernel)
236 local_kernel=local_kernel)
234 elif args.plain:
237 elif args.plain:
235 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
238 widget = IPythonWidget(paging=args.paging, local_kernel=local_kernel)
236 else:
239 else:
237 widget = RichIPythonWidget(paging=args.paging,
240 widget = RichIPythonWidget(paging=args.paging,
238 local_kernel=local_kernel)
241 local_kernel=local_kernel)
239 widget.gui_completion = args.gui_completion
242 widget.gui_completion = args.gui_completion
240 widget.kernel_manager = kernel_manager
243 widget.kernel_manager = kernel_manager
241
244
242 # Configure the style.
245 # Configure the style.
243 if not args.pure: # only IPythonWidget supports styles
246 if not args.pure: # only IPythonWidget supports styles
244 if args.style:
247 if args.style:
245 widget.syntax_style = args.style
248 widget.syntax_style = args.style
246 widget.style_sheet = styles.sheet_from_template(args.style, colors)
249 widget.style_sheet = styles.sheet_from_template(args.style, colors)
247 widget._syntax_style_changed()
250 widget._syntax_style_changed()
248 widget._style_sheet_changed()
251 widget._style_sheet_changed()
249 elif colors:
252 elif colors:
250 # use a default style
253 # use a default style
251 widget.set_default_style(colors=colors)
254 widget.set_default_style(colors=colors)
252 else:
255 else:
253 # this is redundant for now, but allows the widget's
256 # this is redundant for now, but allows the widget's
254 # defaults to change
257 # defaults to change
255 widget.set_default_style()
258 widget.set_default_style()
256
259
257 if args.stylesheet:
260 if args.stylesheet:
258 # we got an expicit stylesheet
261 # we got an expicit stylesheet
259 if os.path.isfile(args.stylesheet):
262 if os.path.isfile(args.stylesheet):
260 with open(args.stylesheet) as f:
263 with open(args.stylesheet) as f:
261 sheet = f.read()
264 sheet = f.read()
262 widget.style_sheet = sheet
265 widget.style_sheet = sheet
263 widget._style_sheet_changed()
266 widget._style_sheet_changed()
264 else:
267 else:
265 raise IOError("Stylesheet %r not found."%args.stylesheet)
268 raise IOError("Stylesheet %r not found."%args.stylesheet)
266
269
267 # Create the main window.
270 # Create the main window.
268 window = MainWindow(app, widget, args.existing, may_close=local_kernel)
271 window = MainWindow(app, widget, args.existing, may_close=local_kernel)
269 window.setWindowTitle('Python' if args.pure else 'IPython')
272 window.setWindowTitle('Python' if args.pure else 'IPython')
270 window.show()
273 window.show()
271
274
272 # Start the application main loop.
275 # Start the application main loop.
273 app.exec_()
276 app.exec_()
274
277
275
278
276 if __name__ == '__main__':
279 if __name__ == '__main__':
277 main()
280 main()
@@ -1,299 +1,160 b''
1 """ Defines helper functions for creating kernel entry points and process
1 """ Defines helper functions for creating kernel entry points and process
2 launchers.
2 launchers.
3 """
3 """
4
4
5 # Standard library imports.
5 # Standard library imports.
6 import atexit
6 import atexit
7 import os
7 import os
8 import socket
8 import socket
9 from subprocess import Popen, PIPE
9 from subprocess import Popen, PIPE
10 import sys
10 import sys
11
11
12 # System library imports.
13 import zmq
14
15 # Local imports.
12 # Local imports.
16 from IPython.core.ultratb import FormattedTB
13 from parentpoller import ParentPollerWindows
17 from IPython.external.argparse import ArgumentParser
18 from IPython.utils import io
19 from IPython.utils.localinterfaces import LOCALHOST
20 from displayhook import DisplayHook
21 from heartbeat import Heartbeat
22 from iostream import OutStream
23 from parentpoller import ParentPollerUnix, ParentPollerWindows
24 from session import Session
25
26
27 def bind_port(socket, ip, port):
28 """ Binds the specified ZMQ socket. If the port is zero, a random port is
29 chosen. Returns the port that was bound.
30 """
31 connection = 'tcp://%s' % ip
32 if port <= 0:
33 port = socket.bind_to_random_port(connection)
34 else:
35 connection += ':%i' % port
36 socket.bind(connection)
37 return port
38
39
40 def make_argument_parser():
41 """ Creates an ArgumentParser for the generic arguments supported by all
42 kernel entry points.
43 """
44 parser = ArgumentParser()
45 parser.add_argument('--ip', type=str, default=LOCALHOST,
46 help='set the kernel\'s IP address [default: local]')
47 parser.add_argument('--xrep', type=int, metavar='PORT', default=0,
48 help='set the XREP channel port [default: random]')
49 parser.add_argument('--pub', type=int, metavar='PORT', default=0,
50 help='set the PUB channel port [default: random]')
51 parser.add_argument('--req', type=int, metavar='PORT', default=0,
52 help='set the REQ channel port [default: random]')
53 parser.add_argument('--hb', type=int, metavar='PORT', default=0,
54 help='set the heartbeat port [default: random]')
55 parser.add_argument('--no-stdout', action='store_true',
56 help='redirect stdout to the null device')
57 parser.add_argument('--no-stderr', action='store_true',
58 help='redirect stderr to the null device')
59
60 if sys.platform == 'win32':
61 parser.add_argument('--interrupt', type=int, metavar='HANDLE',
62 default=0, help='interrupt this process when '
63 'HANDLE is signaled')
64 parser.add_argument('--parent', type=int, metavar='HANDLE',
65 default=0, help='kill this process if the process '
66 'with HANDLE dies')
67 else:
68 parser.add_argument('--parent', action='store_true',
69 help='kill this process if its parent dies')
70
71 return parser
72
73
74 def make_kernel(namespace, kernel_factory,
75 out_stream_factory=None, display_hook_factory=None):
76 """ Creates a kernel, redirects stdout/stderr, and installs a display hook
77 and exception handler.
78 """
79 # Re-direct stdout/stderr, if necessary.
80 if namespace.no_stdout or namespace.no_stderr:
81 blackhole = file(os.devnull, 'w')
82 if namespace.no_stdout:
83 sys.stdout = sys.__stdout__ = blackhole
84 if namespace.no_stderr:
85 sys.stderr = sys.__stderr__ = blackhole
86
87 # Install minimal exception handling
88 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
89 ostream=sys.__stdout__)
90
14
91 # Create a context, a session, and the kernel sockets.
92 io.raw_print("Starting the kernel at pid:", os.getpid())
93 context = zmq.Context()
94 # Uncomment this to try closing the context.
95 # atexit.register(context.close)
96 session = Session(username=u'kernel')
97
15
98 reply_socket = context.socket(zmq.XREP)
99 xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep)
100 io.raw_print("XREP Channel on port", xrep_port)
101
16
102 pub_socket = context.socket(zmq.PUB)
17 def base_launch_kernel(code, shell_port=0, iopub_port=0, stdin_port=0, hb_port=0,
103 pub_port = bind_port(pub_socket, namespace.ip, namespace.pub)
18 ip=None, stdin=None, stdout=None, stderr=None,
104 io.raw_print("PUB Channel on port", pub_port)
105
106 req_socket = context.socket(zmq.XREQ)
107 req_port = bind_port(req_socket, namespace.ip, namespace.req)
108 io.raw_print("REQ Channel on port", req_port)
109
110 hb = Heartbeat(context, (namespace.ip, namespace.hb))
111 hb.start()
112 hb_port = hb.port
113 io.raw_print("Heartbeat REP Channel on port", hb_port)
114
115 # Helper to make it easier to connect to an existing kernel, until we have
116 # single-port connection negotiation fully implemented.
117 io.raw_print("To connect another client to this kernel, use:")
118 io.raw_print("-e --xreq {0} --sub {1} --rep {2} --hb {3}".format(
119 xrep_port, pub_port, req_port, hb_port))
120
121 # Redirect input streams and set a display hook.
122 if out_stream_factory:
123 sys.stdout = out_stream_factory(session, pub_socket, u'stdout')
124 sys.stderr = out_stream_factory(session, pub_socket, u'stderr')
125 if display_hook_factory:
126 sys.displayhook = display_hook_factory(session, pub_socket)
127
128 # Create the kernel.
129 kernel = kernel_factory(session=session, reply_socket=reply_socket,
130 pub_socket=pub_socket, req_socket=req_socket)
131 kernel.record_ports(xrep_port=xrep_port, pub_port=pub_port,
132 req_port=req_port, hb_port=hb_port)
133 return kernel
134
135
136 def start_kernel(namespace, kernel):
137 """ Starts a kernel.
138 """
139 # Configure this kernel process to poll the parent process, if necessary.
140 if sys.platform == 'win32':
141 if namespace.interrupt or namespace.parent:
142 poller = ParentPollerWindows(namespace.interrupt, namespace.parent)
143 poller.start()
144 elif namespace.parent:
145 poller = ParentPollerUnix()
146 poller.start()
147
148 # Start the kernel mainloop.
149 kernel.start()
150
151
152 def make_default_main(kernel_factory):
153 """ Creates the simplest possible kernel entry point.
154 """
155 def main():
156 namespace = make_argument_parser().parse_args()
157 kernel = make_kernel(namespace, kernel_factory, OutStream, DisplayHook)
158 start_kernel(namespace, kernel)
159 return main
160
161
162 def base_launch_kernel(code, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
163 stdin=None, stdout=None, stderr=None,
164 executable=None, independent=False, extra_arguments=[]):
19 executable=None, independent=False, extra_arguments=[]):
165 """ Launches a localhost kernel, binding to the specified ports.
20 """ Launches a localhost kernel, binding to the specified ports.
166
21
167 Parameters
22 Parameters
168 ----------
23 ----------
169 code : str,
24 code : str,
170 A string of Python code that imports and executes a kernel entry point.
25 A string of Python code that imports and executes a kernel entry point.
171
26
172 xrep_port : int, optional
27 shell_port : int, optional
173 The port to use for XREP channel.
28 The port to use for XREP channel.
174
29
175 pub_port : int, optional
30 iopub_port : int, optional
176 The port to use for the SUB channel.
31 The port to use for the SUB channel.
177
32
178 req_port : int, optional
33 stdin_port : int, optional
179 The port to use for the REQ (raw input) channel.
34 The port to use for the REQ (raw input) channel.
180
35
181 hb_port : int, optional
36 hb_port : int, optional
182 The port to use for the hearbeat REP channel.
37 The port to use for the hearbeat REP channel.
183
38
39 ip : str, optional
40 The ip address the kernel will bind to.
41
184 stdin, stdout, stderr : optional (default None)
42 stdin, stdout, stderr : optional (default None)
185 Standards streams, as defined in subprocess.Popen.
43 Standards streams, as defined in subprocess.Popen.
186
44
187 executable : str, optional (default sys.executable)
45 executable : str, optional (default sys.executable)
188 The Python executable to use for the kernel process.
46 The Python executable to use for the kernel process.
189
47
190 independent : bool, optional (default False)
48 independent : bool, optional (default False)
191 If set, the kernel process is guaranteed to survive if this process
49 If set, the kernel process is guaranteed to survive if this process
192 dies. If not set, an effort is made to ensure that the kernel is killed
50 dies. If not set, an effort is made to ensure that the kernel is killed
193 when this process dies. Note that in this case it is still good practice
51 when this process dies. Note that in this case it is still good practice
194 to kill kernels manually before exiting.
52 to kill kernels manually before exiting.
195
53
196 extra_arguments = list, optional
54 extra_arguments = list, optional
197 A list of extra arguments to pass when executing the launch code.
55 A list of extra arguments to pass when executing the launch code.
198
56
199 Returns
57 Returns
200 -------
58 -------
201 A tuple of form:
59 A tuple of form:
202 (kernel_process, xrep_port, pub_port, req_port)
60 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
203 where kernel_process is a Popen object and the ports are integers.
61 where kernel_process is a Popen object and the ports are integers.
204 """
62 """
205 # Find open ports as necessary.
63 # Find open ports as necessary.
206 ports = []
64 ports = []
207 ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + \
65 ports_needed = int(shell_port <= 0) + int(iopub_port <= 0) + \
208 int(req_port <= 0) + int(hb_port <= 0)
66 int(stdin_port <= 0) + int(hb_port <= 0)
209 for i in xrange(ports_needed):
67 for i in xrange(ports_needed):
210 sock = socket.socket()
68 sock = socket.socket()
211 sock.bind(('', 0))
69 sock.bind(('', 0))
212 ports.append(sock)
70 ports.append(sock)
213 for i, sock in enumerate(ports):
71 for i, sock in enumerate(ports):
214 port = sock.getsockname()[1]
72 port = sock.getsockname()[1]
215 sock.close()
73 sock.close()
216 ports[i] = port
74 ports[i] = port
217 if xrep_port <= 0:
75 if shell_port <= 0:
218 xrep_port = ports.pop(0)
76 shell_port = ports.pop(0)
219 if pub_port <= 0:
77 if iopub_port <= 0:
220 pub_port = ports.pop(0)
78 iopub_port = ports.pop(0)
221 if req_port <= 0:
79 if stdin_port <= 0:
222 req_port = ports.pop(0)
80 stdin_port = ports.pop(0)
223 if hb_port <= 0:
81 if hb_port <= 0:
224 hb_port = ports.pop(0)
82 hb_port = ports.pop(0)
225
83
226 # Build the kernel launch command.
84 # Build the kernel launch command.
227 if executable is None:
85 if executable is None:
228 executable = sys.executable
86 executable = sys.executable
229 arguments = [ executable, '-c', code, '--xrep', str(xrep_port),
87 arguments = [ executable, '-c', code, 'shell=%i'%shell_port,
230 '--pub', str(pub_port), '--req', str(req_port),
88 'iopub=%i'%iopub_port, 'stdin=%i'%stdin_port,
231 '--hb', str(hb_port) ]
89 'hb=%i'%hb_port
90 ]
91 if ip is not None:
92 arguments.append('ip=%s'%ip)
232 arguments.extend(extra_arguments)
93 arguments.extend(extra_arguments)
233
94
234 # Spawn a kernel.
95 # Spawn a kernel.
235 if sys.platform == 'win32':
96 if sys.platform == 'win32':
236 # Create a Win32 event for interrupting the kernel.
97 # Create a Win32 event for interrupting the kernel.
237 interrupt_event = ParentPollerWindows.create_interrupt_event()
98 interrupt_event = ParentPollerWindows.create_interrupt_event()
238 arguments += [ '--interrupt', str(int(interrupt_event)) ]
99 arguments += [ 'interrupt=%i'%interrupt_event ]
239
100
240 # If this process in running on pythonw, stdin, stdout, and stderr are
101 # If this process in running on pythonw, stdin, stdout, and stderr are
241 # invalid. Popen will fail unless they are suitably redirected. We don't
102 # invalid. Popen will fail unless they are suitably redirected. We don't
242 # read from the pipes, but they must exist.
103 # read from the pipes, but they must exist.
243 if sys.executable.endswith('pythonw.exe'):
104 if sys.executable.endswith('pythonw.exe'):
244 redirect = True
105 redirect = True
245 _stdin = PIPE if stdin is None else stdin
106 _stdin = PIPE if stdin is None else stdin
246 _stdout = PIPE if stdout is None else stdout
107 _stdout = PIPE if stdout is None else stdout
247 _stderr = PIPE if stderr is None else stderr
108 _stderr = PIPE if stderr is None else stderr
248 else:
109 else:
249 redirect = False
110 redirect = False
250 _stdin, _stdout, _stderr = stdin, stdout, stderr
111 _stdin, _stdout, _stderr = stdin, stdout, stderr
251
112
252 # If the kernel is running on pythonw and stdout/stderr are not been
113 # If the kernel is running on pythonw and stdout/stderr are not been
253 # re-directed, it will crash when more than 4KB of data is written to
114 # re-directed, it will crash when more than 4KB of data is written to
254 # stdout or stderr. This is a bug that has been with Python for a very
115 # stdout or stderr. This is a bug that has been with Python for a very
255 # long time; see http://bugs.python.org/issue706263.
116 # long time; see http://bugs.python.org/issue706263.
256 # A cleaner solution to this problem would be to pass os.devnull to
117 # A cleaner solution to this problem would be to pass os.devnull to
257 # Popen directly. Unfortunately, that does not work.
118 # Popen directly. Unfortunately, that does not work.
258 if executable.endswith('pythonw.exe'):
119 if executable.endswith('pythonw.exe'):
259 if stdout is None:
120 if stdout is None:
260 arguments.append('--no-stdout')
121 arguments.append('--no-stdout')
261 if stderr is None:
122 if stderr is None:
262 arguments.append('--no-stderr')
123 arguments.append('--no-stderr')
263
124
264 # Launch the kernel process.
125 # Launch the kernel process.
265 if independent:
126 if independent:
266 proc = Popen(arguments,
127 proc = Popen(arguments,
267 creationflags=512, # CREATE_NEW_PROCESS_GROUP
128 creationflags=512, # CREATE_NEW_PROCESS_GROUP
268 stdin=_stdin, stdout=_stdout, stderr=_stderr)
129 stdin=_stdin, stdout=_stdout, stderr=_stderr)
269 else:
130 else:
270 from _subprocess import DuplicateHandle, GetCurrentProcess, \
131 from _subprocess import DuplicateHandle, GetCurrentProcess, \
271 DUPLICATE_SAME_ACCESS
132 DUPLICATE_SAME_ACCESS
272 pid = GetCurrentProcess()
133 pid = GetCurrentProcess()
273 handle = DuplicateHandle(pid, pid, pid, 0,
134 handle = DuplicateHandle(pid, pid, pid, 0,
274 True, # Inheritable by new processes.
135 True, # Inheritable by new processes.
275 DUPLICATE_SAME_ACCESS)
136 DUPLICATE_SAME_ACCESS)
276 proc = Popen(arguments + ['--parent', str(int(handle))],
137 proc = Popen(arguments + ['parent=%i'%int(handle)],
277 stdin=_stdin, stdout=_stdout, stderr=_stderr)
138 stdin=_stdin, stdout=_stdout, stderr=_stderr)
278
139
279 # Attach the interrupt event to the Popen objet so it can be used later.
140 # Attach the interrupt event to the Popen objet so it can be used later.
280 proc.win32_interrupt_event = interrupt_event
141 proc.win32_interrupt_event = interrupt_event
281
142
282 # Clean up pipes created to work around Popen bug.
143 # Clean up pipes created to work around Popen bug.
283 if redirect:
144 if redirect:
284 if stdin is None:
145 if stdin is None:
285 proc.stdin.close()
146 proc.stdin.close()
286 if stdout is None:
147 if stdout is None:
287 proc.stdout.close()
148 proc.stdout.close()
288 if stderr is None:
149 if stderr is None:
289 proc.stderr.close()
150 proc.stderr.close()
290
151
291 else:
152 else:
292 if independent:
153 if independent:
293 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
154 proc = Popen(arguments, preexec_fn=lambda: os.setsid(),
294 stdin=stdin, stdout=stdout, stderr=stderr)
155 stdin=stdin, stdout=stdout, stderr=stderr)
295 else:
156 else:
296 proc = Popen(arguments + ['--parent'],
157 proc = Popen(arguments + ['parent=1'],
297 stdin=stdin, stdout=stdout, stderr=stderr)
158 stdin=stdin, stdout=stdout, stderr=stderr)
298
159
299 return proc, xrep_port, pub_port, req_port, hb_port
160 return proc, shell_port, iopub_port, stdin_port, hb_port
@@ -1,689 +1,746 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 import logging
24 import logging
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.config.application import boolean_flag
31 from IPython.core.newapplication import ProfileDir
30 from IPython.utils import io
32 from IPython.utils import io
31 from IPython.utils.jsonutil import json_clean
33 from IPython.utils.jsonutil import json_clean
32 from IPython.lib import pylabtools
34 from IPython.lib import pylabtools
33 from IPython.utils.traitlets import Instance, Float
35 from IPython.utils.traitlets import (
34 from entry_point import (base_launch_kernel, make_argument_parser, make_kernel,
36 List, Instance, Float, Dict, Bool, Int, Unicode, CaselessStrEnum
35 start_kernel)
37 )
38 from entry_point import base_launch_kernel
39 from kernelapp import KernelApp, kernel_flags, kernel_aliases
36 from iostream import OutStream
40 from iostream import OutStream
37 from session import Session, Message
41 from session import Session, Message
38 from zmqshell import ZMQInteractiveShell
42 from zmqshell import ZMQInteractiveShell
39
43
40 #-----------------------------------------------------------------------------
41 # Globals
42 #-----------------------------------------------------------------------------
43
44 # Module-level logger
45 logger = logging.getLogger(__name__)
46
44
47 # FIXME: this needs to be done more cleanly later, once we have proper
48 # configuration support. This is a library, so it shouldn't set a stream
49 # handler, see:
50 # http://docs.python.org/library/logging.html#configuring-logging-for-a-library
51 # But this lets us at least do developer debugging for now by manually turning
52 # it on/off. And once we have full config support, the client entry points
53 # will select their logging handlers, as well as passing to this library the
54 # logging level.
55
56 if 0: # dbg - set to 1 to actually see the messages.
57 logger.addHandler(logging.StreamHandler())
58 logger.setLevel(logging.DEBUG)
59
60 # /FIXME
61
45
62 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
63 # Main kernel class
47 # Main kernel class
64 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
65
49
66 class Kernel(Configurable):
50 class Kernel(Configurable):
67
51
68 #---------------------------------------------------------------------------
52 #---------------------------------------------------------------------------
69 # Kernel interface
53 # Kernel interface
70 #---------------------------------------------------------------------------
54 #---------------------------------------------------------------------------
71
55
72 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
56 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
73 session = Instance(Session)
57 session = Instance(Session)
74 reply_socket = Instance('zmq.Socket')
58 shell_socket = Instance('zmq.Socket')
75 pub_socket = Instance('zmq.Socket')
59 iopub_socket = Instance('zmq.Socket')
76 req_socket = Instance('zmq.Socket')
60 stdin_socket = Instance('zmq.Socket')
61 log = Instance(logging.Logger)
77
62
78 # Private interface
63 # Private interface
79
64
80 # Time to sleep after flushing the stdout/err buffers in each execute
65 # Time to sleep after flushing the stdout/err buffers in each execute
81 # cycle. While this introduces a hard limit on the minimal latency of the
66 # cycle. While this introduces a hard limit on the minimal latency of the
82 # execute cycle, it helps prevent output synchronization problems for
67 # execute cycle, it helps prevent output synchronization problems for
83 # clients.
68 # clients.
84 # Units are in seconds. The minimum zmq latency on local host is probably
69 # Units are in seconds. The minimum zmq latency on local host is probably
85 # ~150 microseconds, set this to 500us for now. We may need to increase it
70 # ~150 microseconds, set this to 500us for now. We may need to increase it
86 # a little if it's not enough after more interactive testing.
71 # a little if it's not enough after more interactive testing.
87 _execute_sleep = Float(0.0005, config=True)
72 _execute_sleep = Float(0.0005, config=True)
88
73
89 # Frequency of the kernel's event loop.
74 # Frequency of the kernel's event loop.
90 # Units are in seconds, kernel subclasses for GUI toolkits may need to
75 # Units are in seconds, kernel subclasses for GUI toolkits may need to
91 # adapt to milliseconds.
76 # adapt to milliseconds.
92 _poll_interval = Float(0.05, config=True)
77 _poll_interval = Float(0.05, config=True)
93
78
94 # If the shutdown was requested over the network, we leave here the
79 # If the shutdown was requested over the network, we leave here the
95 # necessary reply message so it can be sent by our registered atexit
80 # necessary reply message so it can be sent by our registered atexit
96 # handler. This ensures that the reply is only sent to clients truly at
81 # handler. This ensures that the reply is only sent to clients truly at
97 # the end of our shutdown process (which happens after the underlying
82 # the end of our shutdown process (which happens after the underlying
98 # IPython shell's own shutdown).
83 # IPython shell's own shutdown).
99 _shutdown_message = None
84 _shutdown_message = None
100
85
101 # This is a dict of port number that the kernel is listening on. It is set
86 # This is a dict of port number that the kernel is listening on. It is set
102 # by record_ports and used by connect_request.
87 # by record_ports and used by connect_request.
103 _recorded_ports = None
88 _recorded_ports = Dict()
89
104
90
105
91
106 def __init__(self, **kwargs):
92 def __init__(self, **kwargs):
107 super(Kernel, self).__init__(**kwargs)
93 super(Kernel, self).__init__(**kwargs)
108
94
109 # Before we even start up the shell, register *first* our exit handlers
95 # Before we even start up the shell, register *first* our exit handlers
110 # so they come before the shell's
96 # so they come before the shell's
111 atexit.register(self._at_shutdown)
97 atexit.register(self._at_shutdown)
112
98
113 # Initialize the InteractiveShell subclass
99 # Initialize the InteractiveShell subclass
114 self.shell = ZMQInteractiveShell.instance()
100 self.shell = ZMQInteractiveShell.instance(config=self.config)
115 self.shell.displayhook.session = self.session
101 self.shell.displayhook.session = self.session
116 self.shell.displayhook.pub_socket = self.pub_socket
102 self.shell.displayhook.pub_socket = self.iopub_socket
117 self.shell.display_pub.session = self.session
103 self.shell.display_pub.session = self.session
118 self.shell.display_pub.pub_socket = self.pub_socket
104 self.shell.display_pub.pub_socket = self.iopub_socket
119
105
120 # TMP - hack while developing
106 # TMP - hack while developing
121 self.shell._reply_content = None
107 self.shell._reply_content = None
122
108
123 # Build dict of handlers for message types
109 # Build dict of handlers for message types
124 msg_types = [ 'execute_request', 'complete_request',
110 msg_types = [ 'execute_request', 'complete_request',
125 'object_info_request', 'history_request',
111 'object_info_request', 'history_request',
126 'connect_request', 'shutdown_request']
112 'connect_request', 'shutdown_request']
127 self.handlers = {}
113 self.handlers = {}
128 for msg_type in msg_types:
114 for msg_type in msg_types:
129 self.handlers[msg_type] = getattr(self, msg_type)
115 self.handlers[msg_type] = getattr(self, msg_type)
130
116
131 def do_one_iteration(self):
117 def do_one_iteration(self):
132 """Do one iteration of the kernel's evaluation loop.
118 """Do one iteration of the kernel's evaluation loop.
133 """
119 """
134 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
120 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
135 if msg is None:
121 if msg is None:
136 return
122 return
137
123
138 # This assert will raise in versions of zeromq 2.0.7 and lesser.
124 # This assert will raise in versions of zeromq 2.0.7 and lesser.
139 # We now require 2.0.8 or above, so we can uncomment for safety.
125 # We now require 2.0.8 or above, so we can uncomment for safety.
140 # print(ident,msg, file=sys.__stdout__)
126 # print(ident,msg, file=sys.__stdout__)
141 assert ident is not None, "Missing message part."
127 assert ident is not None, "Missing message part."
142
128
143 # Print some info about this message and leave a '--->' marker, so it's
129 # Print some info about this message and leave a '--->' marker, so it's
144 # easier to trace visually the message chain when debugging. Each
130 # easier to trace visually the message chain when debugging. Each
145 # handler prints its message at the end.
131 # handler prints its message at the end.
146 # Eventually we'll move these from stdout to a logger.
132 self.log.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
147 logger.debug('\n*** MESSAGE TYPE:'+str(msg['msg_type'])+'***')
133 self.log.debug(' Content: '+str(msg['content'])+'\n --->\n ')
148 logger.debug(' Content: '+str(msg['content'])+'\n --->\n ')
149
134
150 # Find and call actual handler for message
135 # Find and call actual handler for message
151 handler = self.handlers.get(msg['msg_type'], None)
136 handler = self.handlers.get(msg['msg_type'], None)
152 if handler is None:
137 if handler is None:
153 logger.error("UNKNOWN MESSAGE TYPE:" +str(msg))
138 self.log.error("UNKNOWN MESSAGE TYPE:" +str(msg))
154 else:
139 else:
155 handler(ident, msg)
140 handler(ident, msg)
156
141
157 # Check whether we should exit, in case the incoming message set the
142 # Check whether we should exit, in case the incoming message set the
158 # exit flag on
143 # exit flag on
159 if self.shell.exit_now:
144 if self.shell.exit_now:
160 logger.debug('\nExiting IPython kernel...')
145 self.log.debug('\nExiting IPython kernel...')
161 # We do a normal, clean exit, which allows any actions registered
146 # We do a normal, clean exit, which allows any actions registered
162 # via atexit (such as history saving) to take place.
147 # via atexit (such as history saving) to take place.
163 sys.exit(0)
148 sys.exit(0)
164
149
165
150
166 def start(self):
151 def start(self):
167 """ Start the kernel main loop.
152 """ Start the kernel main loop.
168 """
153 """
154 poller = zmq.Poller()
155 poller.register(self.shell_socket, zmq.POLLIN)
169 while True:
156 while True:
170 try:
157 try:
171 time.sleep(self._poll_interval)
158 # scale by extra factor of 10, because there is no
159 # reason for this to be anything less than ~ 0.1s
160 # since it is a real poller and will respond
161 # to events immediately
162 poller.poll(10*1000*self._poll_interval)
172 self.do_one_iteration()
163 self.do_one_iteration()
173 except KeyboardInterrupt:
164 except KeyboardInterrupt:
174 # Ctrl-C shouldn't crash the kernel
165 # Ctrl-C shouldn't crash the kernel
175 io.raw_print("KeyboardInterrupt caught in kernel")
166 io.raw_print("KeyboardInterrupt caught in kernel")
176
167
177 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
168 def record_ports(self, ports):
178 """Record the ports that this kernel is using.
169 """Record the ports that this kernel is using.
179
170
180 The creator of the Kernel instance must call this methods if they
171 The creator of the Kernel instance must call this methods if they
181 want the :meth:`connect_request` method to return the port numbers.
172 want the :meth:`connect_request` method to return the port numbers.
182 """
173 """
183 self._recorded_ports = {
174 self._recorded_ports = ports
184 'xrep_port' : xrep_port,
185 'pub_port' : pub_port,
186 'req_port' : req_port,
187 'hb_port' : hb_port
188 }
189
175
190 #---------------------------------------------------------------------------
176 #---------------------------------------------------------------------------
191 # Kernel request handlers
177 # Kernel request handlers
192 #---------------------------------------------------------------------------
178 #---------------------------------------------------------------------------
193
179
194 def _publish_pyin(self, code, parent):
180 def _publish_pyin(self, code, parent):
195 """Publish the code request on the pyin stream."""
181 """Publish the code request on the pyin stream."""
196
182
197 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
183 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
198
184
199 def execute_request(self, ident, parent):
185 def execute_request(self, ident, parent):
200
186
201 status_msg = self.session.send(self.pub_socket,
187 status_msg = self.session.send(self.iopub_socket,
202 u'status',
188 u'status',
203 {u'execution_state':u'busy'},
189 {u'execution_state':u'busy'},
204 parent=parent
190 parent=parent
205 )
191 )
206
192
207 try:
193 try:
208 content = parent[u'content']
194 content = parent[u'content']
209 code = content[u'code']
195 code = content[u'code']
210 silent = content[u'silent']
196 silent = content[u'silent']
211 except:
197 except:
212 logger.error("Got bad msg: ")
198 self.log.error("Got bad msg: ")
213 logger.error(str(Message(parent)))
199 self.log.error(str(Message(parent)))
214 return
200 return
215
201
216 shell = self.shell # we'll need this a lot here
202 shell = self.shell # we'll need this a lot here
217
203
218 # Replace raw_input. Note that is not sufficient to replace
204 # Replace raw_input. Note that is not sufficient to replace
219 # raw_input in the user namespace.
205 # raw_input in the user namespace.
220 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
206 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
221 __builtin__.raw_input = raw_input
207 __builtin__.raw_input = raw_input
222
208
223 # Set the parent message of the display hook and out streams.
209 # Set the parent message of the display hook and out streams.
224 shell.displayhook.set_parent(parent)
210 shell.displayhook.set_parent(parent)
225 shell.display_pub.set_parent(parent)
211 shell.display_pub.set_parent(parent)
226 sys.stdout.set_parent(parent)
212 sys.stdout.set_parent(parent)
227 sys.stderr.set_parent(parent)
213 sys.stderr.set_parent(parent)
228
214
229 # Re-broadcast our input for the benefit of listening clients, and
215 # Re-broadcast our input for the benefit of listening clients, and
230 # start computing output
216 # start computing output
231 if not silent:
217 if not silent:
232 self._publish_pyin(code, parent)
218 self._publish_pyin(code, parent)
233
219
234 reply_content = {}
220 reply_content = {}
235 try:
221 try:
236 if silent:
222 if silent:
237 # run_code uses 'exec' mode, so no displayhook will fire, and it
223 # run_code uses 'exec' mode, so no displayhook will fire, and it
238 # doesn't call logging or history manipulations. Print
224 # doesn't call logging or history manipulations. Print
239 # statements in that code will obviously still execute.
225 # statements in that code will obviously still execute.
240 shell.run_code(code)
226 shell.run_code(code)
241 else:
227 else:
242 # FIXME: the shell calls the exception handler itself.
228 # FIXME: the shell calls the exception handler itself.
243 shell.run_cell(code)
229 shell.run_cell(code)
244 except:
230 except:
245 status = u'error'
231 status = u'error'
246 # FIXME: this code right now isn't being used yet by default,
232 # FIXME: this code right now isn't being used yet by default,
247 # because the run_cell() call above directly fires off exception
233 # because the run_cell() call above directly fires off exception
248 # reporting. This code, therefore, is only active in the scenario
234 # reporting. This code, therefore, is only active in the scenario
249 # where runlines itself has an unhandled exception. We need to
235 # where runlines itself has an unhandled exception. We need to
250 # uniformize this, for all exception construction to come from a
236 # uniformize this, for all exception construction to come from a
251 # single location in the codbase.
237 # single location in the codbase.
252 etype, evalue, tb = sys.exc_info()
238 etype, evalue, tb = sys.exc_info()
253 tb_list = traceback.format_exception(etype, evalue, tb)
239 tb_list = traceback.format_exception(etype, evalue, tb)
254 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
240 reply_content.update(shell._showtraceback(etype, evalue, tb_list))
255 else:
241 else:
256 status = u'ok'
242 status = u'ok'
257
243
258 reply_content[u'status'] = status
244 reply_content[u'status'] = status
259
245
260 # Return the execution counter so clients can display prompts
246 # Return the execution counter so clients can display prompts
261 reply_content['execution_count'] = shell.execution_count -1
247 reply_content['execution_count'] = shell.execution_count -1
262
248
263 # FIXME - fish exception info out of shell, possibly left there by
249 # FIXME - fish exception info out of shell, possibly left there by
264 # runlines. We'll need to clean up this logic later.
250 # runlines. We'll need to clean up this logic later.
265 if shell._reply_content is not None:
251 if shell._reply_content is not None:
266 reply_content.update(shell._reply_content)
252 reply_content.update(shell._reply_content)
267 # reset after use
253 # reset after use
268 shell._reply_content = None
254 shell._reply_content = None
269
255
270 # At this point, we can tell whether the main code execution succeeded
256 # At this point, we can tell whether the main code execution succeeded
271 # or not. If it did, we proceed to evaluate user_variables/expressions
257 # or not. If it did, we proceed to evaluate user_variables/expressions
272 if reply_content['status'] == 'ok':
258 if reply_content['status'] == 'ok':
273 reply_content[u'user_variables'] = \
259 reply_content[u'user_variables'] = \
274 shell.user_variables(content[u'user_variables'])
260 shell.user_variables(content[u'user_variables'])
275 reply_content[u'user_expressions'] = \
261 reply_content[u'user_expressions'] = \
276 shell.user_expressions(content[u'user_expressions'])
262 shell.user_expressions(content[u'user_expressions'])
277 else:
263 else:
278 # If there was an error, don't even try to compute variables or
264 # If there was an error, don't even try to compute variables or
279 # expressions
265 # expressions
280 reply_content[u'user_variables'] = {}
266 reply_content[u'user_variables'] = {}
281 reply_content[u'user_expressions'] = {}
267 reply_content[u'user_expressions'] = {}
282
268
283 # Payloads should be retrieved regardless of outcome, so we can both
269 # Payloads should be retrieved regardless of outcome, so we can both
284 # recover partial output (that could have been generated early in a
270 # recover partial output (that could have been generated early in a
285 # block, before an error) and clear the payload system always.
271 # block, before an error) and clear the payload system always.
286 reply_content[u'payload'] = shell.payload_manager.read_payload()
272 reply_content[u'payload'] = shell.payload_manager.read_payload()
287 # Be agressive about clearing the payload because we don't want
273 # Be agressive about clearing the payload because we don't want
288 # it to sit in memory until the next execute_request comes in.
274 # it to sit in memory until the next execute_request comes in.
289 shell.payload_manager.clear_payload()
275 shell.payload_manager.clear_payload()
290
276
291 # Flush output before sending the reply.
277 # Flush output before sending the reply.
292 sys.stdout.flush()
278 sys.stdout.flush()
293 sys.stderr.flush()
279 sys.stderr.flush()
294 # FIXME: on rare occasions, the flush doesn't seem to make it to the
280 # FIXME: on rare occasions, the flush doesn't seem to make it to the
295 # clients... This seems to mitigate the problem, but we definitely need
281 # clients... This seems to mitigate the problem, but we definitely need
296 # to better understand what's going on.
282 # to better understand what's going on.
297 if self._execute_sleep:
283 if self._execute_sleep:
298 time.sleep(self._execute_sleep)
284 time.sleep(self._execute_sleep)
299
285
300 # Send the reply.
286 # Send the reply.
301 reply_msg = self.session.send(self.reply_socket, u'execute_reply',
287 reply_msg = self.session.send(self.shell_socket, u'execute_reply',
302 reply_content, parent, ident=ident)
288 reply_content, parent, ident=ident)
303 logger.debug(str(reply_msg))
289 self.log.debug(str(reply_msg))
304
290
305 if reply_msg['content']['status'] == u'error':
291 if reply_msg['content']['status'] == u'error':
306 self._abort_queue()
292 self._abort_queue()
307
293
308 status_msg = self.session.send(self.pub_socket,
294 status_msg = self.session.send(self.iopub_socket,
309 u'status',
295 u'status',
310 {u'execution_state':u'idle'},
296 {u'execution_state':u'idle'},
311 parent=parent
297 parent=parent
312 )
298 )
313
299
314 def complete_request(self, ident, parent):
300 def complete_request(self, ident, parent):
315 txt, matches = self._complete(parent)
301 txt, matches = self._complete(parent)
316 matches = {'matches' : matches,
302 matches = {'matches' : matches,
317 'matched_text' : txt,
303 'matched_text' : txt,
318 'status' : 'ok'}
304 'status' : 'ok'}
319 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
305 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
320 matches, parent, ident)
306 matches, parent, ident)
321 logger.debug(str(completion_msg))
307 self.log.debug(str(completion_msg))
322
308
323 def object_info_request(self, ident, parent):
309 def object_info_request(self, ident, parent):
324 object_info = self.shell.object_inspect(parent['content']['oname'])
310 object_info = self.shell.object_inspect(parent['content']['oname'])
325 # Before we send this object over, we scrub it for JSON usage
311 # Before we send this object over, we scrub it for JSON usage
326 oinfo = json_clean(object_info)
312 oinfo = json_clean(object_info)
327 msg = self.session.send(self.reply_socket, 'object_info_reply',
313 msg = self.session.send(self.shell_socket, 'object_info_reply',
328 oinfo, parent, ident)
314 oinfo, parent, ident)
329 logger.debug(msg)
315 self.log.debug(msg)
330
316
331 def history_request(self, ident, parent):
317 def history_request(self, ident, parent):
332 # We need to pull these out, as passing **kwargs doesn't work with
318 # We need to pull these out, as passing **kwargs doesn't work with
333 # unicode keys before Python 2.6.5.
319 # unicode keys before Python 2.6.5.
334 hist_access_type = parent['content']['hist_access_type']
320 hist_access_type = parent['content']['hist_access_type']
335 raw = parent['content']['raw']
321 raw = parent['content']['raw']
336 output = parent['content']['output']
322 output = parent['content']['output']
337 if hist_access_type == 'tail':
323 if hist_access_type == 'tail':
338 n = parent['content']['n']
324 n = parent['content']['n']
339 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
325 hist = self.shell.history_manager.get_tail(n, raw=raw, output=output,
340 include_latest=True)
326 include_latest=True)
341
327
342 elif hist_access_type == 'range':
328 elif hist_access_type == 'range':
343 session = parent['content']['session']
329 session = parent['content']['session']
344 start = parent['content']['start']
330 start = parent['content']['start']
345 stop = parent['content']['stop']
331 stop = parent['content']['stop']
346 hist = self.shell.history_manager.get_range(session, start, stop,
332 hist = self.shell.history_manager.get_range(session, start, stop,
347 raw=raw, output=output)
333 raw=raw, output=output)
348
334
349 elif hist_access_type == 'search':
335 elif hist_access_type == 'search':
350 pattern = parent['content']['pattern']
336 pattern = parent['content']['pattern']
351 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
337 hist = self.shell.history_manager.search(pattern, raw=raw, output=output)
352
338
353 else:
339 else:
354 hist = []
340 hist = []
355 content = {'history' : list(hist)}
341 content = {'history' : list(hist)}
356 msg = self.session.send(self.reply_socket, 'history_reply',
342 msg = self.session.send(self.shell_socket, 'history_reply',
357 content, parent, ident)
343 content, parent, ident)
358 logger.debug(str(msg))
344 self.log.debug(str(msg))
359
345
360 def connect_request(self, ident, parent):
346 def connect_request(self, ident, parent):
361 if self._recorded_ports is not None:
347 if self._recorded_ports is not None:
362 content = self._recorded_ports.copy()
348 content = self._recorded_ports.copy()
363 else:
349 else:
364 content = {}
350 content = {}
365 msg = self.session.send(self.reply_socket, 'connect_reply',
351 msg = self.session.send(self.shell_socket, 'connect_reply',
366 content, parent, ident)
352 content, parent, ident)
367 logger.debug(msg)
353 self.log.debug(msg)
368
354
369 def shutdown_request(self, ident, parent):
355 def shutdown_request(self, ident, parent):
370 self.shell.exit_now = True
356 self.shell.exit_now = True
371 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
357 self._shutdown_message = self.session.msg(u'shutdown_reply', parent['content'], parent)
372 sys.exit(0)
358 sys.exit(0)
373
359
374 #---------------------------------------------------------------------------
360 #---------------------------------------------------------------------------
375 # Protected interface
361 # Protected interface
376 #---------------------------------------------------------------------------
362 #---------------------------------------------------------------------------
377
363
378 def _abort_queue(self):
364 def _abort_queue(self):
379 while True:
365 while True:
380 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
366 ident,msg = self.session.recv(self.shell_socket, zmq.NOBLOCK)
381 if msg is None:
367 if msg is None:
382 break
368 break
383 else:
369 else:
384 assert ident is not None, \
370 assert ident is not None, \
385 "Unexpected missing message part."
371 "Unexpected missing message part."
386
372
387 logger.debug("Aborting:\n"+str(Message(msg)))
373 self.log.debug("Aborting:\n"+str(Message(msg)))
388 msg_type = msg['msg_type']
374 msg_type = msg['msg_type']
389 reply_type = msg_type.split('_')[0] + '_reply'
375 reply_type = msg_type.split('_')[0] + '_reply'
390 reply_msg = self.session.send(self.reply_socket, reply_type,
376 reply_msg = self.session.send(self.shell_socket, reply_type,
391 {'status' : 'aborted'}, msg, ident=ident)
377 {'status' : 'aborted'}, msg, ident=ident)
392 logger.debug(reply_msg)
378 self.log.debug(reply_msg)
393 # We need to wait a bit for requests to come in. This can probably
379 # We need to wait a bit for requests to come in. This can probably
394 # be set shorter for true asynchronous clients.
380 # be set shorter for true asynchronous clients.
395 time.sleep(0.1)
381 time.sleep(0.1)
396
382
397 def _raw_input(self, prompt, ident, parent):
383 def _raw_input(self, prompt, ident, parent):
398 # Flush output before making the request.
384 # Flush output before making the request.
399 sys.stderr.flush()
385 sys.stderr.flush()
400 sys.stdout.flush()
386 sys.stdout.flush()
401
387
402 # Send the input request.
388 # Send the input request.
403 content = dict(prompt=prompt)
389 content = dict(prompt=prompt)
404 msg = self.session.send(self.req_socket, u'input_request', content, parent)
390 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
405
391
406 # Await a response.
392 # Await a response.
407 ident, reply = self.session.recv(self.req_socket, 0)
393 ident, reply = self.session.recv(self.stdin_socket, 0)
408 try:
394 try:
409 value = reply['content']['value']
395 value = reply['content']['value']
410 except:
396 except:
411 logger.error("Got bad raw_input reply: ")
397 self.log.error("Got bad raw_input reply: ")
412 logger.error(str(Message(parent)))
398 self.log.error(str(Message(parent)))
413 value = ''
399 value = ''
414 return value
400 return value
415
401
416 def _complete(self, msg):
402 def _complete(self, msg):
417 c = msg['content']
403 c = msg['content']
418 try:
404 try:
419 cpos = int(c['cursor_pos'])
405 cpos = int(c['cursor_pos'])
420 except:
406 except:
421 # If we don't get something that we can convert to an integer, at
407 # If we don't get something that we can convert to an integer, at
422 # least attempt the completion guessing the cursor is at the end of
408 # least attempt the completion guessing the cursor is at the end of
423 # the text, if there's any, and otherwise of the line
409 # the text, if there's any, and otherwise of the line
424 cpos = len(c['text'])
410 cpos = len(c['text'])
425 if cpos==0:
411 if cpos==0:
426 cpos = len(c['line'])
412 cpos = len(c['line'])
427 return self.shell.complete(c['text'], c['line'], cpos)
413 return self.shell.complete(c['text'], c['line'], cpos)
428
414
429 def _object_info(self, context):
415 def _object_info(self, context):
430 symbol, leftover = self._symbol_from_context(context)
416 symbol, leftover = self._symbol_from_context(context)
431 if symbol is not None and not leftover:
417 if symbol is not None and not leftover:
432 doc = getattr(symbol, '__doc__', '')
418 doc = getattr(symbol, '__doc__', '')
433 else:
419 else:
434 doc = ''
420 doc = ''
435 object_info = dict(docstring = doc)
421 object_info = dict(docstring = doc)
436 return object_info
422 return object_info
437
423
438 def _symbol_from_context(self, context):
424 def _symbol_from_context(self, context):
439 if not context:
425 if not context:
440 return None, context
426 return None, context
441
427
442 base_symbol_string = context[0]
428 base_symbol_string = context[0]
443 symbol = self.shell.user_ns.get(base_symbol_string, None)
429 symbol = self.shell.user_ns.get(base_symbol_string, None)
444 if symbol is None:
430 if symbol is None:
445 symbol = __builtin__.__dict__.get(base_symbol_string, None)
431 symbol = __builtin__.__dict__.get(base_symbol_string, None)
446 if symbol is None:
432 if symbol is None:
447 return None, context
433 return None, context
448
434
449 context = context[1:]
435 context = context[1:]
450 for i, name in enumerate(context):
436 for i, name in enumerate(context):
451 new_symbol = getattr(symbol, name, None)
437 new_symbol = getattr(symbol, name, None)
452 if new_symbol is None:
438 if new_symbol is None:
453 return symbol, context[i:]
439 return symbol, context[i:]
454 else:
440 else:
455 symbol = new_symbol
441 symbol = new_symbol
456
442
457 return symbol, []
443 return symbol, []
458
444
459 def _at_shutdown(self):
445 def _at_shutdown(self):
460 """Actions taken at shutdown by the kernel, called by python's atexit.
446 """Actions taken at shutdown by the kernel, called by python's atexit.
461 """
447 """
462 # io.rprint("Kernel at_shutdown") # dbg
448 # io.rprint("Kernel at_shutdown") # dbg
463 if self._shutdown_message is not None:
449 if self._shutdown_message is not None:
464 self.session.send(self.reply_socket, self._shutdown_message)
450 self.session.send(self.shell_socket, self._shutdown_message)
465 self.session.send(self.pub_socket, self._shutdown_message)
451 self.session.send(self.iopub_socket, self._shutdown_message)
466 logger.debug(str(self._shutdown_message))
452 self.log.debug(str(self._shutdown_message))
467 # A very short sleep to give zmq time to flush its message buffers
453 # A very short sleep to give zmq time to flush its message buffers
468 # before Python truly shuts down.
454 # before Python truly shuts down.
469 time.sleep(0.01)
455 time.sleep(0.01)
470
456
471
457
472 class QtKernel(Kernel):
458 class QtKernel(Kernel):
473 """A Kernel subclass with Qt support."""
459 """A Kernel subclass with Qt support."""
474
460
475 def start(self):
461 def start(self):
476 """Start a kernel with QtPy4 event loop integration."""
462 """Start a kernel with QtPy4 event loop integration."""
477
463
478 from PyQt4 import QtCore
464 from PyQt4 import QtCore
479 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
465 from IPython.lib.guisupport import get_app_qt4, start_event_loop_qt4
480
466
481 self.app = get_app_qt4([" "])
467 self.app = get_app_qt4([" "])
482 self.app.setQuitOnLastWindowClosed(False)
468 self.app.setQuitOnLastWindowClosed(False)
483 self.timer = QtCore.QTimer()
469 self.timer = QtCore.QTimer()
484 self.timer.timeout.connect(self.do_one_iteration)
470 self.timer.timeout.connect(self.do_one_iteration)
485 # Units for the timer are in milliseconds
471 # Units for the timer are in milliseconds
486 self.timer.start(1000*self._poll_interval)
472 self.timer.start(1000*self._poll_interval)
487 start_event_loop_qt4(self.app)
473 start_event_loop_qt4(self.app)
488
474
489
475
490 class WxKernel(Kernel):
476 class WxKernel(Kernel):
491 """A Kernel subclass with Wx support."""
477 """A Kernel subclass with Wx support."""
492
478
493 def start(self):
479 def start(self):
494 """Start a kernel with wx event loop support."""
480 """Start a kernel with wx event loop support."""
495
481
496 import wx
482 import wx
497 from IPython.lib.guisupport import start_event_loop_wx
483 from IPython.lib.guisupport import start_event_loop_wx
498
484
499 doi = self.do_one_iteration
485 doi = self.do_one_iteration
500 # Wx uses milliseconds
486 # Wx uses milliseconds
501 poll_interval = int(1000*self._poll_interval)
487 poll_interval = int(1000*self._poll_interval)
502
488
503 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
489 # We have to put the wx.Timer in a wx.Frame for it to fire properly.
504 # We make the Frame hidden when we create it in the main app below.
490 # We make the Frame hidden when we create it in the main app below.
505 class TimerFrame(wx.Frame):
491 class TimerFrame(wx.Frame):
506 def __init__(self, func):
492 def __init__(self, func):
507 wx.Frame.__init__(self, None, -1)
493 wx.Frame.__init__(self, None, -1)
508 self.timer = wx.Timer(self)
494 self.timer = wx.Timer(self)
509 # Units for the timer are in milliseconds
495 # Units for the timer are in milliseconds
510 self.timer.Start(poll_interval)
496 self.timer.Start(poll_interval)
511 self.Bind(wx.EVT_TIMER, self.on_timer)
497 self.Bind(wx.EVT_TIMER, self.on_timer)
512 self.func = func
498 self.func = func
513
499
514 def on_timer(self, event):
500 def on_timer(self, event):
515 self.func()
501 self.func()
516
502
517 # We need a custom wx.App to create our Frame subclass that has the
503 # We need a custom wx.App to create our Frame subclass that has the
518 # wx.Timer to drive the ZMQ event loop.
504 # wx.Timer to drive the ZMQ event loop.
519 class IPWxApp(wx.App):
505 class IPWxApp(wx.App):
520 def OnInit(self):
506 def OnInit(self):
521 self.frame = TimerFrame(doi)
507 self.frame = TimerFrame(doi)
522 self.frame.Show(False)
508 self.frame.Show(False)
523 return True
509 return True
524
510
525 # The redirect=False here makes sure that wx doesn't replace
511 # The redirect=False here makes sure that wx doesn't replace
526 # sys.stdout/stderr with its own classes.
512 # sys.stdout/stderr with its own classes.
527 self.app = IPWxApp(redirect=False)
513 self.app = IPWxApp(redirect=False)
528 start_event_loop_wx(self.app)
514 start_event_loop_wx(self.app)
529
515
530
516
531 class TkKernel(Kernel):
517 class TkKernel(Kernel):
532 """A Kernel subclass with Tk support."""
518 """A Kernel subclass with Tk support."""
533
519
534 def start(self):
520 def start(self):
535 """Start a Tk enabled event loop."""
521 """Start a Tk enabled event loop."""
536
522
537 import Tkinter
523 import Tkinter
538 doi = self.do_one_iteration
524 doi = self.do_one_iteration
539 # Tk uses milliseconds
525 # Tk uses milliseconds
540 poll_interval = int(1000*self._poll_interval)
526 poll_interval = int(1000*self._poll_interval)
541 # For Tkinter, we create a Tk object and call its withdraw method.
527 # For Tkinter, we create a Tk object and call its withdraw method.
542 class Timer(object):
528 class Timer(object):
543 def __init__(self, func):
529 def __init__(self, func):
544 self.app = Tkinter.Tk()
530 self.app = Tkinter.Tk()
545 self.app.withdraw()
531 self.app.withdraw()
546 self.func = func
532 self.func = func
547
533
548 def on_timer(self):
534 def on_timer(self):
549 self.func()
535 self.func()
550 self.app.after(poll_interval, self.on_timer)
536 self.app.after(poll_interval, self.on_timer)
551
537
552 def start(self):
538 def start(self):
553 self.on_timer() # Call it once to get things going.
539 self.on_timer() # Call it once to get things going.
554 self.app.mainloop()
540 self.app.mainloop()
555
541
556 self.timer = Timer(doi)
542 self.timer = Timer(doi)
557 self.timer.start()
543 self.timer.start()
558
544
559
545
560 class GTKKernel(Kernel):
546 class GTKKernel(Kernel):
561 """A Kernel subclass with GTK support."""
547 """A Kernel subclass with GTK support."""
562
548
563 def start(self):
549 def start(self):
564 """Start the kernel, coordinating with the GTK event loop"""
550 """Start the kernel, coordinating with the GTK event loop"""
565 from .gui.gtkembed import GTKEmbed
551 from .gui.gtkembed import GTKEmbed
566
552
567 gtk_kernel = GTKEmbed(self)
553 gtk_kernel = GTKEmbed(self)
568 gtk_kernel.start()
554 gtk_kernel.start()
569
555
570
556
571 #-----------------------------------------------------------------------------
557 #-----------------------------------------------------------------------------
572 # Kernel main and launch functions
558 # Aliases and Flags for the IPKernelApp
573 #-----------------------------------------------------------------------------
559 #-----------------------------------------------------------------------------
574
560
575 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
561 flags = dict(kernel_flags)
576 stdin=None, stdout=None, stderr=None,
577 executable=None, independent=False, pylab=False, colors=None):
578 """Launches a localhost kernel, binding to the specified ports.
579
580 Parameters
581 ----------
582 ip : str, optional
583 The ip address the kernel will bind to.
584
585 xrep_port : int, optional
586 The port to use for XREP channel.
587
588 pub_port : int, optional
589 The port to use for the SUB channel.
590
562
591 req_port : int, optional
563 addflag = lambda *args: flags.update(boolean_flag(*args))
592 The port to use for the REQ (raw input) channel.
564 addflag('automagic', 'InteractiveShell.automagic',
593
565 """Turn on the auto calling of magic commands. Type %%magic at the
594 hb_port : int, optional
566 IPython prompt for more information.""",
595 The port to use for the hearbeat REP channel.
567 'Turn off the auto calling of magic commands.'
568 )
569 addflag('banner', 'InteractiveShell.display_banner',
570 "Display a banner upon starting IPython.",
571 "Don't display a banner upon starting IPython."
572 )
573 addflag('pdb', 'InteractiveShell.pdb',
574 "Enable auto calling the pdb debugger after every exception.",
575 "Disable auto calling the pdb debugger after every exception."
576 )
577 addflag('pprint', 'PlainTextFormatter.pprint',
578 "Enable auto pretty printing of results.",
579 "Disable auto auto pretty printing of results."
580 )
581 addflag('color-info', 'InteractiveShell.color_info',
582 """IPython can display information about objects via a set of func-
583 tions, and optionally can use colors for this, syntax highlighting
584 source code and various other elements. However, because this
585 information is passed through a pager (like 'less') and many pagers get
586 confused with color codes, this option is off by default. You can test
587 it and turn it on permanently in your ipython_config.py file if it
588 works for you. Test it and turn it on permanently if it works with
589 your system. The magic function %%color_info allows you to toggle this
590 inter- actively for testing.""",
591 "Disable using colors for info related things."
592 )
593 addflag('deep-reload', 'InteractiveShell.deep_reload',
594 """Enable deep (recursive) reloading by default. IPython can use the
595 deep_reload module which reloads changes in modules recursively (it
596 replaces the reload() function, so you don't need to change anything to
597 use it). deep_reload() forces a full reload of modules whose code may
598 have changed, which the default reload() function does not. When
599 deep_reload is off, IPython will use the normal reload(), but
600 deep_reload will still be available as dreload(). This fea- ture is off
601 by default [which means that you have both normal reload() and
602 dreload()].""",
603 "Disable deep (recursive) reloading by default."
604 )
605 addflag('readline', 'InteractiveShell.readline_use',
606 "Enable readline for command line usage.",
607 "Disable readline for command line usage."
608 )
596
609
597 stdin, stdout, stderr : optional (default None)
610 flags['pylab'] = (
598 Standards streams, as defined in subprocess.Popen.
611 {'IPKernelApp' : {'pylab' : 'auto'}},
612 """Pre-load matplotlib and numpy for interactive use with
613 the default matplotlib backend."""
614 )
599
615
600 executable : str, optional (default sys.executable)
616 aliases = dict(kernel_aliases)
601 The Python executable to use for the kernel process.
617
618 # it's possible we don't want short aliases for *all* of these:
619 aliases.update(dict(
620 autocall='InteractiveShell.autocall',
621 cache_size='InteractiveShell.cache_size',
622 colors='InteractiveShell.colors',
623 logfile='InteractiveShell.logfile',
624 log_append='InteractiveShell.logappend',
625 pi1='InteractiveShell.prompt_in1',
626 pi2='InteractiveShell.prompt_in2',
627 po='InteractiveShell.prompt_out',
628 si='InteractiveShell.separate_in',
629 so='InteractiveShell.separate_out',
630 so2='InteractiveShell.separate_out2',
631 xmode='InteractiveShell.xmode',
632 c='IPKernelApp.code_to_run',
633 ext='IPKernelApp.extra_extension',
634 pylab='IPKernelApp.pylab',
635 ))
602
636
603 independent : bool, optional (default False)
637 #-----------------------------------------------------------------------------
604 If set, the kernel process is guaranteed to survive if this process
638 # The IPKernelApp class
605 dies. If not set, an effort is made to ensure that the kernel is killed
639 #-----------------------------------------------------------------------------
606 when this process dies. Note that in this case it is still good practice
607 to kill kernels manually before exiting.
608
640
609 pylab : bool or string, optional (default False)
641 class IPKernelApp(KernelApp):
610 If not False, the kernel will be launched with pylab enabled. If a
642 name = 'ipkernel'
611 string is passed, matplotlib will use the specified backend. Otherwise,
643
612 matplotlib's default backend will be used.
644 aliases = Dict(aliases)
645 flags = Dict(flags)
646 classes = [Kernel, ZMQInteractiveShell, ProfileDir]
647 # configurables
648 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'],
649 config=True,
650 help="""Pre-load matplotlib and numpy for interactive use,
651 selecting a particular matplotlib backend and loop integration.
652 """
653 )
654 extensions = List(Unicode, config=True,
655 help="A list of dotted module names of IPython extensions to load."
656 )
657 extra_extension = Unicode('', config=True,
658 help="dotted module name of an IPython extension to load."
659 )
660 def _extra_extension_changed(self, name, old, new):
661 if new:
662 # add to self.extensions
663 self.extensions.append(new)
613
664
614 colors : None or string, optional (default None)
665 exec_files = List(Unicode, config=True,
615 If not None, specify the color scheme. One of (NoColor, LightBG, Linux)
666 help="""List of files to run at IPython startup."""
667 )
668 file_to_run = Unicode('', config=True,
669 help="""A file to be run""")
670 def _file_to_run_changed(self, name, old, new):
671 self.exec_files.append(new)
616
672
617 Returns
673 exec_lines = List(Unicode, config=True,
618 -------
674 help="""lines of code to run at IPython startup."""
619 A tuple of form:
675 )
620 (kernel_process, xrep_port, pub_port, req_port)
676 code_to_run = Unicode('', config=True,
621 where kernel_process is a Popen object and the ports are integers.
677 help="Execute the given command string."
622 """
678 )
623 extra_arguments = []
679 def _code_to_run_changed(self, name, old, new):
624 if pylab:
680 self.exec_lines.append(new)
625 extra_arguments.append('--pylab')
626 if isinstance(pylab, basestring):
627 extra_arguments.append(pylab)
628 if ip is not None:
629 extra_arguments.append('--ip')
630 if isinstance(ip, basestring):
631 extra_arguments.append(ip)
632 if colors is not None:
633 extra_arguments.append('--colors')
634 extra_arguments.append(colors)
635 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
636 xrep_port, pub_port, req_port, hb_port,
637 stdin, stdout, stderr,
638 executable, independent, extra_arguments)
639
681
682 def init_kernel(self):
683 kernel_factory = Kernel
640
684
641 def main():
685 kernel_map = {
642 """ The IPython kernel main entry point.
643 """
644 parser = make_argument_parser()
645 parser.add_argument('--pylab', type=str, metavar='GUI', nargs='?',
646 const='auto', help = \
647 "Pre-load matplotlib and numpy for interactive use. If GUI is not \
648 given, the GUI backend is matplotlib's, otherwise use one of: \
649 ['tk', 'gtk', 'qt', 'wx', 'osx', 'inline'].")
650 parser.add_argument('--colors',
651 type=str, dest='colors',
652 help="Set the color scheme (NoColor, Linux, and LightBG).",
653 metavar='ZMQInteractiveShell.colors')
654 namespace = parser.parse_args()
655
656 kernel_class = Kernel
657
658 kernel_classes = {
659 'qt' : QtKernel,
686 'qt' : QtKernel,
660 'qt4': QtKernel,
687 'qt4': QtKernel,
661 'inline': Kernel,
688 'inline': Kernel,
662 'osx': TkKernel,
689 'osx': TkKernel,
663 'wx' : WxKernel,
690 'wx' : WxKernel,
664 'tk' : TkKernel,
691 'tk' : TkKernel,
665 'gtk': GTKKernel,
692 'gtk': GTKKernel,
666 }
693 }
667 if namespace.pylab:
694
668 if namespace.pylab == 'auto':
695 if self.pylab:
669 gui, backend = pylabtools.find_gui_and_backend()
696 key = None if self.pylab == 'auto' else self.pylab
670 else:
697 gui, backend = pylabtools.find_gui_and_backend(key)
671 gui, backend = pylabtools.find_gui_and_backend(namespace.pylab)
698 kernel_factory = kernel_map.get(gui)
672 kernel_class = kernel_classes.get(gui)
699 if kernel_factory is None:
673 if kernel_class is None:
674 raise ValueError('GUI is not supported: %r' % gui)
700 raise ValueError('GUI is not supported: %r' % gui)
675 pylabtools.activate_matplotlib(backend)
701 pylabtools.activate_matplotlib(backend)
676 if namespace.colors:
677 ZMQInteractiveShell.colors=namespace.colors
678
702
679 kernel = make_kernel(namespace, kernel_class, OutStream)
703 kernel = kernel_factory(config=self.config, session=self.session,
704 shell_socket=self.shell_socket,
705 iopub_socket=self.iopub_socket,
706 stdin_socket=self.stdin_socket,
707 log=self.log
708 )
709 self.kernel = kernel
710 kernel.record_ports(self.ports)
680
711
681 if namespace.pylab:
712 if self.pylab:
682 pylabtools.import_pylab(kernel.shell.user_ns, backend,
713 pylabtools.import_pylab(kernel.shell.user_ns, backend,
683 shell=kernel.shell)
714 shell=kernel.shell)
684
715
685 start_kernel(namespace, kernel)
716
717
718 #-----------------------------------------------------------------------------
719 # Kernel main and launch functions
720 #-----------------------------------------------------------------------------
721
722 def launch_kernel(*args, **kwargs):
723 """Launches a localhost IPython kernel, binding to the specified ports.
724
725 This function simply calls entry_point.base_launch_kernel with the right first
726 command to start an ipkernel. See base_launch_kernel for arguments.
727
728 Returns
729 -------
730 A tuple of form:
731 (kernel_process, shell_port, iopub_port, stdin_port, hb_port)
732 where kernel_process is a Popen object and the ports are integers.
733 """
734 return base_launch_kernel('from IPython.zmq.ipkernel import main; main()',
735 *args, **kwargs)
736
737
738 def main():
739 """Run a PyKernel as an application"""
740 app = IPKernelApp()
741 app.initialize()
742 app.start()
686
743
687
744
688 if __name__ == '__main__':
745 if __name__ == '__main__':
689 main()
746 main()
@@ -1,968 +1,968 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 import errno
20 import errno
21 from Queue import Queue, Empty
21 from Queue import Queue, Empty
22 from subprocess import Popen
22 from subprocess import Popen
23 import signal
23 import signal
24 import sys
24 import sys
25 from threading import Thread
25 from threading import Thread
26 import time
26 import time
27 import logging
27 import logging
28
28
29 # System library imports.
29 # System library imports.
30 import zmq
30 import zmq
31 from zmq import POLLIN, POLLOUT, POLLERR
31 from zmq import POLLIN, POLLOUT, POLLERR
32 from zmq.eventloop import ioloop
32 from zmq.eventloop import ioloop
33
33
34 # Local imports.
34 # Local imports.
35 from IPython.utils import io
35 from IPython.utils import io
36 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
36 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
37 from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress
37 from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress
38 from session import Session, Message
38 from session import Session, Message
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Constants and exceptions
41 # Constants and exceptions
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 class InvalidPortNumber(Exception):
44 class InvalidPortNumber(Exception):
45 pass
45 pass
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Utility functions
48 # Utility functions
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 # some utilities to validate message structure, these might get moved elsewhere
51 # some utilities to validate message structure, these might get moved elsewhere
52 # if they prove to have more generic utility
52 # if they prove to have more generic utility
53
53
54 def validate_string_list(lst):
54 def validate_string_list(lst):
55 """Validate that the input is a list of strings.
55 """Validate that the input is a list of strings.
56
56
57 Raises ValueError if not."""
57 Raises ValueError if not."""
58 if not isinstance(lst, list):
58 if not isinstance(lst, list):
59 raise ValueError('input %r must be a list' % lst)
59 raise ValueError('input %r must be a list' % lst)
60 for x in lst:
60 for x in lst:
61 if not isinstance(x, basestring):
61 if not isinstance(x, basestring):
62 raise ValueError('element %r in list must be a string' % x)
62 raise ValueError('element %r in list must be a string' % x)
63
63
64
64
65 def validate_string_dict(dct):
65 def validate_string_dict(dct):
66 """Validate that the input is a dict with string keys and values.
66 """Validate that the input is a dict with string keys and values.
67
67
68 Raises ValueError if not."""
68 Raises ValueError if not."""
69 for k,v in dct.iteritems():
69 for k,v in dct.iteritems():
70 if not isinstance(k, basestring):
70 if not isinstance(k, basestring):
71 raise ValueError('key %r in dict must be a string' % k)
71 raise ValueError('key %r in dict must be a string' % k)
72 if not isinstance(v, basestring):
72 if not isinstance(v, basestring):
73 raise ValueError('value %r in dict must be a string' % v)
73 raise ValueError('value %r in dict must be a string' % v)
74
74
75
75
76 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
77 # ZMQ Socket Channel classes
77 # ZMQ Socket Channel classes
78 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
79
79
80 class ZmqSocketChannel(Thread):
80 class ZmqSocketChannel(Thread):
81 """The base class for the channels that use ZMQ sockets.
81 """The base class for the channels that use ZMQ sockets.
82 """
82 """
83 context = None
83 context = None
84 session = None
84 session = None
85 socket = None
85 socket = None
86 ioloop = None
86 ioloop = None
87 iostate = None
87 iostate = None
88 _address = None
88 _address = None
89
89
90 def __init__(self, context, session, address):
90 def __init__(self, context, session, address):
91 """Create a channel
91 """Create a channel
92
92
93 Parameters
93 Parameters
94 ----------
94 ----------
95 context : :class:`zmq.Context`
95 context : :class:`zmq.Context`
96 The ZMQ context to use.
96 The ZMQ context to use.
97 session : :class:`session.Session`
97 session : :class:`session.Session`
98 The session to use.
98 The session to use.
99 address : tuple
99 address : tuple
100 Standard (ip, port) tuple that the kernel is listening on.
100 Standard (ip, port) tuple that the kernel is listening on.
101 """
101 """
102 super(ZmqSocketChannel, self).__init__()
102 super(ZmqSocketChannel, self).__init__()
103 self.daemon = True
103 self.daemon = True
104
104
105 self.context = context
105 self.context = context
106 self.session = session
106 self.session = session
107 if address[1] == 0:
107 if address[1] == 0:
108 message = 'The port number for a channel cannot be 0.'
108 message = 'The port number for a channel cannot be 0.'
109 raise InvalidPortNumber(message)
109 raise InvalidPortNumber(message)
110 self._address = address
110 self._address = address
111
111
112 def _run_loop(self):
112 def _run_loop(self):
113 """Run my loop, ignoring EINTR events in the poller"""
113 """Run my loop, ignoring EINTR events in the poller"""
114 while True:
114 while True:
115 try:
115 try:
116 self.ioloop.start()
116 self.ioloop.start()
117 except zmq.ZMQError as e:
117 except zmq.ZMQError as e:
118 if e.errno == errno.EINTR:
118 if e.errno == errno.EINTR:
119 continue
119 continue
120 else:
120 else:
121 raise
121 raise
122 else:
122 else:
123 break
123 break
124
124
125 def stop(self):
125 def stop(self):
126 """Stop the channel's activity.
126 """Stop the channel's activity.
127
127
128 This calls :method:`Thread.join` and returns when the thread
128 This calls :method:`Thread.join` and returns when the thread
129 terminates. :class:`RuntimeError` will be raised if
129 terminates. :class:`RuntimeError` will be raised if
130 :method:`self.start` is called again.
130 :method:`self.start` is called again.
131 """
131 """
132 self.join()
132 self.join()
133
133
134 @property
134 @property
135 def address(self):
135 def address(self):
136 """Get the channel's address as an (ip, port) tuple.
136 """Get the channel's address as an (ip, port) tuple.
137
137
138 By the default, the address is (localhost, 0), where 0 means a random
138 By the default, the address is (localhost, 0), where 0 means a random
139 port.
139 port.
140 """
140 """
141 return self._address
141 return self._address
142
142
143 def add_io_state(self, state):
143 def add_io_state(self, state):
144 """Add IO state to the eventloop.
144 """Add IO state to the eventloop.
145
145
146 Parameters
146 Parameters
147 ----------
147 ----------
148 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
148 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
149 The IO state flag to set.
149 The IO state flag to set.
150
150
151 This is thread safe as it uses the thread safe IOLoop.add_callback.
151 This is thread safe as it uses the thread safe IOLoop.add_callback.
152 """
152 """
153 def add_io_state_callback():
153 def add_io_state_callback():
154 if not self.iostate & state:
154 if not self.iostate & state:
155 self.iostate = self.iostate | state
155 self.iostate = self.iostate | state
156 self.ioloop.update_handler(self.socket, self.iostate)
156 self.ioloop.update_handler(self.socket, self.iostate)
157 self.ioloop.add_callback(add_io_state_callback)
157 self.ioloop.add_callback(add_io_state_callback)
158
158
159 def drop_io_state(self, state):
159 def drop_io_state(self, state):
160 """Drop IO state from the eventloop.
160 """Drop IO state from the eventloop.
161
161
162 Parameters
162 Parameters
163 ----------
163 ----------
164 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
164 state : zmq.POLLIN|zmq.POLLOUT|zmq.POLLERR
165 The IO state flag to set.
165 The IO state flag to set.
166
166
167 This is thread safe as it uses the thread safe IOLoop.add_callback.
167 This is thread safe as it uses the thread safe IOLoop.add_callback.
168 """
168 """
169 def drop_io_state_callback():
169 def drop_io_state_callback():
170 if self.iostate & state:
170 if self.iostate & state:
171 self.iostate = self.iostate & (~state)
171 self.iostate = self.iostate & (~state)
172 self.ioloop.update_handler(self.socket, self.iostate)
172 self.ioloop.update_handler(self.socket, self.iostate)
173 self.ioloop.add_callback(drop_io_state_callback)
173 self.ioloop.add_callback(drop_io_state_callback)
174
174
175
175
176 class XReqSocketChannel(ZmqSocketChannel):
176 class XReqSocketChannel(ZmqSocketChannel):
177 """The XREQ channel for issues request/replies to the kernel.
177 """The XREQ channel for issues request/replies to the kernel.
178 """
178 """
179
179
180 command_queue = None
180 command_queue = None
181
181
182 def __init__(self, context, session, address):
182 def __init__(self, context, session, address):
183 super(XReqSocketChannel, self).__init__(context, session, address)
183 super(XReqSocketChannel, self).__init__(context, session, address)
184 self.command_queue = Queue()
184 self.command_queue = Queue()
185 self.ioloop = ioloop.IOLoop()
185 self.ioloop = ioloop.IOLoop()
186
186
187 def run(self):
187 def run(self):
188 """The thread's main activity. Call start() instead."""
188 """The thread's main activity. Call start() instead."""
189 self.socket = self.context.socket(zmq.XREQ)
189 self.socket = self.context.socket(zmq.XREQ)
190 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
190 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
191 self.socket.connect('tcp://%s:%i' % self.address)
191 self.socket.connect('tcp://%s:%i' % self.address)
192 self.iostate = POLLERR|POLLIN
192 self.iostate = POLLERR|POLLIN
193 self.ioloop.add_handler(self.socket, self._handle_events,
193 self.ioloop.add_handler(self.socket, self._handle_events,
194 self.iostate)
194 self.iostate)
195 self._run_loop()
195 self._run_loop()
196
196
197 def stop(self):
197 def stop(self):
198 self.ioloop.stop()
198 self.ioloop.stop()
199 super(XReqSocketChannel, self).stop()
199 super(XReqSocketChannel, self).stop()
200
200
201 def call_handlers(self, msg):
201 def call_handlers(self, msg):
202 """This method is called in the ioloop thread when a message arrives.
202 """This method is called in the ioloop thread when a message arrives.
203
203
204 Subclasses should override this method to handle incoming messages.
204 Subclasses should override this method to handle incoming messages.
205 It is important to remember that this method is called in the thread
205 It is important to remember that this method is called in the thread
206 so that some logic must be done to ensure that the application leve
206 so that some logic must be done to ensure that the application leve
207 handlers are called in the application thread.
207 handlers are called in the application thread.
208 """
208 """
209 raise NotImplementedError('call_handlers must be defined in a subclass.')
209 raise NotImplementedError('call_handlers must be defined in a subclass.')
210
210
211 def execute(self, code, silent=False,
211 def execute(self, code, silent=False,
212 user_variables=None, user_expressions=None):
212 user_variables=None, user_expressions=None):
213 """Execute code in the kernel.
213 """Execute code in the kernel.
214
214
215 Parameters
215 Parameters
216 ----------
216 ----------
217 code : str
217 code : str
218 A string of Python code.
218 A string of Python code.
219
219
220 silent : bool, optional (default False)
220 silent : bool, optional (default False)
221 If set, the kernel will execute the code as quietly possible.
221 If set, the kernel will execute the code as quietly possible.
222
222
223 user_variables : list, optional
223 user_variables : list, optional
224 A list of variable names to pull from the user's namespace. They
224 A list of variable names to pull from the user's namespace. They
225 will come back as a dict with these names as keys and their
225 will come back as a dict with these names as keys and their
226 :func:`repr` as values.
226 :func:`repr` as values.
227
227
228 user_expressions : dict, optional
228 user_expressions : dict, optional
229 A dict with string keys and to pull from the user's
229 A dict with string keys and to pull from the user's
230 namespace. They will come back as a dict with these names as keys
230 namespace. They will come back as a dict with these names as keys
231 and their :func:`repr` as values.
231 and their :func:`repr` as values.
232
232
233 Returns
233 Returns
234 -------
234 -------
235 The msg_id of the message sent.
235 The msg_id of the message sent.
236 """
236 """
237 if user_variables is None:
237 if user_variables is None:
238 user_variables = []
238 user_variables = []
239 if user_expressions is None:
239 if user_expressions is None:
240 user_expressions = {}
240 user_expressions = {}
241
241
242 # Don't waste network traffic if inputs are invalid
242 # Don't waste network traffic if inputs are invalid
243 if not isinstance(code, basestring):
243 if not isinstance(code, basestring):
244 raise ValueError('code %r must be a string' % code)
244 raise ValueError('code %r must be a string' % code)
245 validate_string_list(user_variables)
245 validate_string_list(user_variables)
246 validate_string_dict(user_expressions)
246 validate_string_dict(user_expressions)
247
247
248 # Create class for content/msg creation. Related to, but possibly
248 # Create class for content/msg creation. Related to, but possibly
249 # not in Session.
249 # not in Session.
250 content = dict(code=code, silent=silent,
250 content = dict(code=code, silent=silent,
251 user_variables=user_variables,
251 user_variables=user_variables,
252 user_expressions=user_expressions)
252 user_expressions=user_expressions)
253 msg = self.session.msg('execute_request', content)
253 msg = self.session.msg('execute_request', content)
254 self._queue_request(msg)
254 self._queue_request(msg)
255 return msg['header']['msg_id']
255 return msg['header']['msg_id']
256
256
257 def complete(self, text, line, cursor_pos, block=None):
257 def complete(self, text, line, cursor_pos, block=None):
258 """Tab complete text in the kernel's namespace.
258 """Tab complete text in the kernel's namespace.
259
259
260 Parameters
260 Parameters
261 ----------
261 ----------
262 text : str
262 text : str
263 The text to complete.
263 The text to complete.
264 line : str
264 line : str
265 The full line of text that is the surrounding context for the
265 The full line of text that is the surrounding context for the
266 text to complete.
266 text to complete.
267 cursor_pos : int
267 cursor_pos : int
268 The position of the cursor in the line where the completion was
268 The position of the cursor in the line where the completion was
269 requested.
269 requested.
270 block : str, optional
270 block : str, optional
271 The full block of code in which the completion is being requested.
271 The full block of code in which the completion is being requested.
272
272
273 Returns
273 Returns
274 -------
274 -------
275 The msg_id of the message sent.
275 The msg_id of the message sent.
276 """
276 """
277 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
277 content = dict(text=text, line=line, block=block, cursor_pos=cursor_pos)
278 msg = self.session.msg('complete_request', content)
278 msg = self.session.msg('complete_request', content)
279 self._queue_request(msg)
279 self._queue_request(msg)
280 return msg['header']['msg_id']
280 return msg['header']['msg_id']
281
281
282 def object_info(self, oname):
282 def object_info(self, oname):
283 """Get metadata information about an object.
283 """Get metadata information about an object.
284
284
285 Parameters
285 Parameters
286 ----------
286 ----------
287 oname : str
287 oname : str
288 A string specifying the object name.
288 A string specifying the object name.
289
289
290 Returns
290 Returns
291 -------
291 -------
292 The msg_id of the message sent.
292 The msg_id of the message sent.
293 """
293 """
294 content = dict(oname=oname)
294 content = dict(oname=oname)
295 msg = self.session.msg('object_info_request', content)
295 msg = self.session.msg('object_info_request', content)
296 self._queue_request(msg)
296 self._queue_request(msg)
297 return msg['header']['msg_id']
297 return msg['header']['msg_id']
298
298
299 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
299 def history(self, raw=True, output=False, hist_access_type='range', **kwargs):
300 """Get entries from the history list.
300 """Get entries from the history list.
301
301
302 Parameters
302 Parameters
303 ----------
303 ----------
304 raw : bool
304 raw : bool
305 If True, return the raw input.
305 If True, return the raw input.
306 output : bool
306 output : bool
307 If True, then return the output as well.
307 If True, then return the output as well.
308 hist_access_type : str
308 hist_access_type : str
309 'range' (fill in session, start and stop params), 'tail' (fill in n)
309 'range' (fill in session, start and stop params), 'tail' (fill in n)
310 or 'search' (fill in pattern param).
310 or 'search' (fill in pattern param).
311
311
312 session : int
312 session : int
313 For a range request, the session from which to get lines. Session
313 For a range request, the session from which to get lines. Session
314 numbers are positive integers; negative ones count back from the
314 numbers are positive integers; negative ones count back from the
315 current session.
315 current session.
316 start : int
316 start : int
317 The first line number of a history range.
317 The first line number of a history range.
318 stop : int
318 stop : int
319 The final (excluded) line number of a history range.
319 The final (excluded) line number of a history range.
320
320
321 n : int
321 n : int
322 The number of lines of history to get for a tail request.
322 The number of lines of history to get for a tail request.
323
323
324 pattern : str
324 pattern : str
325 The glob-syntax pattern for a search request.
325 The glob-syntax pattern for a search request.
326
326
327 Returns
327 Returns
328 -------
328 -------
329 The msg_id of the message sent.
329 The msg_id of the message sent.
330 """
330 """
331 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
331 content = dict(raw=raw, output=output, hist_access_type=hist_access_type,
332 **kwargs)
332 **kwargs)
333 msg = self.session.msg('history_request', content)
333 msg = self.session.msg('history_request', content)
334 self._queue_request(msg)
334 self._queue_request(msg)
335 return msg['header']['msg_id']
335 return msg['header']['msg_id']
336
336
337 def shutdown(self, restart=False):
337 def shutdown(self, restart=False):
338 """Request an immediate kernel shutdown.
338 """Request an immediate kernel shutdown.
339
339
340 Upon receipt of the (empty) reply, client code can safely assume that
340 Upon receipt of the (empty) reply, client code can safely assume that
341 the kernel has shut down and it's safe to forcefully terminate it if
341 the kernel has shut down and it's safe to forcefully terminate it if
342 it's still alive.
342 it's still alive.
343
343
344 The kernel will send the reply via a function registered with Python's
344 The kernel will send the reply via a function registered with Python's
345 atexit module, ensuring it's truly done as the kernel is done with all
345 atexit module, ensuring it's truly done as the kernel is done with all
346 normal operation.
346 normal operation.
347 """
347 """
348 # Send quit message to kernel. Once we implement kernel-side setattr,
348 # Send quit message to kernel. Once we implement kernel-side setattr,
349 # this should probably be done that way, but for now this will do.
349 # this should probably be done that way, but for now this will do.
350 msg = self.session.msg('shutdown_request', {'restart':restart})
350 msg = self.session.msg('shutdown_request', {'restart':restart})
351 self._queue_request(msg)
351 self._queue_request(msg)
352 return msg['header']['msg_id']
352 return msg['header']['msg_id']
353
353
354 def _handle_events(self, socket, events):
354 def _handle_events(self, socket, events):
355 if events & POLLERR:
355 if events & POLLERR:
356 self._handle_err()
356 self._handle_err()
357 if events & POLLOUT:
357 if events & POLLOUT:
358 self._handle_send()
358 self._handle_send()
359 if events & POLLIN:
359 if events & POLLIN:
360 self._handle_recv()
360 self._handle_recv()
361
361
362 def _handle_recv(self):
362 def _handle_recv(self):
363 ident,msg = self.session.recv(self.socket, 0)
363 ident,msg = self.session.recv(self.socket, 0)
364 self.call_handlers(msg)
364 self.call_handlers(msg)
365
365
366 def _handle_send(self):
366 def _handle_send(self):
367 try:
367 try:
368 msg = self.command_queue.get(False)
368 msg = self.command_queue.get(False)
369 except Empty:
369 except Empty:
370 pass
370 pass
371 else:
371 else:
372 self.session.send(self.socket,msg)
372 self.session.send(self.socket,msg)
373 if self.command_queue.empty():
373 if self.command_queue.empty():
374 self.drop_io_state(POLLOUT)
374 self.drop_io_state(POLLOUT)
375
375
376 def _handle_err(self):
376 def _handle_err(self):
377 # We don't want to let this go silently, so eventually we should log.
377 # We don't want to let this go silently, so eventually we should log.
378 raise zmq.ZMQError()
378 raise zmq.ZMQError()
379
379
380 def _queue_request(self, msg):
380 def _queue_request(self, msg):
381 self.command_queue.put(msg)
381 self.command_queue.put(msg)
382 self.add_io_state(POLLOUT)
382 self.add_io_state(POLLOUT)
383
383
384
384
385 class SubSocketChannel(ZmqSocketChannel):
385 class SubSocketChannel(ZmqSocketChannel):
386 """The SUB channel which listens for messages that the kernel publishes.
386 """The SUB channel which listens for messages that the kernel publishes.
387 """
387 """
388
388
389 def __init__(self, context, session, address):
389 def __init__(self, context, session, address):
390 super(SubSocketChannel, self).__init__(context, session, address)
390 super(SubSocketChannel, self).__init__(context, session, address)
391 self.ioloop = ioloop.IOLoop()
391 self.ioloop = ioloop.IOLoop()
392
392
393 def run(self):
393 def run(self):
394 """The thread's main activity. Call start() instead."""
394 """The thread's main activity. Call start() instead."""
395 self.socket = self.context.socket(zmq.SUB)
395 self.socket = self.context.socket(zmq.SUB)
396 self.socket.setsockopt(zmq.SUBSCRIBE,'')
396 self.socket.setsockopt(zmq.SUBSCRIBE,'')
397 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
397 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
398 self.socket.connect('tcp://%s:%i' % self.address)
398 self.socket.connect('tcp://%s:%i' % self.address)
399 self.iostate = POLLIN|POLLERR
399 self.iostate = POLLIN|POLLERR
400 self.ioloop.add_handler(self.socket, self._handle_events,
400 self.ioloop.add_handler(self.socket, self._handle_events,
401 self.iostate)
401 self.iostate)
402 self._run_loop()
402 self._run_loop()
403
403
404 def stop(self):
404 def stop(self):
405 self.ioloop.stop()
405 self.ioloop.stop()
406 super(SubSocketChannel, self).stop()
406 super(SubSocketChannel, self).stop()
407
407
408 def call_handlers(self, msg):
408 def call_handlers(self, msg):
409 """This method is called in the ioloop thread when a message arrives.
409 """This method is called in the ioloop thread when a message arrives.
410
410
411 Subclasses should override this method to handle incoming messages.
411 Subclasses should override this method to handle incoming messages.
412 It is important to remember that this method is called in the thread
412 It is important to remember that this method is called in the thread
413 so that some logic must be done to ensure that the application leve
413 so that some logic must be done to ensure that the application leve
414 handlers are called in the application thread.
414 handlers are called in the application thread.
415 """
415 """
416 raise NotImplementedError('call_handlers must be defined in a subclass.')
416 raise NotImplementedError('call_handlers must be defined in a subclass.')
417
417
418 def flush(self, timeout=1.0):
418 def flush(self, timeout=1.0):
419 """Immediately processes all pending messages on the SUB channel.
419 """Immediately processes all pending messages on the SUB channel.
420
420
421 Callers should use this method to ensure that :method:`call_handlers`
421 Callers should use this method to ensure that :method:`call_handlers`
422 has been called for all messages that have been received on the
422 has been called for all messages that have been received on the
423 0MQ SUB socket of this channel.
423 0MQ SUB socket of this channel.
424
424
425 This method is thread safe.
425 This method is thread safe.
426
426
427 Parameters
427 Parameters
428 ----------
428 ----------
429 timeout : float, optional
429 timeout : float, optional
430 The maximum amount of time to spend flushing, in seconds. The
430 The maximum amount of time to spend flushing, in seconds. The
431 default is one second.
431 default is one second.
432 """
432 """
433 # We do the IOLoop callback process twice to ensure that the IOLoop
433 # We do the IOLoop callback process twice to ensure that the IOLoop
434 # gets to perform at least one full poll.
434 # gets to perform at least one full poll.
435 stop_time = time.time() + timeout
435 stop_time = time.time() + timeout
436 for i in xrange(2):
436 for i in xrange(2):
437 self._flushed = False
437 self._flushed = False
438 self.ioloop.add_callback(self._flush)
438 self.ioloop.add_callback(self._flush)
439 while not self._flushed and time.time() < stop_time:
439 while not self._flushed and time.time() < stop_time:
440 time.sleep(0.01)
440 time.sleep(0.01)
441
441
442 def _handle_events(self, socket, events):
442 def _handle_events(self, socket, events):
443 # Turn on and off POLLOUT depending on if we have made a request
443 # Turn on and off POLLOUT depending on if we have made a request
444 if events & POLLERR:
444 if events & POLLERR:
445 self._handle_err()
445 self._handle_err()
446 if events & POLLIN:
446 if events & POLLIN:
447 self._handle_recv()
447 self._handle_recv()
448
448
449 def _handle_err(self):
449 def _handle_err(self):
450 # We don't want to let this go silently, so eventually we should log.
450 # We don't want to let this go silently, so eventually we should log.
451 raise zmq.ZMQError()
451 raise zmq.ZMQError()
452
452
453 def _handle_recv(self):
453 def _handle_recv(self):
454 # Get all of the messages we can
454 # Get all of the messages we can
455 while True:
455 while True:
456 try:
456 try:
457 ident,msg = self.session.recv(self.socket)
457 ident,msg = self.session.recv(self.socket)
458 except zmq.ZMQError:
458 except zmq.ZMQError:
459 # Check the errno?
459 # Check the errno?
460 # Will this trigger POLLERR?
460 # Will this trigger POLLERR?
461 break
461 break
462 else:
462 else:
463 if msg is None:
463 if msg is None:
464 break
464 break
465 self.call_handlers(msg)
465 self.call_handlers(msg)
466
466
467 def _flush(self):
467 def _flush(self):
468 """Callback for :method:`self.flush`."""
468 """Callback for :method:`self.flush`."""
469 self._flushed = True
469 self._flushed = True
470
470
471
471
472 class RepSocketChannel(ZmqSocketChannel):
472 class RepSocketChannel(ZmqSocketChannel):
473 """A reply channel to handle raw_input requests that the kernel makes."""
473 """A reply channel to handle raw_input requests that the kernel makes."""
474
474
475 msg_queue = None
475 msg_queue = None
476
476
477 def __init__(self, context, session, address):
477 def __init__(self, context, session, address):
478 super(RepSocketChannel, self).__init__(context, session, address)
478 super(RepSocketChannel, self).__init__(context, session, address)
479 self.ioloop = ioloop.IOLoop()
479 self.ioloop = ioloop.IOLoop()
480 self.msg_queue = Queue()
480 self.msg_queue = Queue()
481
481
482 def run(self):
482 def run(self):
483 """The thread's main activity. Call start() instead."""
483 """The thread's main activity. Call start() instead."""
484 self.socket = self.context.socket(zmq.XREQ)
484 self.socket = self.context.socket(zmq.XREQ)
485 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
485 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
486 self.socket.connect('tcp://%s:%i' % self.address)
486 self.socket.connect('tcp://%s:%i' % self.address)
487 self.iostate = POLLERR|POLLIN
487 self.iostate = POLLERR|POLLIN
488 self.ioloop.add_handler(self.socket, self._handle_events,
488 self.ioloop.add_handler(self.socket, self._handle_events,
489 self.iostate)
489 self.iostate)
490 self._run_loop()
490 self._run_loop()
491
491
492 def stop(self):
492 def stop(self):
493 self.ioloop.stop()
493 self.ioloop.stop()
494 super(RepSocketChannel, self).stop()
494 super(RepSocketChannel, self).stop()
495
495
496 def call_handlers(self, msg):
496 def call_handlers(self, msg):
497 """This method is called in the ioloop thread when a message arrives.
497 """This method is called in the ioloop thread when a message arrives.
498
498
499 Subclasses should override this method to handle incoming messages.
499 Subclasses should override this method to handle incoming messages.
500 It is important to remember that this method is called in the thread
500 It is important to remember that this method is called in the thread
501 so that some logic must be done to ensure that the application leve
501 so that some logic must be done to ensure that the application leve
502 handlers are called in the application thread.
502 handlers are called in the application thread.
503 """
503 """
504 raise NotImplementedError('call_handlers must be defined in a subclass.')
504 raise NotImplementedError('call_handlers must be defined in a subclass.')
505
505
506 def input(self, string):
506 def input(self, string):
507 """Send a string of raw input to the kernel."""
507 """Send a string of raw input to the kernel."""
508 content = dict(value=string)
508 content = dict(value=string)
509 msg = self.session.msg('input_reply', content)
509 msg = self.session.msg('input_reply', content)
510 self._queue_reply(msg)
510 self._queue_reply(msg)
511
511
512 def _handle_events(self, socket, events):
512 def _handle_events(self, socket, events):
513 if events & POLLERR:
513 if events & POLLERR:
514 self._handle_err()
514 self._handle_err()
515 if events & POLLOUT:
515 if events & POLLOUT:
516 self._handle_send()
516 self._handle_send()
517 if events & POLLIN:
517 if events & POLLIN:
518 self._handle_recv()
518 self._handle_recv()
519
519
520 def _handle_recv(self):
520 def _handle_recv(self):
521 ident,msg = self.session.recv(self.socket, 0)
521 ident,msg = self.session.recv(self.socket, 0)
522 self.call_handlers(msg)
522 self.call_handlers(msg)
523
523
524 def _handle_send(self):
524 def _handle_send(self):
525 try:
525 try:
526 msg = self.msg_queue.get(False)
526 msg = self.msg_queue.get(False)
527 except Empty:
527 except Empty:
528 pass
528 pass
529 else:
529 else:
530 self.session.send(self.socket,msg)
530 self.session.send(self.socket,msg)
531 if self.msg_queue.empty():
531 if self.msg_queue.empty():
532 self.drop_io_state(POLLOUT)
532 self.drop_io_state(POLLOUT)
533
533
534 def _handle_err(self):
534 def _handle_err(self):
535 # We don't want to let this go silently, so eventually we should log.
535 # We don't want to let this go silently, so eventually we should log.
536 raise zmq.ZMQError()
536 raise zmq.ZMQError()
537
537
538 def _queue_reply(self, msg):
538 def _queue_reply(self, msg):
539 self.msg_queue.put(msg)
539 self.msg_queue.put(msg)
540 self.add_io_state(POLLOUT)
540 self.add_io_state(POLLOUT)
541
541
542
542
543 class HBSocketChannel(ZmqSocketChannel):
543 class HBSocketChannel(ZmqSocketChannel):
544 """The heartbeat channel which monitors the kernel heartbeat.
544 """The heartbeat channel which monitors the kernel heartbeat.
545
545
546 Note that the heartbeat channel is paused by default. As long as you start
546 Note that the heartbeat channel is paused by default. As long as you start
547 this channel, the kernel manager will ensure that it is paused and un-paused
547 this channel, the kernel manager will ensure that it is paused and un-paused
548 as appropriate.
548 as appropriate.
549 """
549 """
550
550
551 time_to_dead = 3.0
551 time_to_dead = 3.0
552 socket = None
552 socket = None
553 poller = None
553 poller = None
554 _running = None
554 _running = None
555 _pause = None
555 _pause = None
556
556
557 def __init__(self, context, session, address):
557 def __init__(self, context, session, address):
558 super(HBSocketChannel, self).__init__(context, session, address)
558 super(HBSocketChannel, self).__init__(context, session, address)
559 self._running = False
559 self._running = False
560 self._pause = True
560 self._pause = True
561
561
562 def _create_socket(self):
562 def _create_socket(self):
563 self.socket = self.context.socket(zmq.REQ)
563 self.socket = self.context.socket(zmq.REQ)
564 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
564 self.socket.setsockopt(zmq.IDENTITY, self.session.session)
565 self.socket.connect('tcp://%s:%i' % self.address)
565 self.socket.connect('tcp://%s:%i' % self.address)
566 self.poller = zmq.Poller()
566 self.poller = zmq.Poller()
567 self.poller.register(self.socket, zmq.POLLIN)
567 self.poller.register(self.socket, zmq.POLLIN)
568
568
569 def run(self):
569 def run(self):
570 """The thread's main activity. Call start() instead."""
570 """The thread's main activity. Call start() instead."""
571 self._create_socket()
571 self._create_socket()
572 self._running = True
572 self._running = True
573 while self._running:
573 while self._running:
574 if self._pause:
574 if self._pause:
575 time.sleep(self.time_to_dead)
575 time.sleep(self.time_to_dead)
576 else:
576 else:
577 since_last_heartbeat = 0.0
577 since_last_heartbeat = 0.0
578 request_time = time.time()
578 request_time = time.time()
579 try:
579 try:
580 #io.rprint('Ping from HB channel') # dbg
580 #io.rprint('Ping from HB channel') # dbg
581 self.socket.send(b'ping')
581 self.socket.send(b'ping')
582 except zmq.ZMQError, e:
582 except zmq.ZMQError, e:
583 #io.rprint('*** HB Error:', e) # dbg
583 #io.rprint('*** HB Error:', e) # dbg
584 if e.errno == zmq.EFSM:
584 if e.errno == zmq.EFSM:
585 #io.rprint('sleep...', self.time_to_dead) # dbg
585 #io.rprint('sleep...', self.time_to_dead) # dbg
586 time.sleep(self.time_to_dead)
586 time.sleep(self.time_to_dead)
587 self._create_socket()
587 self._create_socket()
588 else:
588 else:
589 raise
589 raise
590 else:
590 else:
591 while True:
591 while True:
592 try:
592 try:
593 self.socket.recv(zmq.NOBLOCK)
593 self.socket.recv(zmq.NOBLOCK)
594 except zmq.ZMQError, e:
594 except zmq.ZMQError, e:
595 #io.rprint('*** HB Error 2:', e) # dbg
595 #io.rprint('*** HB Error 2:', e) # dbg
596 if e.errno == zmq.EAGAIN:
596 if e.errno == zmq.EAGAIN:
597 before_poll = time.time()
597 before_poll = time.time()
598 until_dead = self.time_to_dead - (before_poll -
598 until_dead = self.time_to_dead - (before_poll -
599 request_time)
599 request_time)
600
600
601 # When the return value of poll() is an empty
601 # When the return value of poll() is an empty
602 # list, that is when things have gone wrong
602 # list, that is when things have gone wrong
603 # (zeromq bug). As long as it is not an empty
603 # (zeromq bug). As long as it is not an empty
604 # list, poll is working correctly even if it
604 # list, poll is working correctly even if it
605 # returns quickly. Note: poll timeout is in
605 # returns quickly. Note: poll timeout is in
606 # milliseconds.
606 # milliseconds.
607 if until_dead > 0.0:
607 if until_dead > 0.0:
608 while True:
608 while True:
609 try:
609 try:
610 self.poller.poll(1000 * until_dead)
610 self.poller.poll(1000 * until_dead)
611 except zmq.ZMQError as e:
611 except zmq.ZMQError as e:
612 if e.errno == errno.EINTR:
612 if e.errno == errno.EINTR:
613 continue
613 continue
614 else:
614 else:
615 raise
615 raise
616 else:
616 else:
617 break
617 break
618
618
619 since_last_heartbeat = time.time()-request_time
619 since_last_heartbeat = time.time()-request_time
620 if since_last_heartbeat > self.time_to_dead:
620 if since_last_heartbeat > self.time_to_dead:
621 self.call_handlers(since_last_heartbeat)
621 self.call_handlers(since_last_heartbeat)
622 break
622 break
623 else:
623 else:
624 # FIXME: We should probably log this instead.
624 # FIXME: We should probably log this instead.
625 raise
625 raise
626 else:
626 else:
627 until_dead = self.time_to_dead - (time.time() -
627 until_dead = self.time_to_dead - (time.time() -
628 request_time)
628 request_time)
629 if until_dead > 0.0:
629 if until_dead > 0.0:
630 #io.rprint('sleep...', self.time_to_dead) # dbg
630 #io.rprint('sleep...', self.time_to_dead) # dbg
631 time.sleep(until_dead)
631 time.sleep(until_dead)
632 break
632 break
633
633
634 def pause(self):
634 def pause(self):
635 """Pause the heartbeat."""
635 """Pause the heartbeat."""
636 self._pause = True
636 self._pause = True
637
637
638 def unpause(self):
638 def unpause(self):
639 """Unpause the heartbeat."""
639 """Unpause the heartbeat."""
640 self._pause = False
640 self._pause = False
641
641
642 def is_beating(self):
642 def is_beating(self):
643 """Is the heartbeat running and not paused."""
643 """Is the heartbeat running and not paused."""
644 if self.is_alive() and not self._pause:
644 if self.is_alive() and not self._pause:
645 return True
645 return True
646 else:
646 else:
647 return False
647 return False
648
648
649 def stop(self):
649 def stop(self):
650 self._running = False
650 self._running = False
651 super(HBSocketChannel, self).stop()
651 super(HBSocketChannel, self).stop()
652
652
653 def call_handlers(self, since_last_heartbeat):
653 def call_handlers(self, since_last_heartbeat):
654 """This method is called in the ioloop thread when a message arrives.
654 """This method is called in the ioloop thread when a message arrives.
655
655
656 Subclasses should override this method to handle incoming messages.
656 Subclasses should override this method to handle incoming messages.
657 It is important to remember that this method is called in the thread
657 It is important to remember that this method is called in the thread
658 so that some logic must be done to ensure that the application leve
658 so that some logic must be done to ensure that the application leve
659 handlers are called in the application thread.
659 handlers are called in the application thread.
660 """
660 """
661 raise NotImplementedError('call_handlers must be defined in a subclass.')
661 raise NotImplementedError('call_handlers must be defined in a subclass.')
662
662
663
663
664 #-----------------------------------------------------------------------------
664 #-----------------------------------------------------------------------------
665 # Main kernel manager class
665 # Main kernel manager class
666 #-----------------------------------------------------------------------------
666 #-----------------------------------------------------------------------------
667
667
668 class KernelManager(HasTraits):
668 class KernelManager(HasTraits):
669 """ Manages a kernel for a frontend.
669 """ Manages a kernel for a frontend.
670
670
671 The SUB channel is for the frontend to receive messages published by the
671 The SUB channel is for the frontend to receive messages published by the
672 kernel.
672 kernel.
673
673
674 The REQ channel is for the frontend to make requests of the kernel.
674 The REQ channel is for the frontend to make requests of the kernel.
675
675
676 The REP channel is for the kernel to request stdin (raw_input) from the
676 The REP channel is for the kernel to request stdin (raw_input) from the
677 frontend.
677 frontend.
678 """
678 """
679 # The PyZMQ Context to use for communication with the kernel.
679 # The PyZMQ Context to use for communication with the kernel.
680 context = Instance(zmq.Context,(),{})
680 context = Instance(zmq.Context,(),{})
681
681
682 # The Session to use for communication with the kernel.
682 # The Session to use for communication with the kernel.
683 session = Instance(Session,(),{})
683 session = Instance(Session,(),{})
684
684
685 # The kernel process with which the KernelManager is communicating.
685 # The kernel process with which the KernelManager is communicating.
686 kernel = Instance(Popen)
686 kernel = Instance(Popen)
687
687
688 # The addresses for the communication channels.
688 # The addresses for the communication channels.
689 xreq_address = TCPAddress((LOCALHOST, 0))
689 xreq_address = TCPAddress((LOCALHOST, 0))
690 sub_address = TCPAddress((LOCALHOST, 0))
690 sub_address = TCPAddress((LOCALHOST, 0))
691 rep_address = TCPAddress((LOCALHOST, 0))
691 rep_address = TCPAddress((LOCALHOST, 0))
692 hb_address = TCPAddress((LOCALHOST, 0))
692 hb_address = TCPAddress((LOCALHOST, 0))
693
693
694 # The classes to use for the various channels.
694 # The classes to use for the various channels.
695 xreq_channel_class = Type(XReqSocketChannel)
695 xreq_channel_class = Type(XReqSocketChannel)
696 sub_channel_class = Type(SubSocketChannel)
696 sub_channel_class = Type(SubSocketChannel)
697 rep_channel_class = Type(RepSocketChannel)
697 rep_channel_class = Type(RepSocketChannel)
698 hb_channel_class = Type(HBSocketChannel)
698 hb_channel_class = Type(HBSocketChannel)
699
699
700 # Protected traits.
700 # Protected traits.
701 _launch_args = Any
701 _launch_args = Any
702 _xreq_channel = Any
702 _xreq_channel = Any
703 _sub_channel = Any
703 _sub_channel = Any
704 _rep_channel = Any
704 _rep_channel = Any
705 _hb_channel = Any
705 _hb_channel = Any
706
706
707 def __init__(self, **kwargs):
707 def __init__(self, **kwargs):
708 super(KernelManager, self).__init__(**kwargs)
708 super(KernelManager, self).__init__(**kwargs)
709 # Uncomment this to try closing the context.
709 # Uncomment this to try closing the context.
710 # atexit.register(self.context.close)
710 # atexit.register(self.context.close)
711
711
712 #--------------------------------------------------------------------------
712 #--------------------------------------------------------------------------
713 # Channel management methods:
713 # Channel management methods:
714 #--------------------------------------------------------------------------
714 #--------------------------------------------------------------------------
715
715
716 def start_channels(self, xreq=True, sub=True, rep=True, hb=True):
716 def start_channels(self, xreq=True, sub=True, rep=True, hb=True):
717 """Starts the channels for this kernel.
717 """Starts the channels for this kernel.
718
718
719 This will create the channels if they do not exist and then start
719 This will create the channels if they do not exist and then start
720 them. If port numbers of 0 are being used (random ports) then you
720 them. If port numbers of 0 are being used (random ports) then you
721 must first call :method:`start_kernel`. If the channels have been
721 must first call :method:`start_kernel`. If the channels have been
722 stopped and you call this, :class:`RuntimeError` will be raised.
722 stopped and you call this, :class:`RuntimeError` will be raised.
723 """
723 """
724 if xreq:
724 if xreq:
725 self.xreq_channel.start()
725 self.xreq_channel.start()
726 if sub:
726 if sub:
727 self.sub_channel.start()
727 self.sub_channel.start()
728 if rep:
728 if rep:
729 self.rep_channel.start()
729 self.rep_channel.start()
730 if hb:
730 if hb:
731 self.hb_channel.start()
731 self.hb_channel.start()
732
732
733 def stop_channels(self):
733 def stop_channels(self):
734 """Stops all the running channels for this kernel.
734 """Stops all the running channels for this kernel.
735 """
735 """
736 if self.xreq_channel.is_alive():
736 if self.xreq_channel.is_alive():
737 self.xreq_channel.stop()
737 self.xreq_channel.stop()
738 if self.sub_channel.is_alive():
738 if self.sub_channel.is_alive():
739 self.sub_channel.stop()
739 self.sub_channel.stop()
740 if self.rep_channel.is_alive():
740 if self.rep_channel.is_alive():
741 self.rep_channel.stop()
741 self.rep_channel.stop()
742 if self.hb_channel.is_alive():
742 if self.hb_channel.is_alive():
743 self.hb_channel.stop()
743 self.hb_channel.stop()
744
744
745 @property
745 @property
746 def channels_running(self):
746 def channels_running(self):
747 """Are any of the channels created and running?"""
747 """Are any of the channels created and running?"""
748 return (self.xreq_channel.is_alive() or self.sub_channel.is_alive() or
748 return (self.xreq_channel.is_alive() or self.sub_channel.is_alive() or
749 self.rep_channel.is_alive() or self.hb_channel.is_alive())
749 self.rep_channel.is_alive() or self.hb_channel.is_alive())
750
750
751 #--------------------------------------------------------------------------
751 #--------------------------------------------------------------------------
752 # Kernel process management methods:
752 # Kernel process management methods:
753 #--------------------------------------------------------------------------
753 #--------------------------------------------------------------------------
754
754
755 def start_kernel(self, **kw):
755 def start_kernel(self, **kw):
756 """Starts a kernel process and configures the manager to use it.
756 """Starts a kernel process and configures the manager to use it.
757
757
758 If random ports (port=0) are being used, this method must be called
758 If random ports (port=0) are being used, this method must be called
759 before the channels are created.
759 before the channels are created.
760
760
761 Parameters:
761 Parameters:
762 -----------
762 -----------
763 ipython : bool, optional (default True)
763 ipython : bool, optional (default True)
764 Whether to use an IPython kernel instead of a plain Python kernel.
764 Whether to use an IPython kernel instead of a plain Python kernel.
765
765
766 **kw : optional
766 **kw : optional
767 See respective options for IPython and Python kernels.
767 See respective options for IPython and Python kernels.
768 """
768 """
769 xreq, sub, rep, hb = self.xreq_address, self.sub_address, \
769 xreq, sub, rep, hb = self.xreq_address, self.sub_address, \
770 self.rep_address, self.hb_address
770 self.rep_address, self.hb_address
771 if xreq[0] not in LOCAL_IPS or sub[0] not in LOCAL_IPS or \
771 if xreq[0] not in LOCAL_IPS or sub[0] not in LOCAL_IPS or \
772 rep[0] not in LOCAL_IPS or hb[0] not in LOCAL_IPS:
772 rep[0] not in LOCAL_IPS or hb[0] not in LOCAL_IPS:
773 raise RuntimeError("Can only launch a kernel on a local interface. "
773 raise RuntimeError("Can only launch a kernel on a local interface. "
774 "Make sure that the '*_address' attributes are "
774 "Make sure that the '*_address' attributes are "
775 "configured properly. "
775 "configured properly. "
776 "Currently valid addresses are: %s"%LOCAL_IPS
776 "Currently valid addresses are: %s"%LOCAL_IPS
777 )
777 )
778
778
779 self._launch_args = kw.copy()
779 self._launch_args = kw.copy()
780 if kw.pop('ipython', True):
780 if kw.pop('ipython', True):
781 from ipkernel import launch_kernel
781 from ipkernel import launch_kernel
782 else:
782 else:
783 from pykernel import launch_kernel
783 from pykernel import launch_kernel
784 self.kernel, xrep, pub, req, _hb = launch_kernel(
784 self.kernel, xrep, pub, req, _hb = launch_kernel(
785 xrep_port=xreq[1], pub_port=sub[1],
785 shell_port=xreq[1], iopub_port=sub[1],
786 req_port=rep[1], hb_port=hb[1], **kw)
786 stdin_port=rep[1], hb_port=hb[1], **kw)
787 self.xreq_address = (xreq[0], xrep)
787 self.xreq_address = (xreq[0], xrep)
788 self.sub_address = (sub[0], pub)
788 self.sub_address = (sub[0], pub)
789 self.rep_address = (rep[0], req)
789 self.rep_address = (rep[0], req)
790 self.hb_address = (hb[0], _hb)
790 self.hb_address = (hb[0], _hb)
791
791
792 def shutdown_kernel(self, restart=False):
792 def shutdown_kernel(self, restart=False):
793 """ Attempts to the stop the kernel process cleanly. If the kernel
793 """ Attempts to the stop the kernel process cleanly. If the kernel
794 cannot be stopped, it is killed, if possible.
794 cannot be stopped, it is killed, if possible.
795 """
795 """
796 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
796 # FIXME: Shutdown does not work on Windows due to ZMQ errors!
797 if sys.platform == 'win32':
797 if sys.platform == 'win32':
798 self.kill_kernel()
798 self.kill_kernel()
799 return
799 return
800
800
801 # Pause the heart beat channel if it exists.
801 # Pause the heart beat channel if it exists.
802 if self._hb_channel is not None:
802 if self._hb_channel is not None:
803 self._hb_channel.pause()
803 self._hb_channel.pause()
804
804
805 # Don't send any additional kernel kill messages immediately, to give
805 # Don't send any additional kernel kill messages immediately, to give
806 # the kernel a chance to properly execute shutdown actions. Wait for at
806 # the kernel a chance to properly execute shutdown actions. Wait for at
807 # most 1s, checking every 0.1s.
807 # most 1s, checking every 0.1s.
808 self.xreq_channel.shutdown(restart=restart)
808 self.xreq_channel.shutdown(restart=restart)
809 for i in range(10):
809 for i in range(10):
810 if self.is_alive:
810 if self.is_alive:
811 time.sleep(0.1)
811 time.sleep(0.1)
812 else:
812 else:
813 break
813 break
814 else:
814 else:
815 # OK, we've waited long enough.
815 # OK, we've waited long enough.
816 if self.has_kernel:
816 if self.has_kernel:
817 self.kill_kernel()
817 self.kill_kernel()
818
818
819 def restart_kernel(self, now=False, **kw):
819 def restart_kernel(self, now=False, **kw):
820 """Restarts a kernel with the arguments that were used to launch it.
820 """Restarts a kernel with the arguments that were used to launch it.
821
821
822 If the old kernel was launched with random ports, the same ports will be
822 If the old kernel was launched with random ports, the same ports will be
823 used for the new kernel.
823 used for the new kernel.
824
824
825 Parameters
825 Parameters
826 ----------
826 ----------
827 now : bool, optional
827 now : bool, optional
828 If True, the kernel is forcefully restarted *immediately*, without
828 If True, the kernel is forcefully restarted *immediately*, without
829 having a chance to do any cleanup action. Otherwise the kernel is
829 having a chance to do any cleanup action. Otherwise the kernel is
830 given 1s to clean up before a forceful restart is issued.
830 given 1s to clean up before a forceful restart is issued.
831
831
832 In all cases the kernel is restarted, the only difference is whether
832 In all cases the kernel is restarted, the only difference is whether
833 it is given a chance to perform a clean shutdown or not.
833 it is given a chance to perform a clean shutdown or not.
834
834
835 **kw : optional
835 **kw : optional
836 Any options specified here will replace those used to launch the
836 Any options specified here will replace those used to launch the
837 kernel.
837 kernel.
838 """
838 """
839 if self._launch_args is None:
839 if self._launch_args is None:
840 raise RuntimeError("Cannot restart the kernel. "
840 raise RuntimeError("Cannot restart the kernel. "
841 "No previous call to 'start_kernel'.")
841 "No previous call to 'start_kernel'.")
842 else:
842 else:
843 # Stop currently running kernel.
843 # Stop currently running kernel.
844 if self.has_kernel:
844 if self.has_kernel:
845 if now:
845 if now:
846 self.kill_kernel()
846 self.kill_kernel()
847 else:
847 else:
848 self.shutdown_kernel(restart=True)
848 self.shutdown_kernel(restart=True)
849
849
850 # Start new kernel.
850 # Start new kernel.
851 self._launch_args.update(kw)
851 self._launch_args.update(kw)
852 self.start_kernel(**self._launch_args)
852 self.start_kernel(**self._launch_args)
853
853
854 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
854 # FIXME: Messages get dropped in Windows due to probable ZMQ bug
855 # unless there is some delay here.
855 # unless there is some delay here.
856 if sys.platform == 'win32':
856 if sys.platform == 'win32':
857 time.sleep(0.2)
857 time.sleep(0.2)
858
858
859 @property
859 @property
860 def has_kernel(self):
860 def has_kernel(self):
861 """Returns whether a kernel process has been specified for the kernel
861 """Returns whether a kernel process has been specified for the kernel
862 manager.
862 manager.
863 """
863 """
864 return self.kernel is not None
864 return self.kernel is not None
865
865
866 def kill_kernel(self):
866 def kill_kernel(self):
867 """ Kill the running kernel. """
867 """ Kill the running kernel. """
868 if self.has_kernel:
868 if self.has_kernel:
869 # Pause the heart beat channel if it exists.
869 # Pause the heart beat channel if it exists.
870 if self._hb_channel is not None:
870 if self._hb_channel is not None:
871 self._hb_channel.pause()
871 self._hb_channel.pause()
872
872
873 # Attempt to kill the kernel.
873 # Attempt to kill the kernel.
874 try:
874 try:
875 self.kernel.kill()
875 self.kernel.kill()
876 except OSError, e:
876 except OSError, e:
877 # In Windows, we will get an Access Denied error if the process
877 # In Windows, we will get an Access Denied error if the process
878 # has already terminated. Ignore it.
878 # has already terminated. Ignore it.
879 if sys.platform == 'win32':
879 if sys.platform == 'win32':
880 if e.winerror != 5:
880 if e.winerror != 5:
881 raise
881 raise
882 # On Unix, we may get an ESRCH error if the process has already
882 # On Unix, we may get an ESRCH error if the process has already
883 # terminated. Ignore it.
883 # terminated. Ignore it.
884 else:
884 else:
885 from errno import ESRCH
885 from errno import ESRCH
886 if e.errno != ESRCH:
886 if e.errno != ESRCH:
887 raise
887 raise
888 self.kernel = None
888 self.kernel = None
889 else:
889 else:
890 raise RuntimeError("Cannot kill kernel. No kernel is running!")
890 raise RuntimeError("Cannot kill kernel. No kernel is running!")
891
891
892 def interrupt_kernel(self):
892 def interrupt_kernel(self):
893 """ Interrupts the kernel. Unlike ``signal_kernel``, this operation is
893 """ Interrupts the kernel. Unlike ``signal_kernel``, this operation is
894 well supported on all platforms.
894 well supported on all platforms.
895 """
895 """
896 if self.has_kernel:
896 if self.has_kernel:
897 if sys.platform == 'win32':
897 if sys.platform == 'win32':
898 from parentpoller import ParentPollerWindows as Poller
898 from parentpoller import ParentPollerWindows as Poller
899 Poller.send_interrupt(self.kernel.win32_interrupt_event)
899 Poller.send_interrupt(self.kernel.win32_interrupt_event)
900 else:
900 else:
901 self.kernel.send_signal(signal.SIGINT)
901 self.kernel.send_signal(signal.SIGINT)
902 else:
902 else:
903 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
903 raise RuntimeError("Cannot interrupt kernel. No kernel is running!")
904
904
905 def signal_kernel(self, signum):
905 def signal_kernel(self, signum):
906 """ Sends a signal to the kernel. Note that since only SIGTERM is
906 """ Sends a signal to the kernel. Note that since only SIGTERM is
907 supported on Windows, this function is only useful on Unix systems.
907 supported on Windows, this function is only useful on Unix systems.
908 """
908 """
909 if self.has_kernel:
909 if self.has_kernel:
910 self.kernel.send_signal(signum)
910 self.kernel.send_signal(signum)
911 else:
911 else:
912 raise RuntimeError("Cannot signal kernel. No kernel is running!")
912 raise RuntimeError("Cannot signal kernel. No kernel is running!")
913
913
914 @property
914 @property
915 def is_alive(self):
915 def is_alive(self):
916 """Is the kernel process still running?"""
916 """Is the kernel process still running?"""
917 # FIXME: not using a heartbeat means this method is broken for any
917 # FIXME: not using a heartbeat means this method is broken for any
918 # remote kernel, it's only capable of handling local kernels.
918 # remote kernel, it's only capable of handling local kernels.
919 if self.has_kernel:
919 if self.has_kernel:
920 if self.kernel.poll() is None:
920 if self.kernel.poll() is None:
921 return True
921 return True
922 else:
922 else:
923 return False
923 return False
924 else:
924 else:
925 # We didn't start the kernel with this KernelManager so we don't
925 # We didn't start the kernel with this KernelManager so we don't
926 # know if it is running. We should use a heartbeat for this case.
926 # know if it is running. We should use a heartbeat for this case.
927 return True
927 return True
928
928
929 #--------------------------------------------------------------------------
929 #--------------------------------------------------------------------------
930 # Channels used for communication with the kernel:
930 # Channels used for communication with the kernel:
931 #--------------------------------------------------------------------------
931 #--------------------------------------------------------------------------
932
932
933 @property
933 @property
934 def xreq_channel(self):
934 def xreq_channel(self):
935 """Get the REQ socket channel object to make requests of the kernel."""
935 """Get the REQ socket channel object to make requests of the kernel."""
936 if self._xreq_channel is None:
936 if self._xreq_channel is None:
937 self._xreq_channel = self.xreq_channel_class(self.context,
937 self._xreq_channel = self.xreq_channel_class(self.context,
938 self.session,
938 self.session,
939 self.xreq_address)
939 self.xreq_address)
940 return self._xreq_channel
940 return self._xreq_channel
941
941
942 @property
942 @property
943 def sub_channel(self):
943 def sub_channel(self):
944 """Get the SUB socket channel object."""
944 """Get the SUB socket channel object."""
945 if self._sub_channel is None:
945 if self._sub_channel is None:
946 self._sub_channel = self.sub_channel_class(self.context,
946 self._sub_channel = self.sub_channel_class(self.context,
947 self.session,
947 self.session,
948 self.sub_address)
948 self.sub_address)
949 return self._sub_channel
949 return self._sub_channel
950
950
951 @property
951 @property
952 def rep_channel(self):
952 def rep_channel(self):
953 """Get the REP socket channel object to handle stdin (raw_input)."""
953 """Get the REP socket channel object to handle stdin (raw_input)."""
954 if self._rep_channel is None:
954 if self._rep_channel is None:
955 self._rep_channel = self.rep_channel_class(self.context,
955 self._rep_channel = self.rep_channel_class(self.context,
956 self.session,
956 self.session,
957 self.rep_address)
957 self.rep_address)
958 return self._rep_channel
958 return self._rep_channel
959
959
960 @property
960 @property
961 def hb_channel(self):
961 def hb_channel(self):
962 """Get the heartbeat socket channel object to check that the
962 """Get the heartbeat socket channel object to check that the
963 kernel is alive."""
963 kernel is alive."""
964 if self._hb_channel is None:
964 if self._hb_channel is None:
965 self._hb_channel = self.hb_channel_class(self.context,
965 self._hb_channel = self.hb_channel_class(self.context,
966 self.session,
966 self.session,
967 self.hb_address)
967 self.hb_address)
968 return self._hb_channel
968 return self._hb_channel
@@ -1,316 +1,280 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, Float
28 from IPython.utils.traitlets import HasTraits, Instance, Dict, Float
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
31 from session import Session, Message
31 from session import Session, Message
32 from kernelapp import KernelApp
32
33
33 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
34 # Main kernel class
35 # Main kernel class
35 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
36
37
37 class Kernel(HasTraits):
38 class Kernel(HasTraits):
38
39
39 # Private interface
40 # Private interface
40
41
41 # Time to sleep after flushing the stdout/err buffers in each execute
42 # Time to sleep after flushing the stdout/err buffers in each execute
42 # cycle. While this introduces a hard limit on the minimal latency of the
43 # cycle. While this introduces a hard limit on the minimal latency of the
43 # execute cycle, it helps prevent output synchronization problems for
44 # execute cycle, it helps prevent output synchronization problems for
44 # clients.
45 # clients.
45 # Units are in seconds. The minimum zmq latency on local host is probably
46 # Units are in seconds. The minimum zmq latency on local host is probably
46 # ~150 microseconds, set this to 500us for now. We may need to increase it
47 # ~150 microseconds, set this to 500us for now. We may need to increase it
47 # a little if it's not enough after more interactive testing.
48 # a little if it's not enough after more interactive testing.
48 _execute_sleep = Float(0.0005, config=True)
49 _execute_sleep = Float(0.0005, config=True)
49
50
50 # This is a dict of port number that the kernel is listening on. It is set
51 # This is a dict of port number that the kernel is listening on. It is set
51 # by record_ports and used by connect_request.
52 # by record_ports and used by connect_request.
52 _recorded_ports = None
53 _recorded_ports = Dict()
53
54
54 #---------------------------------------------------------------------------
55 #---------------------------------------------------------------------------
55 # Kernel interface
56 # Kernel interface
56 #---------------------------------------------------------------------------
57 #---------------------------------------------------------------------------
57
58
58 session = Instance(Session)
59 session = Instance(Session)
59 reply_socket = Instance('zmq.Socket')
60 shell_socket = Instance('zmq.Socket')
60 pub_socket = Instance('zmq.Socket')
61 iopub_socket = Instance('zmq.Socket')
61 req_socket = Instance('zmq.Socket')
62 stdin_socket = Instance('zmq.Socket')
62
63
63 def __init__(self, **kwargs):
64 def __init__(self, **kwargs):
64 super(Kernel, self).__init__(**kwargs)
65 super(Kernel, self).__init__(**kwargs)
65 self.user_ns = {}
66 self.user_ns = {}
66 self.history = []
67 self.history = []
67 self.compiler = CommandCompiler()
68 self.compiler = CommandCompiler()
68 self.completer = KernelCompleter(self.user_ns)
69 self.completer = KernelCompleter(self.user_ns)
69
70
70 # Build dict of handlers for message types
71 # Build dict of handlers for message types
71 msg_types = [ 'execute_request', 'complete_request',
72 msg_types = [ 'execute_request', 'complete_request',
72 'object_info_request', 'shutdown_request' ]
73 'object_info_request', 'shutdown_request' ]
73 self.handlers = {}
74 self.handlers = {}
74 for msg_type in msg_types:
75 for msg_type in msg_types:
75 self.handlers[msg_type] = getattr(self, msg_type)
76 self.handlers[msg_type] = getattr(self, msg_type)
76
77
77 def start(self):
78 def start(self):
78 """ Start the kernel main loop.
79 """ Start the kernel main loop.
79 """
80 """
80 while True:
81 while True:
81 ident,msg = self.session.recv(self.reply_socket,0)
82 ident,msg = self.session.recv(self.shell_socket,0)
82 assert ident is not None, "Missing message part."
83 assert ident is not None, "Missing message part."
83 omsg = Message(msg)
84 omsg = Message(msg)
84 print>>sys.__stdout__
85 print>>sys.__stdout__
85 print>>sys.__stdout__, omsg
86 print>>sys.__stdout__, omsg
86 handler = self.handlers.get(omsg.msg_type, None)
87 handler = self.handlers.get(omsg.msg_type, None)
87 if handler is None:
88 if handler is None:
88 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
89 print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg
89 else:
90 else:
90 handler(ident, omsg)
91 handler(ident, omsg)
91
92
92 def record_ports(self, xrep_port, pub_port, req_port, hb_port):
93 def record_ports(self, ports):
93 """Record the ports that this kernel is using.
94 """Record the ports that this kernel is using.
94
95
95 The creator of the Kernel instance must call this methods if they
96 The creator of the Kernel instance must call this methods if they
96 want the :meth:`connect_request` method to return the port numbers.
97 want the :meth:`connect_request` method to return the port numbers.
97 """
98 """
98 self._recorded_ports = {
99 self._recorded_ports = ports
99 'xrep_port' : xrep_port,
100 'pub_port' : pub_port,
101 'req_port' : req_port,
102 'hb_port' : hb_port
103 }
104
100
105 #---------------------------------------------------------------------------
101 #---------------------------------------------------------------------------
106 # Kernel request handlers
102 # Kernel request handlers
107 #---------------------------------------------------------------------------
103 #---------------------------------------------------------------------------
108
104
109 def execute_request(self, ident, parent):
105 def execute_request(self, ident, parent):
110 try:
106 try:
111 code = parent[u'content'][u'code']
107 code = parent[u'content'][u'code']
112 except:
108 except:
113 print>>sys.__stderr__, "Got bad msg: "
109 print>>sys.__stderr__, "Got bad msg: "
114 print>>sys.__stderr__, Message(parent)
110 print>>sys.__stderr__, Message(parent)
115 return
111 return
116 pyin_msg = self.session.send(self.pub_socket, u'pyin',{u'code':code}, parent=parent)
112 pyin_msg = self.session.send(self.iopub_socket, u'pyin',{u'code':code}, parent=parent)
117
113
118 try:
114 try:
119 comp_code = self.compiler(code, '<zmq-kernel>')
115 comp_code = self.compiler(code, '<zmq-kernel>')
120
116
121 # Replace raw_input. Note that is not sufficient to replace
117 # Replace raw_input. Note that is not sufficient to replace
122 # raw_input in the user namespace.
118 # raw_input in the user namespace.
123 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
119 raw_input = lambda prompt='': self._raw_input(prompt, ident, parent)
124 __builtin__.raw_input = raw_input
120 __builtin__.raw_input = raw_input
125
121
126 # Set the parent message of the display hook and out streams.
122 # Set the parent message of the display hook and out streams.
127 sys.displayhook.set_parent(parent)
123 sys.displayhook.set_parent(parent)
128 sys.stdout.set_parent(parent)
124 sys.stdout.set_parent(parent)
129 sys.stderr.set_parent(parent)
125 sys.stderr.set_parent(parent)
130
126
131 exec comp_code in self.user_ns, self.user_ns
127 exec comp_code in self.user_ns, self.user_ns
132 except:
128 except:
133 etype, evalue, tb = sys.exc_info()
129 etype, evalue, tb = sys.exc_info()
134 tb = traceback.format_exception(etype, evalue, tb)
130 tb = traceback.format_exception(etype, evalue, tb)
135 exc_content = {
131 exc_content = {
136 u'status' : u'error',
132 u'status' : u'error',
137 u'traceback' : tb,
133 u'traceback' : tb,
138 u'ename' : unicode(etype.__name__),
134 u'ename' : unicode(etype.__name__),
139 u'evalue' : unicode(evalue)
135 u'evalue' : unicode(evalue)
140 }
136 }
141 exc_msg = self.session.send(self.pub_socket, u'pyerr', exc_content, parent)
137 exc_msg = self.session.send(self.iopub_socket, u'pyerr', exc_content, parent)
142 reply_content = exc_content
138 reply_content = exc_content
143 else:
139 else:
144 reply_content = { 'status' : 'ok', 'payload' : {} }
140 reply_content = { 'status' : 'ok', 'payload' : {} }
145
141
146 # Flush output before sending the reply.
142 # Flush output before sending the reply.
147 sys.stderr.flush()
143 sys.stderr.flush()
148 sys.stdout.flush()
144 sys.stdout.flush()
149 # FIXME: on rare occasions, the flush doesn't seem to make it to the
145 # FIXME: on rare occasions, the flush doesn't seem to make it to the
150 # clients... This seems to mitigate the problem, but we definitely need
146 # clients... This seems to mitigate the problem, but we definitely need
151 # to better understand what's going on.
147 # to better understand what's going on.
152 if self._execute_sleep:
148 if self._execute_sleep:
153 time.sleep(self._execute_sleep)
149 time.sleep(self._execute_sleep)
154
150
155 # Send the reply.
151 # Send the reply.
156 reply_msg = self.session.send(self.reply_socket, u'execute_reply', reply_content, parent, ident=ident)
152 reply_msg = self.session.send(self.shell_socket, u'execute_reply', reply_content, parent, ident=ident)
157 print>>sys.__stdout__, Message(reply_msg)
153 print>>sys.__stdout__, Message(reply_msg)
158 if reply_msg['content']['status'] == u'error':
154 if reply_msg['content']['status'] == u'error':
159 self._abort_queue()
155 self._abort_queue()
160
156
161 def complete_request(self, ident, parent):
157 def complete_request(self, ident, parent):
162 matches = {'matches' : self._complete(parent),
158 matches = {'matches' : self._complete(parent),
163 'status' : 'ok'}
159 'status' : 'ok'}
164 completion_msg = self.session.send(self.reply_socket, 'complete_reply',
160 completion_msg = self.session.send(self.shell_socket, 'complete_reply',
165 matches, parent, ident)
161 matches, parent, ident)
166 print >> sys.__stdout__, completion_msg
162 print >> sys.__stdout__, completion_msg
167
163
168 def object_info_request(self, ident, parent):
164 def object_info_request(self, ident, parent):
169 context = parent['content']['oname'].split('.')
165 context = parent['content']['oname'].split('.')
170 object_info = self._object_info(context)
166 object_info = self._object_info(context)
171 msg = self.session.send(self.reply_socket, 'object_info_reply',
167 msg = self.session.send(self.shell_socket, 'object_info_reply',
172 object_info, parent, ident)
168 object_info, parent, ident)
173 print >> sys.__stdout__, msg
169 print >> sys.__stdout__, msg
174
170
175 def shutdown_request(self, ident, parent):
171 def shutdown_request(self, ident, parent):
176 content = dict(parent['content'])
172 content = dict(parent['content'])
177 msg = self.session.send(self.reply_socket, 'shutdown_reply',
173 msg = self.session.send(self.shell_socket, 'shutdown_reply',
178 content, parent, ident)
174 content, parent, ident)
179 msg = self.session.send(self.pub_socket, 'shutdown_reply',
175 msg = self.session.send(self.iopub_socket, 'shutdown_reply',
180 content, parent, ident)
176 content, parent, ident)
181 print >> sys.__stdout__, msg
177 print >> sys.__stdout__, msg
182 time.sleep(0.1)
178 time.sleep(0.1)
183 sys.exit(0)
179 sys.exit(0)
184
180
185 #---------------------------------------------------------------------------
181 #---------------------------------------------------------------------------
186 # Protected interface
182 # Protected interface
187 #---------------------------------------------------------------------------
183 #---------------------------------------------------------------------------
188
184
189 def _abort_queue(self):
185 def _abort_queue(self):
190 while True:
186 while True:
191 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
187 ident,msg = self.session.recv(self.reply_socket, zmq.NOBLOCK)
192 if msg is None:
188 if msg is None:
193 break
189 break
194 else:
190 else:
195 assert ident is not None, "Unexpected missing message part."
191 assert ident is not None, "Unexpected missing message part."
196 print>>sys.__stdout__, "Aborting:"
192 print>>sys.__stdout__, "Aborting:"
197 print>>sys.__stdout__, Message(msg)
193 print>>sys.__stdout__, Message(msg)
198 msg_type = msg['msg_type']
194 msg_type = msg['msg_type']
199 reply_type = msg_type.split('_')[0] + '_reply'
195 reply_type = msg_type.split('_')[0] + '_reply'
200 reply_msg = self.session.send(self.reply_socket, reply_type, {'status':'aborted'}, msg, ident=ident)
196 reply_msg = self.session.send(self.shell_socket, reply_type, {'status':'aborted'}, msg, ident=ident)
201 print>>sys.__stdout__, Message(reply_msg)
197 print>>sys.__stdout__, Message(reply_msg)
202 # We need to wait a bit for requests to come in. This can probably
198 # We need to wait a bit for requests to come in. This can probably
203 # be set shorter for true asynchronous clients.
199 # be set shorter for true asynchronous clients.
204 time.sleep(0.1)
200 time.sleep(0.1)
205
201
206 def _raw_input(self, prompt, ident, parent):
202 def _raw_input(self, prompt, ident, parent):
207 # Flush output before making the request.
203 # Flush output before making the request.
208 sys.stderr.flush()
204 sys.stderr.flush()
209 sys.stdout.flush()
205 sys.stdout.flush()
210
206
211 # Send the input request.
207 # Send the input request.
212 content = dict(prompt=prompt)
208 content = dict(prompt=prompt)
213 msg = self.session.send(self.req_socket, u'input_request', content, parent)
209 msg = self.session.send(self.stdin_socket, u'input_request', content, parent)
214
210
215 # Await a response.
211 # Await a response.
216 ident,reply = self.session.recv(self.req_socket, 0)
212 ident,reply = self.session.recv(self.stdin_socket, 0)
217 try:
213 try:
218 value = reply['content']['value']
214 value = reply['content']['value']
219 except:
215 except:
220 print>>sys.__stderr__, "Got bad raw_input reply: "
216 print>>sys.__stderr__, "Got bad raw_input reply: "
221 print>>sys.__stderr__, Message(parent)
217 print>>sys.__stderr__, Message(parent)
222 value = ''
218 value = ''
223 return value
219 return value
224
220
225 def _complete(self, msg):
221 def _complete(self, msg):
226 return self.completer.complete(msg.content.line, msg.content.text)
222 return self.completer.complete(msg.content.line, msg.content.text)
227
223
228 def _object_info(self, context):
224 def _object_info(self, context):
229 symbol, leftover = self._symbol_from_context(context)
225 symbol, leftover = self._symbol_from_context(context)
230 if symbol is not None and not leftover:
226 if symbol is not None and not leftover:
231 doc = getattr(symbol, '__doc__', '')
227 doc = getattr(symbol, '__doc__', '')
232 else:
228 else:
233 doc = ''
229 doc = ''
234 object_info = dict(docstring = doc)
230 object_info = dict(docstring = doc)
235 return object_info
231 return object_info
236
232
237 def _symbol_from_context(self, context):
233 def _symbol_from_context(self, context):
238 if not context:
234 if not context:
239 return None, context
235 return None, context
240
236
241 base_symbol_string = context[0]
237 base_symbol_string = context[0]
242 symbol = self.user_ns.get(base_symbol_string, None)
238 symbol = self.user_ns.get(base_symbol_string, None)
243 if symbol is None:
239 if symbol is None:
244 symbol = __builtin__.__dict__.get(base_symbol_string, None)
240 symbol = __builtin__.__dict__.get(base_symbol_string, None)
245 if symbol is None:
241 if symbol is None:
246 return None, context
242 return None, context
247
243
248 context = context[1:]
244 context = context[1:]
249 for i, name in enumerate(context):
245 for i, name in enumerate(context):
250 new_symbol = getattr(symbol, name, None)
246 new_symbol = getattr(symbol, name, None)
251 if new_symbol is None:
247 if new_symbol is None:
252 return symbol, context[i:]
248 return symbol, context[i:]
253 else:
249 else:
254 symbol = new_symbol
250 symbol = new_symbol
255
251
256 return symbol, []
252 return symbol, []
257
253
258 #-----------------------------------------------------------------------------
254 #-----------------------------------------------------------------------------
259 # Kernel main and launch functions
255 # Kernel main and launch functions
260 #-----------------------------------------------------------------------------
256 #-----------------------------------------------------------------------------
261
257
262 def launch_kernel(ip=None, xrep_port=0, pub_port=0, req_port=0, hb_port=0,
258 def launch_kernel(*args, **kwargs):
263 stdin=None, stdout=None, stderr=None,
259 """ Launches a simple Python kernel, binding to the specified ports.
264 executable=None, independent=False):
265 """ Launches a localhost kernel, binding to the specified ports.
266
267 Parameters
268 ----------
269 ip : str, optional
270 The ip address the kernel will bind to.
271
272 xrep_port : int, optional
273 The port to use for XREP channel.
274
275 pub_port : int, optional
276 The port to use for the SUB channel.
277
278 req_port : int, optional
279 The port to use for the REQ (raw input) channel.
280
260
281 hb_port : int, optional
261 This function simply calls entry_point.base_launch_kernel with the right first
282 The port to use for the hearbeat REP channel.
262 command to start a pykernel. See base_launch_kernel for arguments.
283
284 stdin, stdout, stderr : optional (default None)
285 Standards streams, as defined in subprocess.Popen.
286
287 executable : str, optional (default sys.executable)
288 The Python executable to use for the kernel process.
289
290 independent : bool, optional (default False)
291 If set, the kernel process is guaranteed to survive if this process
292 dies. If not set, an effort is made to ensure that the kernel is killed
293 when this process dies. Note that in this case it is still good practice
294 to kill kernels manually before exiting.
295
263
296 Returns
264 Returns
297 -------
265 -------
298 A tuple of form:
266 A tuple of form:
299 (kernel_process, xrep_port, pub_port, req_port)
267 (kernel_process, xrep_port, pub_port, req_port, hb_port)
300 where kernel_process is a Popen object and the ports are integers.
268 where kernel_process is a Popen object and the ports are integers.
301 """
269 """
302 extra_arguments = []
303 if ip is not None:
304 extra_arguments.append('--ip')
305 if isinstance(ip, basestring):
306 extra_arguments.append(ip)
307
308 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
270 return base_launch_kernel('from IPython.zmq.pykernel import main; main()',
309 xrep_port, pub_port, req_port, hb_port,
271 *args, **kwargs)
310 stdin, stdout, stderr,
311 executable, independent, extra_arguments)
312
272
313 main = make_default_main(Kernel)
273 def main():
274 """Run a PyKernel as an application"""
275 app = KernelApp()
276 app.initialize()
277 app.start()
314
278
315 if __name__ == '__main__':
279 if __name__ == '__main__':
316 main()
280 main()
General Comments 0
You need to be logged in to leave comments. Login now