##// END OF EJS Templates
Ensure handler patterns are str, not unicode...
MinRK -
Show More
@@ -1,439 +1,450 b''
1 # coding: utf-8
1 """A tornado based IPython notebook server.
2 """A tornado based IPython notebook server.
2
3
3 Authors:
4 Authors:
4
5
5 * Brian Granger
6 * Brian Granger
6 """
7 """
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2011 The IPython Development Team
9 # Copyright (C) 2008-2011 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # stdlib
19 # stdlib
20 import errno
20 import errno
21 import logging
21 import logging
22 import os
22 import os
23 import signal
23 import signal
24 import socket
24 import socket
25 import sys
25 import sys
26 import threading
26 import threading
27 import webbrowser
27 import webbrowser
28
28
29 # Third party
29 # Third party
30 import zmq
30 import zmq
31
31
32 # Install the pyzmq ioloop. This has to be done before anything else from
32 # Install the pyzmq ioloop. This has to be done before anything else from
33 # tornado is imported.
33 # tornado is imported.
34 from zmq.eventloop import ioloop
34 from zmq.eventloop import ioloop
35 # FIXME: ioloop.install is new in pyzmq-2.1.7, so remove this conditional
35 # FIXME: ioloop.install is new in pyzmq-2.1.7, so remove this conditional
36 # when pyzmq dependency is updated beyond that.
36 # when pyzmq dependency is updated beyond that.
37 if hasattr(ioloop, 'install'):
37 if hasattr(ioloop, 'install'):
38 ioloop.install()
38 ioloop.install()
39 else:
39 else:
40 import tornado.ioloop
40 import tornado.ioloop
41 tornado.ioloop.IOLoop = ioloop.IOLoop
41 tornado.ioloop.IOLoop = ioloop.IOLoop
42
42
43 from tornado import httpserver
43 from tornado import httpserver
44 from tornado import web
44 from tornado import web
45
45
46 # Our own libraries
46 # Our own libraries
47 from .kernelmanager import MappingKernelManager
47 from .kernelmanager import MappingKernelManager
48 from .handlers import (LoginHandler, LogoutHandler,
48 from .handlers import (LoginHandler, LogoutHandler,
49 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
49 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
50 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
50 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
51 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
51 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
52 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler
52 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler
53 )
53 )
54 from .notebookmanager import NotebookManager
54 from .notebookmanager import NotebookManager
55
55
56 from IPython.config.application import catch_config_error, boolean_flag
56 from IPython.config.application import catch_config_error, boolean_flag
57 from IPython.core.application import BaseIPythonApplication
57 from IPython.core.application import BaseIPythonApplication
58 from IPython.core.profiledir import ProfileDir
58 from IPython.core.profiledir import ProfileDir
59 from IPython.lib.kernel import swallow_argv
59 from IPython.lib.kernel import swallow_argv
60 from IPython.zmq.session import Session, default_secure
60 from IPython.zmq.session import Session, default_secure
61 from IPython.zmq.zmqshell import ZMQInteractiveShell
61 from IPython.zmq.zmqshell import ZMQInteractiveShell
62 from IPython.zmq.ipkernel import (
62 from IPython.zmq.ipkernel import (
63 flags as ipkernel_flags,
63 flags as ipkernel_flags,
64 aliases as ipkernel_aliases,
64 aliases as ipkernel_aliases,
65 IPKernelApp
65 IPKernelApp
66 )
66 )
67 from IPython.utils.traitlets import Dict, Unicode, Integer, List, Enum, Bool
67 from IPython.utils.traitlets import Dict, Unicode, Integer, List, Enum, Bool
68 from IPython.utils import py3compat
68
69
69 #-----------------------------------------------------------------------------
70 #-----------------------------------------------------------------------------
70 # Module globals
71 # Module globals
71 #-----------------------------------------------------------------------------
72 #-----------------------------------------------------------------------------
72
73
73 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
74 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
74 _kernel_action_regex = r"(?P<action>restart|interrupt)"
75 _kernel_action_regex = r"(?P<action>restart|interrupt)"
75 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
76 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
76
77
77 LOCALHOST = '127.0.0.1'
78 LOCALHOST = '127.0.0.1'
78
79
79 _examples = """
80 _examples = """
80 ipython notebook # start the notebook
81 ipython notebook # start the notebook
81 ipython notebook --profile=sympy # use the sympy profile
82 ipython notebook --profile=sympy # use the sympy profile
82 ipython notebook --pylab=inline # pylab in inline plotting mode
83 ipython notebook --pylab=inline # pylab in inline plotting mode
83 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
84 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
84 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
85 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
85 """
86 """
86
87
87 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
88 # Helper functions
89 # Helper functions
89 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
90
91
91 def url_path_join(a,b):
92 def url_path_join(a,b):
92 if a.endswith('/') and b.startswith('/'):
93 if a.endswith('/') and b.startswith('/'):
93 return a[:-1]+b
94 return a[:-1]+b
94 else:
95 else:
95 return a+b
96 return a+b
96
97
97 #-----------------------------------------------------------------------------
98 #-----------------------------------------------------------------------------
98 # The Tornado web application
99 # The Tornado web application
99 #-----------------------------------------------------------------------------
100 #-----------------------------------------------------------------------------
100
101
101 class NotebookWebApplication(web.Application):
102 class NotebookWebApplication(web.Application):
102
103
103 def __init__(self, ipython_app, kernel_manager, notebook_manager, log,
104 def __init__(self, ipython_app, kernel_manager, notebook_manager, log,
104 base_project_url, settings_overrides):
105 base_project_url, settings_overrides):
105 handlers = [
106 handlers = [
106 (r"/", ProjectDashboardHandler),
107 (r"/", ProjectDashboardHandler),
107 (r"/login", LoginHandler),
108 (r"/login", LoginHandler),
108 (r"/logout", LogoutHandler),
109 (r"/logout", LogoutHandler),
109 (r"/new", NewHandler),
110 (r"/new", NewHandler),
110 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
111 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
111 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
112 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
112 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
113 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
113 (r"/kernels", MainKernelHandler),
114 (r"/kernels", MainKernelHandler),
114 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
115 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
115 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
116 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
116 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
117 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
117 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
118 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
118 (r"/notebooks", NotebookRootHandler),
119 (r"/notebooks", NotebookRootHandler),
119 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
120 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
120 (r"/rstservice/render", RSTHandler),
121 (r"/rstservice/render", RSTHandler),
121 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
122 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
122 ]
123 ]
123 settings = dict(
124 settings = dict(
124 template_path=os.path.join(os.path.dirname(__file__), "templates"),
125 template_path=os.path.join(os.path.dirname(__file__), "templates"),
125 static_path=os.path.join(os.path.dirname(__file__), "static"),
126 static_path=os.path.join(os.path.dirname(__file__), "static"),
126 cookie_secret=os.urandom(1024),
127 cookie_secret=os.urandom(1024),
127 login_url="/login",
128 login_url="/login",
128 )
129 )
129
130
130 # allow custom overrides for the tornado web app.
131 # allow custom overrides for the tornado web app.
131 settings.update(settings_overrides)
132 settings.update(settings_overrides)
132
133
134 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
135 # base_project_url will always be unicode, which will in turn
136 # make the patterns unicode, and ultimately result in unicode
137 # keys in kwargs to handler._execute(**kwargs) in tornado.
138 # This enforces that base_project_url be ascii in that situation.
139 #
140 # Note that the URLs these patterns check against are escaped,
141 # and thus guaranteed to be ASCII: 'héllo' is really 'h%C3%A9llo'.
142 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
143
133 # prepend base_project_url onto the patterns that we match
144 # prepend base_project_url onto the patterns that we match
134 new_handlers = []
145 new_handlers = []
135 for handler in handlers:
146 for handler in handlers:
136 pattern = url_path_join(base_project_url, handler[0])
147 pattern = url_path_join(base_project_url, handler[0])
137 new_handler = tuple([pattern]+list(handler[1:]))
148 new_handler = tuple([pattern]+list(handler[1:]))
138 new_handlers.append( new_handler )
149 new_handlers.append( new_handler )
139
150
140 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
151 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
141
152
142 self.kernel_manager = kernel_manager
153 self.kernel_manager = kernel_manager
143 self.log = log
154 self.log = log
144 self.notebook_manager = notebook_manager
155 self.notebook_manager = notebook_manager
145 self.ipython_app = ipython_app
156 self.ipython_app = ipython_app
146 self.read_only = self.ipython_app.read_only
157 self.read_only = self.ipython_app.read_only
147
158
148
159
149 #-----------------------------------------------------------------------------
160 #-----------------------------------------------------------------------------
150 # Aliases and Flags
161 # Aliases and Flags
151 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
152
163
153 flags = dict(ipkernel_flags)
164 flags = dict(ipkernel_flags)
154 flags['no-browser']=(
165 flags['no-browser']=(
155 {'NotebookApp' : {'open_browser' : False}},
166 {'NotebookApp' : {'open_browser' : False}},
156 "Don't open the notebook in a browser after startup."
167 "Don't open the notebook in a browser after startup."
157 )
168 )
158 flags['no-mathjax']=(
169 flags['no-mathjax']=(
159 {'NotebookApp' : {'enable_mathjax' : False}},
170 {'NotebookApp' : {'enable_mathjax' : False}},
160 """Disable MathJax
171 """Disable MathJax
161
172
162 MathJax is the javascript library IPython uses to render math/LaTeX. It is
173 MathJax is the javascript library IPython uses to render math/LaTeX. It is
163 very large, so you may want to disable it if you have a slow internet
174 very large, so you may want to disable it if you have a slow internet
164 connection, or for offline use of the notebook.
175 connection, or for offline use of the notebook.
165
176
166 When disabled, equations etc. will appear as their untransformed TeX source.
177 When disabled, equations etc. will appear as their untransformed TeX source.
167 """
178 """
168 )
179 )
169 flags['read-only'] = (
180 flags['read-only'] = (
170 {'NotebookApp' : {'read_only' : True}},
181 {'NotebookApp' : {'read_only' : True}},
171 """Allow read-only access to notebooks.
182 """Allow read-only access to notebooks.
172
183
173 When using a password to protect the notebook server, this flag
184 When using a password to protect the notebook server, this flag
174 allows unauthenticated clients to view the notebook list, and
185 allows unauthenticated clients to view the notebook list, and
175 individual notebooks, but not edit them, start kernels, or run
186 individual notebooks, but not edit them, start kernels, or run
176 code.
187 code.
177
188
178 If no password is set, the server will be entirely read-only.
189 If no password is set, the server will be entirely read-only.
179 """
190 """
180 )
191 )
181
192
182 # Add notebook manager flags
193 # Add notebook manager flags
183 flags.update(boolean_flag('script', 'NotebookManager.save_script',
194 flags.update(boolean_flag('script', 'NotebookManager.save_script',
184 'Auto-save a .py script everytime the .ipynb notebook is saved',
195 'Auto-save a .py script everytime the .ipynb notebook is saved',
185 'Do not auto-save .py scripts for every notebook'))
196 'Do not auto-save .py scripts for every notebook'))
186
197
187 # the flags that are specific to the frontend
198 # the flags that are specific to the frontend
188 # these must be scrubbed before being passed to the kernel,
199 # these must be scrubbed before being passed to the kernel,
189 # or it will raise an error on unrecognized flags
200 # or it will raise an error on unrecognized flags
190 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
201 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
191
202
192 aliases = dict(ipkernel_aliases)
203 aliases = dict(ipkernel_aliases)
193
204
194 aliases.update({
205 aliases.update({
195 'ip': 'NotebookApp.ip',
206 'ip': 'NotebookApp.ip',
196 'port': 'NotebookApp.port',
207 'port': 'NotebookApp.port',
197 'keyfile': 'NotebookApp.keyfile',
208 'keyfile': 'NotebookApp.keyfile',
198 'certfile': 'NotebookApp.certfile',
209 'certfile': 'NotebookApp.certfile',
199 'notebook-dir': 'NotebookManager.notebook_dir',
210 'notebook-dir': 'NotebookManager.notebook_dir',
200 })
211 })
201
212
202 # remove ipkernel flags that are singletons, and don't make sense in
213 # remove ipkernel flags that are singletons, and don't make sense in
203 # multi-kernel evironment:
214 # multi-kernel evironment:
204 aliases.pop('f', None)
215 aliases.pop('f', None)
205
216
206 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
217 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
207 u'notebook-dir']
218 u'notebook-dir']
208
219
209 #-----------------------------------------------------------------------------
220 #-----------------------------------------------------------------------------
210 # NotebookApp
221 # NotebookApp
211 #-----------------------------------------------------------------------------
222 #-----------------------------------------------------------------------------
212
223
213 class NotebookApp(BaseIPythonApplication):
224 class NotebookApp(BaseIPythonApplication):
214
225
215 name = 'ipython-notebook'
226 name = 'ipython-notebook'
216 default_config_file_name='ipython_notebook_config.py'
227 default_config_file_name='ipython_notebook_config.py'
217
228
218 description = """
229 description = """
219 The IPython HTML Notebook.
230 The IPython HTML Notebook.
220
231
221 This launches a Tornado based HTML Notebook Server that serves up an
232 This launches a Tornado based HTML Notebook Server that serves up an
222 HTML5/Javascript Notebook client.
233 HTML5/Javascript Notebook client.
223 """
234 """
224 examples = _examples
235 examples = _examples
225
236
226 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
237 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
227 MappingKernelManager, NotebookManager]
238 MappingKernelManager, NotebookManager]
228 flags = Dict(flags)
239 flags = Dict(flags)
229 aliases = Dict(aliases)
240 aliases = Dict(aliases)
230
241
231 kernel_argv = List(Unicode)
242 kernel_argv = List(Unicode)
232
243
233 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
244 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
234 default_value=logging.INFO,
245 default_value=logging.INFO,
235 config=True,
246 config=True,
236 help="Set the log level by value or name.")
247 help="Set the log level by value or name.")
237
248
238 # create requested profiles by default, if they don't exist:
249 # create requested profiles by default, if they don't exist:
239 auto_create = Bool(True)
250 auto_create = Bool(True)
240
251
241 # Network related information.
252 # Network related information.
242
253
243 ip = Unicode(LOCALHOST, config=True,
254 ip = Unicode(LOCALHOST, config=True,
244 help="The IP address the notebook server will listen on."
255 help="The IP address the notebook server will listen on."
245 )
256 )
246
257
247 def _ip_changed(self, name, old, new):
258 def _ip_changed(self, name, old, new):
248 if new == u'*': self.ip = u''
259 if new == u'*': self.ip = u''
249
260
250 port = Integer(8888, config=True,
261 port = Integer(8888, config=True,
251 help="The port the notebook server will listen on."
262 help="The port the notebook server will listen on."
252 )
263 )
253
264
254 certfile = Unicode(u'', config=True,
265 certfile = Unicode(u'', config=True,
255 help="""The full path to an SSL/TLS certificate file."""
266 help="""The full path to an SSL/TLS certificate file."""
256 )
267 )
257
268
258 keyfile = Unicode(u'', config=True,
269 keyfile = Unicode(u'', config=True,
259 help="""The full path to a private key file for usage with SSL/TLS."""
270 help="""The full path to a private key file for usage with SSL/TLS."""
260 )
271 )
261
272
262 password = Unicode(u'', config=True,
273 password = Unicode(u'', config=True,
263 help="""Hashed password to use for web authentication.
274 help="""Hashed password to use for web authentication.
264
275
265 To generate, type in a python/IPython shell:
276 To generate, type in a python/IPython shell:
266
277
267 from IPython.lib import passwd; passwd()
278 from IPython.lib import passwd; passwd()
268
279
269 The string should be of the form type:salt:hashed-password.
280 The string should be of the form type:salt:hashed-password.
270 """
281 """
271 )
282 )
272
283
273 open_browser = Bool(True, config=True,
284 open_browser = Bool(True, config=True,
274 help="Whether to open in a browser after starting.")
285 help="Whether to open in a browser after starting.")
275
286
276 read_only = Bool(False, config=True,
287 read_only = Bool(False, config=True,
277 help="Whether to prevent editing/execution of notebooks."
288 help="Whether to prevent editing/execution of notebooks."
278 )
289 )
279
290
280 webapp_settings = Dict(config=True,
291 webapp_settings = Dict(config=True,
281 help="Supply overrides for the tornado.web.Application that the "
292 help="Supply overrides for the tornado.web.Application that the "
282 "IPython notebook uses.")
293 "IPython notebook uses.")
283
294
284 enable_mathjax = Bool(True, config=True,
295 enable_mathjax = Bool(True, config=True,
285 help="""Whether to enable MathJax for typesetting math/TeX
296 help="""Whether to enable MathJax for typesetting math/TeX
286
297
287 MathJax is the javascript library IPython uses to render math/LaTeX. It is
298 MathJax is the javascript library IPython uses to render math/LaTeX. It is
288 very large, so you may want to disable it if you have a slow internet
299 very large, so you may want to disable it if you have a slow internet
289 connection, or for offline use of the notebook.
300 connection, or for offline use of the notebook.
290
301
291 When disabled, equations etc. will appear as their untransformed TeX source.
302 When disabled, equations etc. will appear as their untransformed TeX source.
292 """
303 """
293 )
304 )
294 def _enable_mathjax_changed(self, name, old, new):
305 def _enable_mathjax_changed(self, name, old, new):
295 """set mathjax url to empty if mathjax is disabled"""
306 """set mathjax url to empty if mathjax is disabled"""
296 if not new:
307 if not new:
297 self.mathjax_url = u''
308 self.mathjax_url = u''
298
309
299 base_project_url = Unicode('/', config=True,
310 base_project_url = Unicode('/', config=True,
300 help='''The base URL for the notebook server''')
311 help='''The base URL for the notebook server''')
301 base_kernel_url = Unicode('/', config=True,
312 base_kernel_url = Unicode('/', config=True,
302 help='''The base URL for the kernel server''')
313 help='''The base URL for the kernel server''')
303 websocket_host = Unicode("", config=True,
314 websocket_host = Unicode("", config=True,
304 help="""The hostname for the websocket server."""
315 help="""The hostname for the websocket server."""
305 )
316 )
306
317
307 mathjax_url = Unicode("", config=True,
318 mathjax_url = Unicode("", config=True,
308 help="""The url for MathJax.js."""
319 help="""The url for MathJax.js."""
309 )
320 )
310 def _mathjax_url_default(self):
321 def _mathjax_url_default(self):
311 if not self.enable_mathjax:
322 if not self.enable_mathjax:
312 return u''
323 return u''
313 static_path = self.webapp_settings.get("static_path", os.path.join(os.path.dirname(__file__), "static"))
324 static_path = self.webapp_settings.get("static_path", os.path.join(os.path.dirname(__file__), "static"))
314 static_url_prefix = self.webapp_settings.get("static_url_prefix",
325 static_url_prefix = self.webapp_settings.get("static_url_prefix",
315 "/static/")
326 "/static/")
316 if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")):
327 if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")):
317 self.log.info("Using local MathJax")
328 self.log.info("Using local MathJax")
318 return static_url_prefix+u"mathjax/MathJax.js"
329 return static_url_prefix+u"mathjax/MathJax.js"
319 else:
330 else:
320 self.log.info("Using MathJax from CDN")
331 self.log.info("Using MathJax from CDN")
321 return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js"
332 return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js"
322
333
323 def _mathjax_url_changed(self, name, old, new):
334 def _mathjax_url_changed(self, name, old, new):
324 if new and not self.enable_mathjax:
335 if new and not self.enable_mathjax:
325 # enable_mathjax=False overrides mathjax_url
336 # enable_mathjax=False overrides mathjax_url
326 self.mathjax_url = u''
337 self.mathjax_url = u''
327 else:
338 else:
328 self.log.info("Using MathJax: %s", new)
339 self.log.info("Using MathJax: %s", new)
329
340
330 def parse_command_line(self, argv=None):
341 def parse_command_line(self, argv=None):
331 super(NotebookApp, self).parse_command_line(argv)
342 super(NotebookApp, self).parse_command_line(argv)
332 if argv is None:
343 if argv is None:
333 argv = sys.argv[1:]
344 argv = sys.argv[1:]
334
345
335 # Scrub frontend-specific flags
346 # Scrub frontend-specific flags
336 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
347 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
337 # Kernel should inherit default config file from frontend
348 # Kernel should inherit default config file from frontend
338 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
349 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
339
350
340 def init_configurables(self):
351 def init_configurables(self):
341 # force Session default to be secure
352 # force Session default to be secure
342 default_secure(self.config)
353 default_secure(self.config)
343 # Create a KernelManager and start a kernel.
354 # Create a KernelManager and start a kernel.
344 self.kernel_manager = MappingKernelManager(
355 self.kernel_manager = MappingKernelManager(
345 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
356 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
346 connection_dir = self.profile_dir.security_dir,
357 connection_dir = self.profile_dir.security_dir,
347 )
358 )
348 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
359 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
349 self.notebook_manager.list_notebooks()
360 self.notebook_manager.list_notebooks()
350
361
351 def init_logging(self):
362 def init_logging(self):
352 super(NotebookApp, self).init_logging()
363 super(NotebookApp, self).init_logging()
353 # This prevents double log messages because tornado use a root logger that
364 # This prevents double log messages because tornado use a root logger that
354 # self.log is a child of. The logging module dipatches log messages to a log
365 # self.log is a child of. The logging module dipatches log messages to a log
355 # and all of its ancenstors until propagate is set to False.
366 # and all of its ancenstors until propagate is set to False.
356 self.log.propagate = False
367 self.log.propagate = False
357
368
358 def init_webapp(self):
369 def init_webapp(self):
359 """initialize tornado webapp and httpserver"""
370 """initialize tornado webapp and httpserver"""
360 self.web_app = NotebookWebApplication(
371 self.web_app = NotebookWebApplication(
361 self, self.kernel_manager, self.notebook_manager, self.log,
372 self, self.kernel_manager, self.notebook_manager, self.log,
362 self.base_project_url, self.webapp_settings
373 self.base_project_url, self.webapp_settings
363 )
374 )
364 if self.certfile:
375 if self.certfile:
365 ssl_options = dict(certfile=self.certfile)
376 ssl_options = dict(certfile=self.certfile)
366 if self.keyfile:
377 if self.keyfile:
367 ssl_options['keyfile'] = self.keyfile
378 ssl_options['keyfile'] = self.keyfile
368 else:
379 else:
369 ssl_options = None
380 ssl_options = None
370 self.web_app.password = self.password
381 self.web_app.password = self.password
371 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
382 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
372 if ssl_options is None and not self.ip and not (self.read_only and not self.password):
383 if ssl_options is None and not self.ip and not (self.read_only and not self.password):
373 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
384 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
374 'but not using any encryption or authentication. This is highly '
385 'but not using any encryption or authentication. This is highly '
375 'insecure and not recommended.')
386 'insecure and not recommended.')
376
387
377 # Try random ports centered around the default.
388 # Try random ports centered around the default.
378 from random import randint
389 from random import randint
379 n = 50 # Max number of attempts, keep reasonably large.
390 n = 50 # Max number of attempts, keep reasonably large.
380 for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
391 for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
381 try:
392 try:
382 self.http_server.listen(port, self.ip)
393 self.http_server.listen(port, self.ip)
383 except socket.error, e:
394 except socket.error, e:
384 if e.errno != errno.EADDRINUSE:
395 if e.errno != errno.EADDRINUSE:
385 raise
396 raise
386 self.log.info('The port %i is already in use, trying another random port.' % port)
397 self.log.info('The port %i is already in use, trying another random port.' % port)
387 else:
398 else:
388 self.port = port
399 self.port = port
389 break
400 break
390
401
391 @catch_config_error
402 @catch_config_error
392 def initialize(self, argv=None):
403 def initialize(self, argv=None):
393 super(NotebookApp, self).initialize(argv)
404 super(NotebookApp, self).initialize(argv)
394 self.init_configurables()
405 self.init_configurables()
395 self.init_webapp()
406 self.init_webapp()
396
407
397 def cleanup_kernels(self):
408 def cleanup_kernels(self):
398 """shutdown all kernels
409 """shutdown all kernels
399
410
400 The kernels will shutdown themselves when this process no longer exists,
411 The kernels will shutdown themselves when this process no longer exists,
401 but explicit shutdown allows the KernelManagers to cleanup the connection files.
412 but explicit shutdown allows the KernelManagers to cleanup the connection files.
402 """
413 """
403 self.log.info('Shutting down kernels')
414 self.log.info('Shutting down kernels')
404 km = self.kernel_manager
415 km = self.kernel_manager
405 # copy list, since kill_kernel deletes keys
416 # copy list, since kill_kernel deletes keys
406 for kid in list(km.kernel_ids):
417 for kid in list(km.kernel_ids):
407 km.kill_kernel(kid)
418 km.kill_kernel(kid)
408
419
409 def start(self):
420 def start(self):
410 ip = self.ip if self.ip else '[all ip addresses on your system]'
421 ip = self.ip if self.ip else '[all ip addresses on your system]'
411 proto = 'https' if self.certfile else 'http'
422 proto = 'https' if self.certfile else 'http'
412 info = self.log.info
423 info = self.log.info
413 info("The IPython Notebook is running at: %s://%s:%i%s" %
424 info("The IPython Notebook is running at: %s://%s:%i%s" %
414 (proto, ip, self.port,self.base_project_url) )
425 (proto, ip, self.port,self.base_project_url) )
415 info("Use Control-C to stop this server and shut down all kernels.")
426 info("Use Control-C to stop this server and shut down all kernels.")
416
427
417 if self.open_browser:
428 if self.open_browser:
418 ip = self.ip or '127.0.0.1'
429 ip = self.ip or '127.0.0.1'
419 b = lambda : webbrowser.open("%s://%s:%i%s" % (proto, ip, self.port,
430 b = lambda : webbrowser.open("%s://%s:%i%s" % (proto, ip, self.port,
420 self.base_project_url),
431 self.base_project_url),
421 new=2)
432 new=2)
422 threading.Thread(target=b).start()
433 threading.Thread(target=b).start()
423 try:
434 try:
424 ioloop.IOLoop.instance().start()
435 ioloop.IOLoop.instance().start()
425 except KeyboardInterrupt:
436 except KeyboardInterrupt:
426 info("Interrupted...")
437 info("Interrupted...")
427 finally:
438 finally:
428 self.cleanup_kernels()
439 self.cleanup_kernels()
429
440
430
441
431 #-----------------------------------------------------------------------------
442 #-----------------------------------------------------------------------------
432 # Main entry point
443 # Main entry point
433 #-----------------------------------------------------------------------------
444 #-----------------------------------------------------------------------------
434
445
435 def launch_new_instance():
446 def launch_new_instance():
436 app = NotebookApp.instance()
447 app = NotebookApp.instance()
437 app.initialize()
448 app.initialize()
438 app.start()
449 app.start()
439
450
General Comments 0
You need to be logged in to leave comments. Login now