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