##// END OF EJS Templates
Actually kill old kernels upon restart.
Brian E. Granger -
Show More
@@ -1,263 +1,264
1 """A tornado based IPython notebook server."""
1 """A tornado based IPython notebook server."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Copyright (C) 2011 The IPython Development Team
4 # Copyright (C) 2011 The IPython Development Team
5 #
5 #
6 # Distributed under the terms of the BSD License. The full license is in
6 # Distributed under the terms of the BSD License. The full license is in
7 # the file COPYING.txt, distributed as part of this software.
7 # the file COPYING.txt, distributed as part of this software.
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Imports
11 # Imports
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 import logging
14 import logging
15 import os
15 import os
16 import signal
16 import signal
17 import sys
17 import sys
18
18
19 import zmq
19 import zmq
20
20
21 # Install the pyzmq ioloop. This has to be done before anything else from
21 # Install the pyzmq ioloop. This has to be done before anything else from
22 # tornado is imported.
22 # tornado is imported.
23 from zmq.eventloop import ioloop
23 from zmq.eventloop import ioloop
24 import tornado.ioloop
24 import tornado.ioloop
25 tornado.ioloop = ioloop
25 tornado.ioloop = ioloop
26
26
27 from tornado import httpserver
27 from tornado import httpserver
28 from tornado import web
28 from tornado import web
29
29
30 from kernelmanager import KernelManager
30 from kernelmanager import KernelManager
31 from sessionmanager import SessionManager
31 from sessionmanager import SessionManager
32 from handlers import (
32 from handlers import (
33 MainHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
33 MainHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
34 NotebookRootHandler, NotebookHandler
34 NotebookRootHandler, NotebookHandler
35 )
35 )
36 from routers import IOPubStreamRouter, ShellStreamRouter
36 from routers import IOPubStreamRouter, ShellStreamRouter
37
37
38 from IPython.core.application import BaseIPythonApplication
38 from IPython.core.application import BaseIPythonApplication
39 from IPython.core.profiledir import ProfileDir
39 from IPython.core.profiledir import ProfileDir
40 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
40 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
41 from IPython.zmq.session import Session
41 from IPython.zmq.session import Session
42 from IPython.zmq.zmqshell import ZMQInteractiveShell
42 from IPython.zmq.zmqshell import ZMQInteractiveShell
43 from IPython.zmq.ipkernel import (
43 from IPython.zmq.ipkernel import (
44 flags as ipkernel_flags,
44 flags as ipkernel_flags,
45 aliases as ipkernel_aliases,
45 aliases as ipkernel_aliases,
46 IPKernelApp
46 IPKernelApp
47 )
47 )
48 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
48 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
49
49
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 # Module globals
51 # Module globals
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53
53
54 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
54 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
55 _kernel_action_regex = r"(?P<action>restart|interrupt)"
55 _kernel_action_regex = r"(?P<action>restart|interrupt)"
56
56
57 LOCALHOST = '127.0.0.1'
57 LOCALHOST = '127.0.0.1'
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # The Tornado web application
60 # The Tornado web application
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63 class NotebookWebApplication(web.Application):
63 class NotebookWebApplication(web.Application):
64
64
65 def __init__(self, kernel_manager, log, kernel_argv, config):
65 def __init__(self, kernel_manager, log, kernel_argv, config):
66 handlers = [
66 handlers = [
67 (r"/", MainHandler),
67 (r"/", MainHandler),
68 (r"/kernels", KernelHandler),
68 (r"/kernels", KernelHandler),
69 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
69 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
70 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
70 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
71 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
71 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
72 (r"/notebooks", NotebookRootHandler),
72 (r"/notebooks", NotebookRootHandler),
73 (r"/notebooks/([^/]+)", NotebookHandler)
73 (r"/notebooks/([^/]+)", NotebookHandler)
74 ]
74 ]
75 settings = dict(
75 settings = dict(
76 template_path=os.path.join(os.path.dirname(__file__), "templates"),
76 template_path=os.path.join(os.path.dirname(__file__), "templates"),
77 static_path=os.path.join(os.path.dirname(__file__), "static"),
77 static_path=os.path.join(os.path.dirname(__file__), "static"),
78 )
78 )
79 web.Application.__init__(self, handlers, **settings)
79 web.Application.__init__(self, handlers, **settings)
80
80
81 self.kernel_manager = kernel_manager
81 self.kernel_manager = kernel_manager
82 self.log = log
82 self.log = log
83 self.kernel_argv = kernel_argv
83 self.kernel_argv = kernel_argv
84 self.config = config
84 self.config = config
85 self._routers = {}
85 self._routers = {}
86 self._session_dict = {}
86 self._session_dict = {}
87
87
88 #-------------------------------------------------------------------------
88 #-------------------------------------------------------------------------
89 # Methods for managing kernels and sessions
89 # Methods for managing kernels and sessions
90 #-------------------------------------------------------------------------
90 #-------------------------------------------------------------------------
91
91
92 @property
92 @property
93 def kernel_ids(self):
93 def kernel_ids(self):
94 return self.kernel_manager.kernel_ids
94 return self.kernel_manager.kernel_ids
95
95
96 def start_kernel(self):
96 def start_kernel(self):
97 kwargs = dict()
97 kwargs = dict()
98 kwargs['extra_arguments'] = self.kernel_argv
98 kwargs['extra_arguments'] = self.kernel_argv
99 kernel_id = self.kernel_manager.start_kernel(**kwargs)
99 kernel_id = self.kernel_manager.start_kernel(**kwargs)
100 self.log.info("Kernel started: %s" % kernel_id)
100 self.log.info("Kernel started: %s" % kernel_id)
101 self.log.debug("Kernel args: %r" % kwargs)
101 self.log.debug("Kernel args: %r" % kwargs)
102 self.start_session_manager(kernel_id)
102 self.start_session_manager(kernel_id)
103 return kernel_id
103 return kernel_id
104
104
105 def start_session_manager(self, kernel_id):
105 def start_session_manager(self, kernel_id):
106 sm = self.kernel_manager.create_session_manager(kernel_id)
106 sm = self.kernel_manager.create_session_manager(kernel_id)
107 self._session_dict[kernel_id] = sm
107 self._session_dict[kernel_id] = sm
108 iopub_stream = sm.get_iopub_stream()
108 iopub_stream = sm.get_iopub_stream()
109 shell_stream = sm.get_shell_stream()
109 shell_stream = sm.get_shell_stream()
110 iopub_router = IOPubStreamRouter(
110 iopub_router = IOPubStreamRouter(
111 zmq_stream=iopub_stream, session=sm.session, config=self.config
111 zmq_stream=iopub_stream, session=sm.session, config=self.config
112 )
112 )
113 shell_router = ShellStreamRouter(
113 shell_router = ShellStreamRouter(
114 zmq_stream=shell_stream, session=sm.session, config=self.config
114 zmq_stream=shell_stream, session=sm.session, config=self.config
115 )
115 )
116 self._routers[(kernel_id, 'iopub')] = iopub_router
116 self._routers[(kernel_id, 'iopub')] = iopub_router
117 self._routers[(kernel_id, 'shell')] = shell_router
117 self._routers[(kernel_id, 'shell')] = shell_router
118
118
119 def kill_kernel(self, kernel_id):
119 def kill_kernel(self, kernel_id):
120 sm = self._session_dict.pop(kernel_id)
120 sm = self._session_dict.pop(kernel_id)
121 sm.stop()
121 sm.stop()
122 self.kernel_manager.kill_kernel(kernel_id)
122 self.kernel_manager.kill_kernel(kernel_id)
123 self.log.info("Kernel killed: %s" % kernel_id)
123 self.log.info("Kernel killed: %s" % kernel_id)
124
124
125 def interrupt_kernel(self, kernel_id):
125 def interrupt_kernel(self, kernel_id):
126 self.kernel_manager.interrupt_kernel(kernel_id)
126 self.kernel_manager.interrupt_kernel(kernel_id)
127 self.log.debug("Kernel interrupted: %s" % kernel_id)
127 self.log.debug("Kernel interrupted: %s" % kernel_id)
128
128
129 def restart_kernel(self, kernel_id):
129 def restart_kernel(self, kernel_id):
130 # Create the new kernel first so we can move the clients over.
130 # Create the new kernel first so we can move the clients over.
131 new_kernel_id = self.start_kernel()
131 new_kernel_id = self.start_kernel()
132
132
133 # Copy the clients over to the new routers.
133 # Copy the clients over to the new routers.
134 old_iopub_router = self.get_router(kernel_id, 'iopub')
134 old_iopub_router = self.get_router(kernel_id, 'iopub')
135 old_shell_router = self.get_router(kernel_id, 'shell')
135 old_shell_router = self.get_router(kernel_id, 'shell')
136 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
136 new_iopub_router = self.get_router(new_kernel_id, 'iopub')
137 new_shell_router = self.get_router(new_kernel_id, 'shell')
137 new_shell_router = self.get_router(new_kernel_id, 'shell')
138 new_iopub_router.copy_clients(old_iopub_router)
138 new_iopub_router.copy_clients(old_iopub_router)
139 new_shell_router.copy_clients(old_shell_router)
139 new_shell_router.copy_clients(old_shell_router)
140
140
141 # Now shutdown the old session and the kernel.
141 # Now shutdown the old session and the kernel.
142 # TODO: This causes a hard crash in ZMQStream.close, which sets
142 # TODO: This causes a hard crash in ZMQStream.close, which sets
143 # self.socket to None to hastily. We will need to fix this in PyZMQ
143 # self.socket to None to hastily. We will need to fix this in PyZMQ
144 # itself. For now, we just leave the old kernel running :(
144 # itself. For now, we just leave the old kernel running :(
145 # self.kill_kernel(kernel_id)
145 # Maybe this is fixed now, but nothing was changed really.
146 self.kill_kernel(kernel_id)
146
147
147 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
148 self.log.debug("Kernel restarted: %s -> %s" % (kernel_id, new_kernel_id))
148 return new_kernel_id
149 return new_kernel_id
149
150
150 def get_router(self, kernel_id, stream_name):
151 def get_router(self, kernel_id, stream_name):
151 router = self._routers[(kernel_id, stream_name)]
152 router = self._routers[(kernel_id, stream_name)]
152 return router
153 return router
153
154
154
155
155
156
156 #-----------------------------------------------------------------------------
157 #-----------------------------------------------------------------------------
157 # Aliases and Flags
158 # Aliases and Flags
158 #-----------------------------------------------------------------------------
159 #-----------------------------------------------------------------------------
159
160
160 flags = dict(ipkernel_flags)
161 flags = dict(ipkernel_flags)
161
162
162 # the flags that are specific to the frontend
163 # the flags that are specific to the frontend
163 # these must be scrubbed before being passed to the kernel,
164 # these must be scrubbed before being passed to the kernel,
164 # or it will raise an error on unrecognized flags
165 # or it will raise an error on unrecognized flags
165 notebook_flags = []
166 notebook_flags = []
166
167
167 aliases = dict(ipkernel_aliases)
168 aliases = dict(ipkernel_aliases)
168
169
169 aliases.update(dict(
170 aliases.update(dict(
170 ip = 'IPythonNotebookApp.ip',
171 ip = 'IPythonNotebookApp.ip',
171 port = 'IPythonNotebookApp.port',
172 port = 'IPythonNotebookApp.port',
172 colors = 'ZMQInteractiveShell.colors',
173 colors = 'ZMQInteractiveShell.colors',
173 editor = 'RichIPythonWidget.editor',
174 editor = 'RichIPythonWidget.editor',
174 ))
175 ))
175
176
176 #-----------------------------------------------------------------------------
177 #-----------------------------------------------------------------------------
177 # IPythonNotebookApp
178 # IPythonNotebookApp
178 #-----------------------------------------------------------------------------
179 #-----------------------------------------------------------------------------
179
180
180 class IPythonNotebookApp(BaseIPythonApplication):
181 class IPythonNotebookApp(BaseIPythonApplication):
181 name = 'ipython-notebook'
182 name = 'ipython-notebook'
182 default_config_file_name='ipython_notebook_config.py'
183 default_config_file_name='ipython_notebook_config.py'
183
184
184 description = """
185 description = """
185 The IPython HTML Notebook.
186 The IPython HTML Notebook.
186
187
187 This launches a Tornado based HTML Notebook Server that serves up an
188 This launches a Tornado based HTML Notebook Server that serves up an
188 HTML5/Javascript Notebook client.
189 HTML5/Javascript Notebook client.
189 """
190 """
190
191
191 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
192 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
192 KernelManager, SessionManager, RichIPythonWidget]
193 KernelManager, SessionManager, RichIPythonWidget]
193 flags = Dict(flags)
194 flags = Dict(flags)
194 aliases = Dict(aliases)
195 aliases = Dict(aliases)
195
196
196 kernel_argv = List(Unicode)
197 kernel_argv = List(Unicode)
197
198
198 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
199 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
199 default_value=logging.INFO,
200 default_value=logging.INFO,
200 config=True,
201 config=True,
201 help="Set the log level by value or name.")
202 help="Set the log level by value or name.")
202
203
203 # connection info:
204 # connection info:
204 ip = Unicode(LOCALHOST, config=True,
205 ip = Unicode(LOCALHOST, config=True,
205 help="The IP address the notebook server will listen on."
206 help="The IP address the notebook server will listen on."
206 )
207 )
207
208
208 port = Int(8888, config=True,
209 port = Int(8888, config=True,
209 help="The port the notebook server will listen on."
210 help="The port the notebook server will listen on."
210 )
211 )
211
212
212 # the factory for creating a widget
213 # the factory for creating a widget
213 widget_factory = Any(RichIPythonWidget)
214 widget_factory = Any(RichIPythonWidget)
214
215
215 def parse_command_line(self, argv=None):
216 def parse_command_line(self, argv=None):
216 super(IPythonNotebookApp, self).parse_command_line(argv)
217 super(IPythonNotebookApp, self).parse_command_line(argv)
217 if argv is None:
218 if argv is None:
218 argv = sys.argv[1:]
219 argv = sys.argv[1:]
219
220
220 self.kernel_argv = list(argv) # copy
221 self.kernel_argv = list(argv) # copy
221 # kernel should inherit default config file from frontend
222 # kernel should inherit default config file from frontend
222 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
223 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
223 # scrub frontend-specific flags
224 # scrub frontend-specific flags
224 for a in argv:
225 for a in argv:
225 if a.startswith('-') and a.lstrip('-') in notebook_flags:
226 if a.startswith('-') and a.lstrip('-') in notebook_flags:
226 self.kernel_argv.remove(a)
227 self.kernel_argv.remove(a)
227
228
228 def init_kernel_manager(self):
229 def init_kernel_manager(self):
229 # Don't let Qt or ZMQ swallow KeyboardInterupts.
230 # Don't let Qt or ZMQ swallow KeyboardInterupts.
230 signal.signal(signal.SIGINT, signal.SIG_DFL)
231 signal.signal(signal.SIGINT, signal.SIG_DFL)
231
232
232 # Create a KernelManager and start a kernel.
233 # Create a KernelManager and start a kernel.
233 self.kernel_manager = KernelManager(config=self.config, log=self.log)
234 self.kernel_manager = KernelManager(config=self.config, log=self.log)
234
235
235 def init_logging(self):
236 def init_logging(self):
236 super(IPythonNotebookApp, self).init_logging()
237 super(IPythonNotebookApp, self).init_logging()
237 # This prevents double log messages because tornado use a root logger that
238 # This prevents double log messages because tornado use a root logger that
238 # self.log is a child of. The logging module dipatches log messages to a log
239 # self.log is a child of. The logging module dipatches log messages to a log
239 # and all of its ancenstors until propagate is set to False.
240 # and all of its ancenstors until propagate is set to False.
240 self.log.propagate = False
241 self.log.propagate = False
241
242
242 def initialize(self, argv=None):
243 def initialize(self, argv=None):
243 super(IPythonNotebookApp, self).initialize(argv)
244 super(IPythonNotebookApp, self).initialize(argv)
244 self.init_kernel_manager()
245 self.init_kernel_manager()
245 self.web_app = NotebookWebApplication(
246 self.web_app = NotebookWebApplication(
246 self.kernel_manager, self.log, self.kernel_argv, self.config
247 self.kernel_manager, self.log, self.kernel_argv, self.config
247 )
248 )
248 self.http_server = httpserver.HTTPServer(self.web_app)
249 self.http_server = httpserver.HTTPServer(self.web_app)
249 self.http_server.listen(self.port)
250 self.http_server.listen(self.port)
250
251
251 def start(self):
252 def start(self):
252 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
253 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
253 ioloop.IOLoop.instance().start()
254 ioloop.IOLoop.instance().start()
254
255
255 #-----------------------------------------------------------------------------
256 #-----------------------------------------------------------------------------
256 # Main entry point
257 # Main entry point
257 #-----------------------------------------------------------------------------
258 #-----------------------------------------------------------------------------
258
259
259 def launch_new_instance():
260 def launch_new_instance():
260 app = IPythonNotebookApp()
261 app = IPythonNotebookApp()
261 app.initialize()
262 app.initialize()
262 app.start()
263 app.start()
263
264
General Comments 0
You need to be logged in to leave comments. Login now