##// END OF EJS Templates
hook up tornado 3's loggers to our handlers
MinRK -
Show More
@@ -1,728 +1,740
1 # coding: utf-8
1 # coding: utf-8
2 """A tornado based IPython notebook server.
2 """A tornado based IPython notebook server.
3
3
4 Authors:
4 Authors:
5
5
6 * Brian Granger
6 * Brian Granger
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 random
23 import random
24 import re
24 import re
25 import select
25 import select
26 import signal
26 import signal
27 import socket
27 import socket
28 import sys
28 import sys
29 import threading
29 import threading
30 import time
30 import time
31 import uuid
31 import uuid
32 import webbrowser
32 import webbrowser
33
33
34
34
35 # Third party
35 # Third party
36 # check for pyzmq 2.1.11
36 # check for pyzmq 2.1.11
37 from IPython.utils.zmqrelated import check_for_zmq
37 from IPython.utils.zmqrelated import check_for_zmq
38 check_for_zmq('2.1.11', 'IPython.frontend.html.notebook')
38 check_for_zmq('2.1.11', 'IPython.frontend.html.notebook')
39
39
40 import zmq
40 import zmq
41 from jinja2 import Environment, FileSystemLoader
41 from jinja2 import Environment, FileSystemLoader
42
42
43 # Install the pyzmq ioloop. This has to be done before anything else from
43 # Install the pyzmq ioloop. This has to be done before anything else from
44 # tornado is imported.
44 # tornado is imported.
45 from zmq.eventloop import ioloop
45 from zmq.eventloop import ioloop
46 ioloop.install()
46 ioloop.install()
47
47
48 # check for tornado 2.1.0
48 # check for tornado 2.1.0
49 msg = "The IPython Notebook requires tornado >= 2.1.0"
49 msg = "The IPython Notebook requires tornado >= 2.1.0"
50 try:
50 try:
51 import tornado
51 import tornado
52 except ImportError:
52 except ImportError:
53 raise ImportError(msg)
53 raise ImportError(msg)
54 try:
54 try:
55 version_info = tornado.version_info
55 version_info = tornado.version_info
56 except AttributeError:
56 except AttributeError:
57 raise ImportError(msg + ", but you have < 1.1.0")
57 raise ImportError(msg + ", but you have < 1.1.0")
58 if version_info < (2,1,0):
58 if version_info < (2,1,0):
59 raise ImportError(msg + ", but you have %s" % tornado.version)
59 raise ImportError(msg + ", but you have %s" % tornado.version)
60
60
61 from tornado import httpserver
61 from tornado import httpserver
62 from tornado import web
62 from tornado import web
63
63
64 # Our own libraries
64 # Our own libraries
65 from IPython.frontend.html.notebook import DEFAULT_STATIC_FILES_PATH
65 from IPython.frontend.html.notebook import DEFAULT_STATIC_FILES_PATH
66 from .kernelmanager import MappingKernelManager
66 from .kernelmanager import MappingKernelManager
67 from .handlers import (LoginHandler, LogoutHandler,
67 from .handlers import (LoginHandler, LogoutHandler,
68 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
68 ProjectDashboardHandler, NewHandler, NamedNotebookHandler,
69 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
69 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
70 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
70 ShellHandler, NotebookRootHandler, NotebookHandler, NotebookCopyHandler,
71 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
71 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler,
72 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
72 MainClusterHandler, ClusterProfileHandler, ClusterActionHandler,
73 FileFindHandler, NotebookRedirectHandler,
73 FileFindHandler, NotebookRedirectHandler,
74 )
74 )
75 from .nbmanager import NotebookManager
75 from .nbmanager import NotebookManager
76 from .filenbmanager import FileNotebookManager
76 from .filenbmanager import FileNotebookManager
77 from .clustermanager import ClusterManager
77 from .clustermanager import ClusterManager
78
78
79 from IPython.config.application import catch_config_error, boolean_flag
79 from IPython.config.application import catch_config_error, boolean_flag
80 from IPython.core.application import BaseIPythonApplication
80 from IPython.core.application import BaseIPythonApplication
81 from IPython.core.profiledir import ProfileDir
81 from IPython.core.profiledir import ProfileDir
82 from IPython.frontend.consoleapp import IPythonConsoleApp
82 from IPython.frontend.consoleapp import IPythonConsoleApp
83 from IPython.kernel import swallow_argv
83 from IPython.kernel import swallow_argv
84 from IPython.kernel.zmq.session import Session, default_secure
84 from IPython.kernel.zmq.session import Session, default_secure
85 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
85 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
86 from IPython.kernel.zmq.kernelapp import (
86 from IPython.kernel.zmq.kernelapp import (
87 kernel_flags,
87 kernel_flags,
88 kernel_aliases,
88 kernel_aliases,
89 IPKernelApp
89 IPKernelApp
90 )
90 )
91 from IPython.utils.importstring import import_item
91 from IPython.utils.importstring import import_item
92 from IPython.utils.localinterfaces import LOCALHOST
92 from IPython.utils.localinterfaces import LOCALHOST
93 from IPython.utils.traitlets import (
93 from IPython.utils.traitlets import (
94 Dict, Unicode, Integer, List, Enum, Bool,
94 Dict, Unicode, Integer, List, Enum, Bool,
95 DottedObjectName
95 DottedObjectName
96 )
96 )
97 from IPython.utils import py3compat
97 from IPython.utils import py3compat
98 from IPython.utils.path import filefind
98 from IPython.utils.path import filefind
99
99
100 #-----------------------------------------------------------------------------
100 #-----------------------------------------------------------------------------
101 # Module globals
101 # Module globals
102 #-----------------------------------------------------------------------------
102 #-----------------------------------------------------------------------------
103
103
104 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
104 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
105 _kernel_action_regex = r"(?P<action>restart|interrupt)"
105 _kernel_action_regex = r"(?P<action>restart|interrupt)"
106 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
106 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
107 _notebook_name_regex = r"(?P<notebook_name>.+\.ipynb)"
107 _notebook_name_regex = r"(?P<notebook_name>.+\.ipynb)"
108 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
108 _profile_regex = r"(?P<profile>[^\/]+)" # there is almost no text that is invalid
109 _cluster_action_regex = r"(?P<action>start|stop)"
109 _cluster_action_regex = r"(?P<action>start|stop)"
110
110
111 _examples = """
111 _examples = """
112 ipython notebook # start the notebook
112 ipython notebook # start the notebook
113 ipython notebook --profile=sympy # use the sympy profile
113 ipython notebook --profile=sympy # use the sympy profile
114 ipython notebook --pylab=inline # pylab in inline plotting mode
114 ipython notebook --pylab=inline # pylab in inline plotting mode
115 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
115 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
116 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
116 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
117 """
117 """
118
118
119 #-----------------------------------------------------------------------------
119 #-----------------------------------------------------------------------------
120 # Helper functions
120 # Helper functions
121 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
122
122
123 def url_path_join(a,b):
123 def url_path_join(a,b):
124 if a.endswith('/') and b.startswith('/'):
124 if a.endswith('/') and b.startswith('/'):
125 return a[:-1]+b
125 return a[:-1]+b
126 else:
126 else:
127 return a+b
127 return a+b
128
128
129 def random_ports(port, n):
129 def random_ports(port, n):
130 """Generate a list of n random ports near the given port.
130 """Generate a list of n random ports near the given port.
131
131
132 The first 5 ports will be sequential, and the remaining n-5 will be
132 The first 5 ports will be sequential, and the remaining n-5 will be
133 randomly selected in the range [port-2*n, port+2*n].
133 randomly selected in the range [port-2*n, port+2*n].
134 """
134 """
135 for i in range(min(5, n)):
135 for i in range(min(5, n)):
136 yield port + i
136 yield port + i
137 for i in range(n-5):
137 for i in range(n-5):
138 yield port + random.randint(-2*n, 2*n)
138 yield port + random.randint(-2*n, 2*n)
139
139
140 #-----------------------------------------------------------------------------
140 #-----------------------------------------------------------------------------
141 # The Tornado web application
141 # The Tornado web application
142 #-----------------------------------------------------------------------------
142 #-----------------------------------------------------------------------------
143
143
144 class NotebookWebApplication(web.Application):
144 class NotebookWebApplication(web.Application):
145
145
146 def __init__(self, ipython_app, kernel_manager, notebook_manager,
146 def __init__(self, ipython_app, kernel_manager, notebook_manager,
147 cluster_manager, log,
147 cluster_manager, log,
148 base_project_url, settings_overrides):
148 base_project_url, settings_overrides):
149 handlers = [
149 handlers = [
150 (r"/", ProjectDashboardHandler),
150 (r"/", ProjectDashboardHandler),
151 (r"/login", LoginHandler),
151 (r"/login", LoginHandler),
152 (r"/logout", LogoutHandler),
152 (r"/logout", LogoutHandler),
153 (r"/new", NewHandler),
153 (r"/new", NewHandler),
154 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
154 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
155 (r"/%s" % _notebook_name_regex, NotebookRedirectHandler),
155 (r"/%s" % _notebook_name_regex, NotebookRedirectHandler),
156 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
156 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
157 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
157 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
158 (r"/kernels", MainKernelHandler),
158 (r"/kernels", MainKernelHandler),
159 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
159 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
160 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
160 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
161 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
161 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
162 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
162 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
163 (r"/notebooks", NotebookRootHandler),
163 (r"/notebooks", NotebookRootHandler),
164 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
164 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
165 (r"/rstservice/render", RSTHandler),
165 (r"/rstservice/render", RSTHandler),
166 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
166 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
167 (r"/clusters", MainClusterHandler),
167 (r"/clusters", MainClusterHandler),
168 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
168 (r"/clusters/%s/%s" % (_profile_regex, _cluster_action_regex), ClusterActionHandler),
169 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
169 (r"/clusters/%s" % _profile_regex, ClusterProfileHandler),
170 ]
170 ]
171
171
172 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
172 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
173 # base_project_url will always be unicode, which will in turn
173 # base_project_url will always be unicode, which will in turn
174 # make the patterns unicode, and ultimately result in unicode
174 # make the patterns unicode, and ultimately result in unicode
175 # keys in kwargs to handler._execute(**kwargs) in tornado.
175 # keys in kwargs to handler._execute(**kwargs) in tornado.
176 # This enforces that base_project_url be ascii in that situation.
176 # This enforces that base_project_url be ascii in that situation.
177 #
177 #
178 # Note that the URLs these patterns check against are escaped,
178 # Note that the URLs these patterns check against are escaped,
179 # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'.
179 # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'.
180 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
180 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
181 template_path = os.path.join(os.path.dirname(__file__), "templates")
181 template_path = os.path.join(os.path.dirname(__file__), "templates")
182 settings = dict(
182 settings = dict(
183 # basics
183 # basics
184 base_project_url=base_project_url,
184 base_project_url=base_project_url,
185 base_kernel_url=ipython_app.base_kernel_url,
185 base_kernel_url=ipython_app.base_kernel_url,
186 template_path=template_path,
186 template_path=template_path,
187 static_path=ipython_app.static_file_path,
187 static_path=ipython_app.static_file_path,
188 static_handler_class = FileFindHandler,
188 static_handler_class = FileFindHandler,
189 static_url_prefix = url_path_join(base_project_url,'/static/'),
189 static_url_prefix = url_path_join(base_project_url,'/static/'),
190
190
191 # authentication
191 # authentication
192 cookie_secret=os.urandom(1024),
192 cookie_secret=os.urandom(1024),
193 login_url=url_path_join(base_project_url,'/login'),
193 login_url=url_path_join(base_project_url,'/login'),
194 cookie_name='username-%s' % uuid.uuid4(),
194 cookie_name='username-%s' % uuid.uuid4(),
195 read_only=ipython_app.read_only,
195 read_only=ipython_app.read_only,
196 password=ipython_app.password,
196 password=ipython_app.password,
197
197
198 # managers
198 # managers
199 kernel_manager=kernel_manager,
199 kernel_manager=kernel_manager,
200 notebook_manager=notebook_manager,
200 notebook_manager=notebook_manager,
201 cluster_manager=cluster_manager,
201 cluster_manager=cluster_manager,
202
202
203 # IPython stuff
203 # IPython stuff
204 max_msg_size=ipython_app.max_msg_size,
204 max_msg_size=ipython_app.max_msg_size,
205 config=ipython_app.config,
205 config=ipython_app.config,
206 use_less=ipython_app.use_less,
206 use_less=ipython_app.use_less,
207 jinja2_env=Environment(loader=FileSystemLoader(template_path)),
207 jinja2_env=Environment(loader=FileSystemLoader(template_path)),
208 )
208 )
209
209
210 # allow custom overrides for the tornado web app.
210 # allow custom overrides for the tornado web app.
211 settings.update(settings_overrides)
211 settings.update(settings_overrides)
212
212
213 # prepend base_project_url onto the patterns that we match
213 # prepend base_project_url onto the patterns that we match
214 new_handlers = []
214 new_handlers = []
215 for handler in handlers:
215 for handler in handlers:
216 pattern = url_path_join(base_project_url, handler[0])
216 pattern = url_path_join(base_project_url, handler[0])
217 new_handler = tuple([pattern] + list(handler[1:]))
217 new_handler = tuple([pattern] + list(handler[1:]))
218 new_handlers.append(new_handler)
218 new_handlers.append(new_handler)
219
219
220 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
220 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
221
221
222
222
223
223
224 #-----------------------------------------------------------------------------
224 #-----------------------------------------------------------------------------
225 # Aliases and Flags
225 # Aliases and Flags
226 #-----------------------------------------------------------------------------
226 #-----------------------------------------------------------------------------
227
227
228 flags = dict(kernel_flags)
228 flags = dict(kernel_flags)
229 flags['no-browser']=(
229 flags['no-browser']=(
230 {'NotebookApp' : {'open_browser' : False}},
230 {'NotebookApp' : {'open_browser' : False}},
231 "Don't open the notebook in a browser after startup."
231 "Don't open the notebook in a browser after startup."
232 )
232 )
233 flags['no-mathjax']=(
233 flags['no-mathjax']=(
234 {'NotebookApp' : {'enable_mathjax' : False}},
234 {'NotebookApp' : {'enable_mathjax' : False}},
235 """Disable MathJax
235 """Disable MathJax
236
236
237 MathJax is the javascript library IPython uses to render math/LaTeX. It is
237 MathJax is the javascript library IPython uses to render math/LaTeX. It is
238 very large, so you may want to disable it if you have a slow internet
238 very large, so you may want to disable it if you have a slow internet
239 connection, or for offline use of the notebook.
239 connection, or for offline use of the notebook.
240
240
241 When disabled, equations etc. will appear as their untransformed TeX source.
241 When disabled, equations etc. will appear as their untransformed TeX source.
242 """
242 """
243 )
243 )
244 flags['read-only'] = (
244 flags['read-only'] = (
245 {'NotebookApp' : {'read_only' : True}},
245 {'NotebookApp' : {'read_only' : True}},
246 """Allow read-only access to notebooks.
246 """Allow read-only access to notebooks.
247
247
248 When using a password to protect the notebook server, this flag
248 When using a password to protect the notebook server, this flag
249 allows unauthenticated clients to view the notebook list, and
249 allows unauthenticated clients to view the notebook list, and
250 individual notebooks, but not edit them, start kernels, or run
250 individual notebooks, but not edit them, start kernels, or run
251 code.
251 code.
252
252
253 If no password is set, the server will be entirely read-only.
253 If no password is set, the server will be entirely read-only.
254 """
254 """
255 )
255 )
256
256
257 # Add notebook manager flags
257 # Add notebook manager flags
258 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
258 flags.update(boolean_flag('script', 'FileNotebookManager.save_script',
259 'Auto-save a .py script everytime the .ipynb notebook is saved',
259 'Auto-save a .py script everytime the .ipynb notebook is saved',
260 'Do not auto-save .py scripts for every notebook'))
260 'Do not auto-save .py scripts for every notebook'))
261
261
262 # the flags that are specific to the frontend
262 # the flags that are specific to the frontend
263 # these must be scrubbed before being passed to the kernel,
263 # these must be scrubbed before being passed to the kernel,
264 # or it will raise an error on unrecognized flags
264 # or it will raise an error on unrecognized flags
265 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
265 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
266
266
267 aliases = dict(kernel_aliases)
267 aliases = dict(kernel_aliases)
268
268
269 aliases.update({
269 aliases.update({
270 'ip': 'NotebookApp.ip',
270 'ip': 'NotebookApp.ip',
271 'port': 'NotebookApp.port',
271 'port': 'NotebookApp.port',
272 'port-retries': 'NotebookApp.port_retries',
272 'port-retries': 'NotebookApp.port_retries',
273 'transport': 'KernelManager.transport',
273 'transport': 'KernelManager.transport',
274 'keyfile': 'NotebookApp.keyfile',
274 'keyfile': 'NotebookApp.keyfile',
275 'certfile': 'NotebookApp.certfile',
275 'certfile': 'NotebookApp.certfile',
276 'notebook-dir': 'NotebookManager.notebook_dir',
276 'notebook-dir': 'NotebookManager.notebook_dir',
277 'browser': 'NotebookApp.browser',
277 'browser': 'NotebookApp.browser',
278 })
278 })
279
279
280 # remove ipkernel flags that are singletons, and don't make sense in
280 # remove ipkernel flags that are singletons, and don't make sense in
281 # multi-kernel evironment:
281 # multi-kernel evironment:
282 aliases.pop('f', None)
282 aliases.pop('f', None)
283
283
284 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
284 notebook_aliases = [u'port', u'port-retries', u'ip', u'keyfile', u'certfile',
285 u'notebook-dir']
285 u'notebook-dir']
286
286
287 #-----------------------------------------------------------------------------
287 #-----------------------------------------------------------------------------
288 # NotebookApp
288 # NotebookApp
289 #-----------------------------------------------------------------------------
289 #-----------------------------------------------------------------------------
290
290
291 class NotebookApp(BaseIPythonApplication):
291 class NotebookApp(BaseIPythonApplication):
292
292
293 name = 'ipython-notebook'
293 name = 'ipython-notebook'
294 default_config_file_name='ipython_notebook_config.py'
294 default_config_file_name='ipython_notebook_config.py'
295
295
296 description = """
296 description = """
297 The IPython HTML Notebook.
297 The IPython HTML Notebook.
298
298
299 This launches a Tornado based HTML Notebook Server that serves up an
299 This launches a Tornado based HTML Notebook Server that serves up an
300 HTML5/Javascript Notebook client.
300 HTML5/Javascript Notebook client.
301 """
301 """
302 examples = _examples
302 examples = _examples
303
303
304 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
304 classes = IPythonConsoleApp.classes + [MappingKernelManager, NotebookManager,
305 FileNotebookManager]
305 FileNotebookManager]
306 flags = Dict(flags)
306 flags = Dict(flags)
307 aliases = Dict(aliases)
307 aliases = Dict(aliases)
308
308
309 kernel_argv = List(Unicode)
309 kernel_argv = List(Unicode)
310
310
311 max_msg_size = Integer(65536, config=True, help="""
311 max_msg_size = Integer(65536, config=True, help="""
312 The max raw message size accepted from the browser
312 The max raw message size accepted from the browser
313 over a WebSocket connection.
313 over a WebSocket connection.
314 """)
314 """)
315
315
316 def _log_level_default(self):
316 def _log_level_default(self):
317 return logging.INFO
317 return logging.INFO
318
318
319 def _log_format_default(self):
320 """override default log format to include time"""
321 return u"%(asctime)s.%(msecs).03d [%(name)s] %(message)s"
322
319 # create requested profiles by default, if they don't exist:
323 # create requested profiles by default, if they don't exist:
320 auto_create = Bool(True)
324 auto_create = Bool(True)
321
325
322 # file to be opened in the notebook server
326 # file to be opened in the notebook server
323 file_to_run = Unicode('')
327 file_to_run = Unicode('')
324
328
325 # Network related information.
329 # Network related information.
326
330
327 ip = Unicode(LOCALHOST, config=True,
331 ip = Unicode(LOCALHOST, config=True,
328 help="The IP address the notebook server will listen on."
332 help="The IP address the notebook server will listen on."
329 )
333 )
330
334
331 def _ip_changed(self, name, old, new):
335 def _ip_changed(self, name, old, new):
332 if new == u'*': self.ip = u''
336 if new == u'*': self.ip = u''
333
337
334 port = Integer(8888, config=True,
338 port = Integer(8888, config=True,
335 help="The port the notebook server will listen on."
339 help="The port the notebook server will listen on."
336 )
340 )
337 port_retries = Integer(50, config=True,
341 port_retries = Integer(50, config=True,
338 help="The number of additional ports to try if the specified port is not available."
342 help="The number of additional ports to try if the specified port is not available."
339 )
343 )
340
344
341 certfile = Unicode(u'', config=True,
345 certfile = Unicode(u'', config=True,
342 help="""The full path to an SSL/TLS certificate file."""
346 help="""The full path to an SSL/TLS certificate file."""
343 )
347 )
344
348
345 keyfile = Unicode(u'', config=True,
349 keyfile = Unicode(u'', config=True,
346 help="""The full path to a private key file for usage with SSL/TLS."""
350 help="""The full path to a private key file for usage with SSL/TLS."""
347 )
351 )
348
352
349 password = Unicode(u'', config=True,
353 password = Unicode(u'', config=True,
350 help="""Hashed password to use for web authentication.
354 help="""Hashed password to use for web authentication.
351
355
352 To generate, type in a python/IPython shell:
356 To generate, type in a python/IPython shell:
353
357
354 from IPython.lib import passwd; passwd()
358 from IPython.lib import passwd; passwd()
355
359
356 The string should be of the form type:salt:hashed-password.
360 The string should be of the form type:salt:hashed-password.
357 """
361 """
358 )
362 )
359
363
360 open_browser = Bool(True, config=True,
364 open_browser = Bool(True, config=True,
361 help="""Whether to open in a browser after starting.
365 help="""Whether to open in a browser after starting.
362 The specific browser used is platform dependent and
366 The specific browser used is platform dependent and
363 determined by the python standard library `webbrowser`
367 determined by the python standard library `webbrowser`
364 module, unless it is overridden using the --browser
368 module, unless it is overridden using the --browser
365 (NotebookApp.browser) configuration option.
369 (NotebookApp.browser) configuration option.
366 """)
370 """)
367
371
368 browser = Unicode(u'', config=True,
372 browser = Unicode(u'', config=True,
369 help="""Specify what command to use to invoke a web
373 help="""Specify what command to use to invoke a web
370 browser when opening the notebook. If not specified, the
374 browser when opening the notebook. If not specified, the
371 default browser will be determined by the `webbrowser`
375 default browser will be determined by the `webbrowser`
372 standard library module, which allows setting of the
376 standard library module, which allows setting of the
373 BROWSER environment variable to override it.
377 BROWSER environment variable to override it.
374 """)
378 """)
375
379
376 read_only = Bool(False, config=True,
380 read_only = Bool(False, config=True,
377 help="Whether to prevent editing/execution of notebooks."
381 help="Whether to prevent editing/execution of notebooks."
378 )
382 )
379
383
380 use_less = Bool(False, config=True,
384 use_less = Bool(False, config=True,
381 help="""Wether to use Browser Side less-css parsing
385 help="""Wether to use Browser Side less-css parsing
382 instead of compiled css version in templates that allows
386 instead of compiled css version in templates that allows
383 it. This is mainly convenient when working on the less
387 it. This is mainly convenient when working on the less
384 file to avoid a build step, or if user want to overwrite
388 file to avoid a build step, or if user want to overwrite
385 some of the less variables without having to recompile
389 some of the less variables without having to recompile
386 everything.
390 everything.
387
391
388 You will need to install the less.js component in the static directory
392 You will need to install the less.js component in the static directory
389 either in the source tree or in your profile folder.
393 either in the source tree or in your profile folder.
390 """)
394 """)
391
395
392 webapp_settings = Dict(config=True,
396 webapp_settings = Dict(config=True,
393 help="Supply overrides for the tornado.web.Application that the "
397 help="Supply overrides for the tornado.web.Application that the "
394 "IPython notebook uses.")
398 "IPython notebook uses.")
395
399
396 enable_mathjax = Bool(True, config=True,
400 enable_mathjax = Bool(True, config=True,
397 help="""Whether to enable MathJax for typesetting math/TeX
401 help="""Whether to enable MathJax for typesetting math/TeX
398
402
399 MathJax is the javascript library IPython uses to render math/LaTeX. It is
403 MathJax is the javascript library IPython uses to render math/LaTeX. It is
400 very large, so you may want to disable it if you have a slow internet
404 very large, so you may want to disable it if you have a slow internet
401 connection, or for offline use of the notebook.
405 connection, or for offline use of the notebook.
402
406
403 When disabled, equations etc. will appear as their untransformed TeX source.
407 When disabled, equations etc. will appear as their untransformed TeX source.
404 """
408 """
405 )
409 )
406 def _enable_mathjax_changed(self, name, old, new):
410 def _enable_mathjax_changed(self, name, old, new):
407 """set mathjax url to empty if mathjax is disabled"""
411 """set mathjax url to empty if mathjax is disabled"""
408 if not new:
412 if not new:
409 self.mathjax_url = u''
413 self.mathjax_url = u''
410
414
411 base_project_url = Unicode('/', config=True,
415 base_project_url = Unicode('/', config=True,
412 help='''The base URL for the notebook server.
416 help='''The base URL for the notebook server.
413
417
414 Leading and trailing slashes can be omitted,
418 Leading and trailing slashes can be omitted,
415 and will automatically be added.
419 and will automatically be added.
416 ''')
420 ''')
417 def _base_project_url_changed(self, name, old, new):
421 def _base_project_url_changed(self, name, old, new):
418 if not new.startswith('/'):
422 if not new.startswith('/'):
419 self.base_project_url = '/'+new
423 self.base_project_url = '/'+new
420 elif not new.endswith('/'):
424 elif not new.endswith('/'):
421 self.base_project_url = new+'/'
425 self.base_project_url = new+'/'
422
426
423 base_kernel_url = Unicode('/', config=True,
427 base_kernel_url = Unicode('/', config=True,
424 help='''The base URL for the kernel server
428 help='''The base URL for the kernel server
425
429
426 Leading and trailing slashes can be omitted,
430 Leading and trailing slashes can be omitted,
427 and will automatically be added.
431 and will automatically be added.
428 ''')
432 ''')
429 def _base_kernel_url_changed(self, name, old, new):
433 def _base_kernel_url_changed(self, name, old, new):
430 if not new.startswith('/'):
434 if not new.startswith('/'):
431 self.base_kernel_url = '/'+new
435 self.base_kernel_url = '/'+new
432 elif not new.endswith('/'):
436 elif not new.endswith('/'):
433 self.base_kernel_url = new+'/'
437 self.base_kernel_url = new+'/'
434
438
435 websocket_host = Unicode("", config=True,
439 websocket_host = Unicode("", config=True,
436 help="""The hostname for the websocket server."""
440 help="""The hostname for the websocket server."""
437 )
441 )
438
442
439 extra_static_paths = List(Unicode, config=True,
443 extra_static_paths = List(Unicode, config=True,
440 help="""Extra paths to search for serving static files.
444 help="""Extra paths to search for serving static files.
441
445
442 This allows adding javascript/css to be available from the notebook server machine,
446 This allows adding javascript/css to be available from the notebook server machine,
443 or overriding individual files in the IPython"""
447 or overriding individual files in the IPython"""
444 )
448 )
445 def _extra_static_paths_default(self):
449 def _extra_static_paths_default(self):
446 return [os.path.join(self.profile_dir.location, 'static')]
450 return [os.path.join(self.profile_dir.location, 'static')]
447
451
448 @property
452 @property
449 def static_file_path(self):
453 def static_file_path(self):
450 """return extra paths + the default location"""
454 """return extra paths + the default location"""
451 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
455 return self.extra_static_paths + [DEFAULT_STATIC_FILES_PATH]
452
456
453 mathjax_url = Unicode("", config=True,
457 mathjax_url = Unicode("", config=True,
454 help="""The url for MathJax.js."""
458 help="""The url for MathJax.js."""
455 )
459 )
456 def _mathjax_url_default(self):
460 def _mathjax_url_default(self):
457 if not self.enable_mathjax:
461 if not self.enable_mathjax:
458 return u''
462 return u''
459 static_url_prefix = self.webapp_settings.get("static_url_prefix",
463 static_url_prefix = self.webapp_settings.get("static_url_prefix",
460 "/static/")
464 "/static/")
461 try:
465 try:
462 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
466 mathjax = filefind(os.path.join('mathjax', 'MathJax.js'), self.static_file_path)
463 except IOError:
467 except IOError:
464 if self.certfile:
468 if self.certfile:
465 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
469 # HTTPS: load from Rackspace CDN, because SSL certificate requires it
466 base = u"https://c328740.ssl.cf1.rackcdn.com"
470 base = u"https://c328740.ssl.cf1.rackcdn.com"
467 else:
471 else:
468 base = u"http://cdn.mathjax.org"
472 base = u"http://cdn.mathjax.org"
469
473
470 url = base + u"/mathjax/latest/MathJax.js"
474 url = base + u"/mathjax/latest/MathJax.js"
471 self.log.info("Using MathJax from CDN: %s", url)
475 self.log.info("Using MathJax from CDN: %s", url)
472 return url
476 return url
473 else:
477 else:
474 self.log.info("Using local MathJax from %s" % mathjax)
478 self.log.info("Using local MathJax from %s" % mathjax)
475 return static_url_prefix+u"mathjax/MathJax.js"
479 return static_url_prefix+u"mathjax/MathJax.js"
476
480
477 def _mathjax_url_changed(self, name, old, new):
481 def _mathjax_url_changed(self, name, old, new):
478 if new and not self.enable_mathjax:
482 if new and not self.enable_mathjax:
479 # enable_mathjax=False overrides mathjax_url
483 # enable_mathjax=False overrides mathjax_url
480 self.mathjax_url = u''
484 self.mathjax_url = u''
481 else:
485 else:
482 self.log.info("Using MathJax: %s", new)
486 self.log.info("Using MathJax: %s", new)
483
487
484 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
488 notebook_manager_class = DottedObjectName('IPython.frontend.html.notebook.filenbmanager.FileNotebookManager',
485 config=True,
489 config=True,
486 help='The notebook manager class to use.')
490 help='The notebook manager class to use.')
487
491
488 trust_xheaders = Bool(False, config=True,
492 trust_xheaders = Bool(False, config=True,
489 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
493 help=("Whether to trust or not X-Scheme/X-Forwarded-Proto and X-Real-Ip/X-Forwarded-For headers"
490 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
494 "sent by the upstream reverse proxy. Neccesary if the proxy handles SSL")
491 )
495 )
492
496
493 def parse_command_line(self, argv=None):
497 def parse_command_line(self, argv=None):
494 super(NotebookApp, self).parse_command_line(argv)
498 super(NotebookApp, self).parse_command_line(argv)
495 if argv is None:
499 if argv is None:
496 argv = sys.argv[1:]
500 argv = sys.argv[1:]
497
501
498 # Scrub frontend-specific flags
502 # Scrub frontend-specific flags
499 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
503 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
500 # Kernel should inherit default config file from frontend
504 # Kernel should inherit default config file from frontend
501 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
505 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
502
506
503 if self.extra_args:
507 if self.extra_args:
504 f = os.path.abspath(self.extra_args[0])
508 f = os.path.abspath(self.extra_args[0])
505 if os.path.isdir(f):
509 if os.path.isdir(f):
506 nbdir = f
510 nbdir = f
507 else:
511 else:
508 self.file_to_run = f
512 self.file_to_run = f
509 nbdir = os.path.dirname(f)
513 nbdir = os.path.dirname(f)
510 self.config.NotebookManager.notebook_dir = nbdir
514 self.config.NotebookManager.notebook_dir = nbdir
511
515
512 def init_configurables(self):
516 def init_configurables(self):
513 # force Session default to be secure
517 # force Session default to be secure
514 default_secure(self.config)
518 default_secure(self.config)
515 self.kernel_manager = MappingKernelManager(
519 self.kernel_manager = MappingKernelManager(
516 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
520 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
517 connection_dir = self.profile_dir.security_dir,
521 connection_dir = self.profile_dir.security_dir,
518 )
522 )
519 kls = import_item(self.notebook_manager_class)
523 kls = import_item(self.notebook_manager_class)
520 self.notebook_manager = kls(config=self.config, log=self.log)
524 self.notebook_manager = kls(config=self.config, log=self.log)
521 self.notebook_manager.load_notebook_names()
525 self.notebook_manager.load_notebook_names()
522 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
526 self.cluster_manager = ClusterManager(config=self.config, log=self.log)
523 self.cluster_manager.update_profiles()
527 self.cluster_manager.update_profiles()
524
528
525 def init_logging(self):
529 def init_logging(self):
526 # This prevents double log messages because tornado use a root logger that
530 # This prevents double log messages because tornado use a root logger that
527 # self.log is a child of. The logging module dipatches log messages to a log
531 # self.log is a child of. The logging module dipatches log messages to a log
528 # and all of its ancenstors until propagate is set to False.
532 # and all of its ancenstors until propagate is set to False.
529 self.log.propagate = False
533 self.log.propagate = False
530
534
535 # set the date format
536 formatter = logging.Formatter(self.log_format, datefmt="%Y-%m-%d %H:%M:%S")
537 self.log.handlers[0].setFormatter(formatter)
538
539 # hook up tornado 3's loggers to our app handlers
540 for name in ('access', 'application', 'general'):
541 logging.getLogger('tornado.%s' % name).handlers = self.log.handlers
542
531 def init_webapp(self):
543 def init_webapp(self):
532 """initialize tornado webapp and httpserver"""
544 """initialize tornado webapp and httpserver"""
533 self.web_app = NotebookWebApplication(
545 self.web_app = NotebookWebApplication(
534 self, self.kernel_manager, self.notebook_manager,
546 self, self.kernel_manager, self.notebook_manager,
535 self.cluster_manager, self.log,
547 self.cluster_manager, self.log,
536 self.base_project_url, self.webapp_settings
548 self.base_project_url, self.webapp_settings
537 )
549 )
538 if self.certfile:
550 if self.certfile:
539 ssl_options = dict(certfile=self.certfile)
551 ssl_options = dict(certfile=self.certfile)
540 if self.keyfile:
552 if self.keyfile:
541 ssl_options['keyfile'] = self.keyfile
553 ssl_options['keyfile'] = self.keyfile
542 else:
554 else:
543 ssl_options = None
555 ssl_options = None
544 self.web_app.password = self.password
556 self.web_app.password = self.password
545 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
557 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options,
546 xheaders=self.trust_xheaders)
558 xheaders=self.trust_xheaders)
547 if not self.ip:
559 if not self.ip:
548 warning = "WARNING: The notebook server is listening on all IP addresses"
560 warning = "WARNING: The notebook server is listening on all IP addresses"
549 if ssl_options is None:
561 if ssl_options is None:
550 self.log.critical(warning + " and not using encryption. This"
562 self.log.critical(warning + " and not using encryption. This"
551 "is not recommended.")
563 "is not recommended.")
552 if not self.password and not self.read_only:
564 if not self.password and not self.read_only:
553 self.log.critical(warning + "and not using authentication."
565 self.log.critical(warning + "and not using authentication."
554 "This is highly insecure and not recommended.")
566 "This is highly insecure and not recommended.")
555 success = None
567 success = None
556 for port in random_ports(self.port, self.port_retries+1):
568 for port in random_ports(self.port, self.port_retries+1):
557 try:
569 try:
558 self.http_server.listen(port, self.ip)
570 self.http_server.listen(port, self.ip)
559 except socket.error as e:
571 except socket.error as e:
560 # XXX: remove the e.errno == -9 block when we require
572 # XXX: remove the e.errno == -9 block when we require
561 # tornado >= 3.0
573 # tornado >= 3.0
562 if e.errno == -9 and tornado.version_info[0] < 3:
574 if e.errno == -9 and tornado.version_info[0] < 3:
563 # The flags passed to socket.getaddrinfo from
575 # The flags passed to socket.getaddrinfo from
564 # tornado.netutils.bind_sockets can cause "gaierror:
576 # tornado.netutils.bind_sockets can cause "gaierror:
565 # [Errno -9] Address family for hostname not supported"
577 # [Errno -9] Address family for hostname not supported"
566 # when the interface is not associated, for example.
578 # when the interface is not associated, for example.
567 # Changing the flags to exclude socket.AI_ADDRCONFIG does
579 # Changing the flags to exclude socket.AI_ADDRCONFIG does
568 # not cause this error, but the only way to do this is to
580 # not cause this error, but the only way to do this is to
569 # monkeypatch socket to remove the AI_ADDRCONFIG attribute
581 # monkeypatch socket to remove the AI_ADDRCONFIG attribute
570 saved_AI_ADDRCONFIG = socket.AI_ADDRCONFIG
582 saved_AI_ADDRCONFIG = socket.AI_ADDRCONFIG
571 self.log.warn('Monkeypatching socket to fix tornado bug')
583 self.log.warn('Monkeypatching socket to fix tornado bug')
572 del(socket.AI_ADDRCONFIG)
584 del(socket.AI_ADDRCONFIG)
573 try:
585 try:
574 # retry the tornado call without AI_ADDRCONFIG flags
586 # retry the tornado call without AI_ADDRCONFIG flags
575 self.http_server.listen(port, self.ip)
587 self.http_server.listen(port, self.ip)
576 except socket.error as e2:
588 except socket.error as e2:
577 e = e2
589 e = e2
578 else:
590 else:
579 self.port = port
591 self.port = port
580 success = True
592 success = True
581 break
593 break
582 # restore the monekypatch
594 # restore the monekypatch
583 socket.AI_ADDRCONFIG = saved_AI_ADDRCONFIG
595 socket.AI_ADDRCONFIG = saved_AI_ADDRCONFIG
584 if e.errno != errno.EADDRINUSE:
596 if e.errno != errno.EADDRINUSE:
585 raise
597 raise
586 self.log.info('The port %i is already in use, trying another random port.' % port)
598 self.log.info('The port %i is already in use, trying another random port.' % port)
587 else:
599 else:
588 self.port = port
600 self.port = port
589 success = True
601 success = True
590 break
602 break
591 if not success:
603 if not success:
592 self.log.critical('ERROR: the notebook server could not be started because '
604 self.log.critical('ERROR: the notebook server could not be started because '
593 'no available port could be found.')
605 'no available port could be found.')
594 self.exit(1)
606 self.exit(1)
595
607
596 def init_signal(self):
608 def init_signal(self):
597 if not sys.platform.startswith('win'):
609 if not sys.platform.startswith('win'):
598 signal.signal(signal.SIGINT, self._handle_sigint)
610 signal.signal(signal.SIGINT, self._handle_sigint)
599 signal.signal(signal.SIGTERM, self._signal_stop)
611 signal.signal(signal.SIGTERM, self._signal_stop)
600 if hasattr(signal, 'SIGUSR1'):
612 if hasattr(signal, 'SIGUSR1'):
601 # Windows doesn't support SIGUSR1
613 # Windows doesn't support SIGUSR1
602 signal.signal(signal.SIGUSR1, self._signal_info)
614 signal.signal(signal.SIGUSR1, self._signal_info)
603 if hasattr(signal, 'SIGINFO'):
615 if hasattr(signal, 'SIGINFO'):
604 # only on BSD-based systems
616 # only on BSD-based systems
605 signal.signal(signal.SIGINFO, self._signal_info)
617 signal.signal(signal.SIGINFO, self._signal_info)
606
618
607 def _handle_sigint(self, sig, frame):
619 def _handle_sigint(self, sig, frame):
608 """SIGINT handler spawns confirmation dialog"""
620 """SIGINT handler spawns confirmation dialog"""
609 # register more forceful signal handler for ^C^C case
621 # register more forceful signal handler for ^C^C case
610 signal.signal(signal.SIGINT, self._signal_stop)
622 signal.signal(signal.SIGINT, self._signal_stop)
611 # request confirmation dialog in bg thread, to avoid
623 # request confirmation dialog in bg thread, to avoid
612 # blocking the App
624 # blocking the App
613 thread = threading.Thread(target=self._confirm_exit)
625 thread = threading.Thread(target=self._confirm_exit)
614 thread.daemon = True
626 thread.daemon = True
615 thread.start()
627 thread.start()
616
628
617 def _restore_sigint_handler(self):
629 def _restore_sigint_handler(self):
618 """callback for restoring original SIGINT handler"""
630 """callback for restoring original SIGINT handler"""
619 signal.signal(signal.SIGINT, self._handle_sigint)
631 signal.signal(signal.SIGINT, self._handle_sigint)
620
632
621 def _confirm_exit(self):
633 def _confirm_exit(self):
622 """confirm shutdown on ^C
634 """confirm shutdown on ^C
623
635
624 A second ^C, or answering 'y' within 5s will cause shutdown,
636 A second ^C, or answering 'y' within 5s will cause shutdown,
625 otherwise original SIGINT handler will be restored.
637 otherwise original SIGINT handler will be restored.
626
638
627 This doesn't work on Windows.
639 This doesn't work on Windows.
628 """
640 """
629 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
641 # FIXME: remove this delay when pyzmq dependency is >= 2.1.11
630 time.sleep(0.1)
642 time.sleep(0.1)
631 info = self.log.info
643 info = self.log.info
632 info('interrupted')
644 info('interrupted')
633 print self.notebook_info()
645 print self.notebook_info()
634 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
646 sys.stdout.write("Shutdown this notebook server (y/[n])? ")
635 sys.stdout.flush()
647 sys.stdout.flush()
636 r,w,x = select.select([sys.stdin], [], [], 5)
648 r,w,x = select.select([sys.stdin], [], [], 5)
637 if r:
649 if r:
638 line = sys.stdin.readline()
650 line = sys.stdin.readline()
639 if line.lower().startswith('y'):
651 if line.lower().startswith('y'):
640 self.log.critical("Shutdown confirmed")
652 self.log.critical("Shutdown confirmed")
641 ioloop.IOLoop.instance().stop()
653 ioloop.IOLoop.instance().stop()
642 return
654 return
643 else:
655 else:
644 print "No answer for 5s:",
656 print "No answer for 5s:",
645 print "resuming operation..."
657 print "resuming operation..."
646 # no answer, or answer is no:
658 # no answer, or answer is no:
647 # set it back to original SIGINT handler
659 # set it back to original SIGINT handler
648 # use IOLoop.add_callback because signal.signal must be called
660 # use IOLoop.add_callback because signal.signal must be called
649 # from main thread
661 # from main thread
650 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
662 ioloop.IOLoop.instance().add_callback(self._restore_sigint_handler)
651
663
652 def _signal_stop(self, sig, frame):
664 def _signal_stop(self, sig, frame):
653 self.log.critical("received signal %s, stopping", sig)
665 self.log.critical("received signal %s, stopping", sig)
654 ioloop.IOLoop.instance().stop()
666 ioloop.IOLoop.instance().stop()
655
667
656 def _signal_info(self, sig, frame):
668 def _signal_info(self, sig, frame):
657 print self.notebook_info()
669 print self.notebook_info()
658
670
659 @catch_config_error
671 @catch_config_error
660 def initialize(self, argv=None):
672 def initialize(self, argv=None):
661 self.init_logging()
673 self.init_logging()
662 super(NotebookApp, self).initialize(argv)
674 super(NotebookApp, self).initialize(argv)
663 self.init_configurables()
675 self.init_configurables()
664 self.init_webapp()
676 self.init_webapp()
665 self.init_signal()
677 self.init_signal()
666
678
667 def cleanup_kernels(self):
679 def cleanup_kernels(self):
668 """Shutdown all kernels.
680 """Shutdown all kernels.
669
681
670 The kernels will shutdown themselves when this process no longer exists,
682 The kernels will shutdown themselves when this process no longer exists,
671 but explicit shutdown allows the KernelManagers to cleanup the connection files.
683 but explicit shutdown allows the KernelManagers to cleanup the connection files.
672 """
684 """
673 self.log.info('Shutting down kernels')
685 self.log.info('Shutting down kernels')
674 self.kernel_manager.shutdown_all()
686 self.kernel_manager.shutdown_all()
675
687
676 def notebook_info(self):
688 def notebook_info(self):
677 "Return the current working directory and the server url information"
689 "Return the current working directory and the server url information"
678 mgr_info = self.notebook_manager.info_string() + "\n"
690 mgr_info = self.notebook_manager.info_string() + "\n"
679 return mgr_info +"The IPython Notebook is running at: %s" % self._url
691 return mgr_info +"The IPython Notebook is running at: %s" % self._url
680
692
681 def start(self):
693 def start(self):
682 """ Start the IPython Notebok server app, after initialization
694 """ Start the IPython Notebook server app, after initialization
683
695
684 This method takes no arguments so all configuration and initialization
696 This method takes no arguments so all configuration and initialization
685 must be done prior to calling this method."""
697 must be done prior to calling this method."""
686 ip = self.ip if self.ip else '[all ip addresses on your system]'
698 ip = self.ip if self.ip else '[all ip addresses on your system]'
687 proto = 'https' if self.certfile else 'http'
699 proto = 'https' if self.certfile else 'http'
688 info = self.log.info
700 info = self.log.info
689 self._url = "%s://%s:%i%s" % (proto, ip, self.port,
701 self._url = "%s://%s:%i%s" % (proto, ip, self.port,
690 self.base_project_url)
702 self.base_project_url)
691 for line in self.notebook_info().split("\n"):
703 for line in self.notebook_info().split("\n"):
692 info(line)
704 info(line)
693 info("Use Control-C to stop this server and shut down all kernels.")
705 info("Use Control-C to stop this server and shut down all kernels.")
694
706
695 if self.open_browser or self.file_to_run:
707 if self.open_browser or self.file_to_run:
696 ip = self.ip or LOCALHOST
708 ip = self.ip or LOCALHOST
697 try:
709 try:
698 browser = webbrowser.get(self.browser or None)
710 browser = webbrowser.get(self.browser or None)
699 except webbrowser.Error as e:
711 except webbrowser.Error as e:
700 self.log.warn('No web browser found: %s.' % e)
712 self.log.warn('No web browser found: %s.' % e)
701 browser = None
713 browser = None
702
714
703 if self.file_to_run:
715 if self.file_to_run:
704 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
716 name, _ = os.path.splitext(os.path.basename(self.file_to_run))
705 url = self.notebook_manager.rev_mapping.get(name, '')
717 url = self.notebook_manager.rev_mapping.get(name, '')
706 else:
718 else:
707 url = ''
719 url = ''
708 if browser:
720 if browser:
709 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
721 b = lambda : browser.open("%s://%s:%i%s%s" % (proto, ip,
710 self.port, self.base_project_url, url), new=2)
722 self.port, self.base_project_url, url), new=2)
711 threading.Thread(target=b).start()
723 threading.Thread(target=b).start()
712 try:
724 try:
713 ioloop.IOLoop.instance().start()
725 ioloop.IOLoop.instance().start()
714 except KeyboardInterrupt:
726 except KeyboardInterrupt:
715 info("Interrupted...")
727 info("Interrupted...")
716 finally:
728 finally:
717 self.cleanup_kernels()
729 self.cleanup_kernels()
718
730
719
731
720 #-----------------------------------------------------------------------------
732 #-----------------------------------------------------------------------------
721 # Main entry point
733 # Main entry point
722 #-----------------------------------------------------------------------------
734 #-----------------------------------------------------------------------------
723
735
724 def launch_new_instance():
736 def launch_new_instance():
725 app = NotebookApp.instance()
737 app = NotebookApp.instance()
726 app.initialize()
738 app.initialize()
727 app.start()
739 app.start()
728
740
General Comments 0
You need to be logged in to leave comments. Login now