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