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