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