##// END OF EJS Templates
fix inverted self.browser logic
MinRK -
Show More
@@ -1,468 +1,468 b''
1 1 # coding: utf-8
2 2 """A tornado based IPython notebook server.
3 3
4 4 Authors:
5 5
6 6 * Brian Granger
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, NotebookCopyHandler,
52 52 RSTHandler, AuthenticatedFileHandler, PrintNotebookHandler
53 53 )
54 54 from .notebookmanager import NotebookManager
55 55
56 56 from IPython.config.application import catch_config_error, boolean_flag
57 57 from IPython.core.application import BaseIPythonApplication
58 58 from IPython.core.profiledir import ProfileDir
59 59 from IPython.lib.kernel import swallow_argv
60 60 from IPython.zmq.session import Session, default_secure
61 61 from IPython.zmq.zmqshell import ZMQInteractiveShell
62 62 from IPython.zmq.ipkernel import (
63 63 flags as ipkernel_flags,
64 64 aliases as ipkernel_aliases,
65 65 IPKernelApp
66 66 )
67 67 from IPython.utils.traitlets import Dict, Unicode, Integer, List, Enum, Bool
68 68 from IPython.utils import py3compat
69 69
70 70 #-----------------------------------------------------------------------------
71 71 # Module globals
72 72 #-----------------------------------------------------------------------------
73 73
74 74 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
75 75 _kernel_action_regex = r"(?P<action>restart|interrupt)"
76 76 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
77 77
78 78 LOCALHOST = '127.0.0.1'
79 79
80 80 _examples = """
81 81 ipython notebook # start the notebook
82 82 ipython notebook --profile=sympy # use the sympy profile
83 83 ipython notebook --pylab=inline # pylab in inline plotting mode
84 84 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
85 85 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
86 86 """
87 87
88 88 #-----------------------------------------------------------------------------
89 89 # Helper functions
90 90 #-----------------------------------------------------------------------------
91 91
92 92 def url_path_join(a,b):
93 93 if a.endswith('/') and b.startswith('/'):
94 94 return a[:-1]+b
95 95 else:
96 96 return a+b
97 97
98 98 #-----------------------------------------------------------------------------
99 99 # The Tornado web application
100 100 #-----------------------------------------------------------------------------
101 101
102 102 class NotebookWebApplication(web.Application):
103 103
104 104 def __init__(self, ipython_app, kernel_manager, notebook_manager, log,
105 105 base_project_url, settings_overrides):
106 106 handlers = [
107 107 (r"/", ProjectDashboardHandler),
108 108 (r"/login", LoginHandler),
109 109 (r"/logout", LogoutHandler),
110 110 (r"/new", NewHandler),
111 111 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
112 112 (r"/%s/copy" % _notebook_id_regex, NotebookCopyHandler),
113 113 (r"/%s/print" % _notebook_id_regex, PrintNotebookHandler),
114 114 (r"/kernels", MainKernelHandler),
115 115 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
116 116 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
117 117 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
118 118 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
119 119 (r"/notebooks", NotebookRootHandler),
120 120 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
121 121 (r"/rstservice/render", RSTHandler),
122 122 (r"/files/(.*)", AuthenticatedFileHandler, {'path' : notebook_manager.notebook_dir}),
123 123 ]
124 124 settings = dict(
125 125 template_path=os.path.join(os.path.dirname(__file__), "templates"),
126 126 static_path=os.path.join(os.path.dirname(__file__), "static"),
127 127 cookie_secret=os.urandom(1024),
128 128 login_url="/login",
129 129 )
130 130
131 131 # allow custom overrides for the tornado web app.
132 132 settings.update(settings_overrides)
133 133
134 134 # Python < 2.6.5 doesn't accept unicode keys in f(**kwargs), and
135 135 # base_project_url will always be unicode, which will in turn
136 136 # make the patterns unicode, and ultimately result in unicode
137 137 # keys in kwargs to handler._execute(**kwargs) in tornado.
138 138 # This enforces that base_project_url be ascii in that situation.
139 139 #
140 140 # Note that the URLs these patterns check against are escaped,
141 141 # and thus guaranteed to be ASCII: 'hΓ©llo' is really 'h%C3%A9llo'.
142 142 base_project_url = py3compat.unicode_to_str(base_project_url, 'ascii')
143 143
144 144 # prepend base_project_url onto the patterns that we match
145 145 new_handlers = []
146 146 for handler in handlers:
147 147 pattern = url_path_join(base_project_url, handler[0])
148 148 new_handler = tuple([pattern]+list(handler[1:]))
149 149 new_handlers.append( new_handler )
150 150
151 151 super(NotebookWebApplication, self).__init__(new_handlers, **settings)
152 152
153 153 self.kernel_manager = kernel_manager
154 154 self.log = log
155 155 self.notebook_manager = notebook_manager
156 156 self.ipython_app = ipython_app
157 157 self.read_only = self.ipython_app.read_only
158 158
159 159
160 160 #-----------------------------------------------------------------------------
161 161 # Aliases and Flags
162 162 #-----------------------------------------------------------------------------
163 163
164 164 flags = dict(ipkernel_flags)
165 165 flags['no-browser']=(
166 166 {'NotebookApp' : {'open_browser' : False}},
167 167 "Don't open the notebook in a browser after startup."
168 168 )
169 169 flags['no-mathjax']=(
170 170 {'NotebookApp' : {'enable_mathjax' : False}},
171 171 """Disable MathJax
172 172
173 173 MathJax is the javascript library IPython uses to render math/LaTeX. It is
174 174 very large, so you may want to disable it if you have a slow internet
175 175 connection, or for offline use of the notebook.
176 176
177 177 When disabled, equations etc. will appear as their untransformed TeX source.
178 178 """
179 179 )
180 180 flags['read-only'] = (
181 181 {'NotebookApp' : {'read_only' : True}},
182 182 """Allow read-only access to notebooks.
183 183
184 184 When using a password to protect the notebook server, this flag
185 185 allows unauthenticated clients to view the notebook list, and
186 186 individual notebooks, but not edit them, start kernels, or run
187 187 code.
188 188
189 189 If no password is set, the server will be entirely read-only.
190 190 """
191 191 )
192 192
193 193 # Add notebook manager flags
194 194 flags.update(boolean_flag('script', 'NotebookManager.save_script',
195 195 'Auto-save a .py script everytime the .ipynb notebook is saved',
196 196 'Do not auto-save .py scripts for every notebook'))
197 197
198 198 # the flags that are specific to the frontend
199 199 # these must be scrubbed before being passed to the kernel,
200 200 # or it will raise an error on unrecognized flags
201 201 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
202 202
203 203 aliases = dict(ipkernel_aliases)
204 204
205 205 aliases.update({
206 206 'ip': 'NotebookApp.ip',
207 207 'port': 'NotebookApp.port',
208 208 'keyfile': 'NotebookApp.keyfile',
209 209 'certfile': 'NotebookApp.certfile',
210 210 'notebook-dir': 'NotebookManager.notebook_dir',
211 211 'browser': 'NotebookApp.browser',
212 212 })
213 213
214 214 # remove ipkernel flags that are singletons, and don't make sense in
215 215 # multi-kernel evironment:
216 216 aliases.pop('f', None)
217 217
218 218 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
219 219 u'notebook-dir']
220 220
221 221 #-----------------------------------------------------------------------------
222 222 # NotebookApp
223 223 #-----------------------------------------------------------------------------
224 224
225 225 class NotebookApp(BaseIPythonApplication):
226 226
227 227 name = 'ipython-notebook'
228 228 default_config_file_name='ipython_notebook_config.py'
229 229
230 230 description = """
231 231 The IPython HTML Notebook.
232 232
233 233 This launches a Tornado based HTML Notebook Server that serves up an
234 234 HTML5/Javascript Notebook client.
235 235 """
236 236 examples = _examples
237 237
238 238 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
239 239 MappingKernelManager, NotebookManager]
240 240 flags = Dict(flags)
241 241 aliases = Dict(aliases)
242 242
243 243 kernel_argv = List(Unicode)
244 244
245 245 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
246 246 default_value=logging.INFO,
247 247 config=True,
248 248 help="Set the log level by value or name.")
249 249
250 250 # create requested profiles by default, if they don't exist:
251 251 auto_create = Bool(True)
252 252
253 253 # Network related information.
254 254
255 255 ip = Unicode(LOCALHOST, config=True,
256 256 help="The IP address the notebook server will listen on."
257 257 )
258 258
259 259 def _ip_changed(self, name, old, new):
260 260 if new == u'*': self.ip = u''
261 261
262 262 port = Integer(8888, config=True,
263 263 help="The port the notebook server will listen on."
264 264 )
265 265
266 266 certfile = Unicode(u'', config=True,
267 267 help="""The full path to an SSL/TLS certificate file."""
268 268 )
269 269
270 270 keyfile = Unicode(u'', config=True,
271 271 help="""The full path to a private key file for usage with SSL/TLS."""
272 272 )
273 273
274 274 password = Unicode(u'', config=True,
275 275 help="""Hashed password to use for web authentication.
276 276
277 277 To generate, type in a python/IPython shell:
278 278
279 279 from IPython.lib import passwd; passwd()
280 280
281 281 The string should be of the form type:salt:hashed-password.
282 282 """
283 283 )
284 284
285 285 open_browser = Bool(True, config=True,
286 286 help="""Whether to open in a browser after starting.
287 287 The specific browser used is platform dependent and
288 288 determined by the python standard library `webbrowser`
289 289 module, unless it is overridden using the --browser
290 290 (NotebookApp.browser) configuration option.
291 291 """)
292 292
293 293 browser = Unicode(u'', config=True,
294 294 help="""Specify what command to use to invoke a web
295 295 browser when opening the notebook. If not specified, the
296 296 default browser will be determined by the `webbrowser`
297 297 standard library module, which allows setting of the
298 298 BROWSER environment variable to override it.
299 299 """)
300 300
301 301 read_only = Bool(False, config=True,
302 302 help="Whether to prevent editing/execution of notebooks."
303 303 )
304 304
305 305 webapp_settings = Dict(config=True,
306 306 help="Supply overrides for the tornado.web.Application that the "
307 307 "IPython notebook uses.")
308 308
309 309 enable_mathjax = Bool(True, config=True,
310 310 help="""Whether to enable MathJax for typesetting math/TeX
311 311
312 312 MathJax is the javascript library IPython uses to render math/LaTeX. It is
313 313 very large, so you may want to disable it if you have a slow internet
314 314 connection, or for offline use of the notebook.
315 315
316 316 When disabled, equations etc. will appear as their untransformed TeX source.
317 317 """
318 318 )
319 319 def _enable_mathjax_changed(self, name, old, new):
320 320 """set mathjax url to empty if mathjax is disabled"""
321 321 if not new:
322 322 self.mathjax_url = u''
323 323
324 324 base_project_url = Unicode('/', config=True,
325 325 help='''The base URL for the notebook server''')
326 326 base_kernel_url = Unicode('/', config=True,
327 327 help='''The base URL for the kernel server''')
328 328 websocket_host = Unicode("", config=True,
329 329 help="""The hostname for the websocket server."""
330 330 )
331 331
332 332 mathjax_url = Unicode("", config=True,
333 333 help="""The url for MathJax.js."""
334 334 )
335 335 def _mathjax_url_default(self):
336 336 if not self.enable_mathjax:
337 337 return u''
338 338 static_path = self.webapp_settings.get("static_path", os.path.join(os.path.dirname(__file__), "static"))
339 339 static_url_prefix = self.webapp_settings.get("static_url_prefix",
340 340 "/static/")
341 341 if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")):
342 342 self.log.info("Using local MathJax")
343 343 return static_url_prefix+u"mathjax/MathJax.js"
344 344 else:
345 345 self.log.info("Using MathJax from CDN")
346 346 return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js"
347 347
348 348 def _mathjax_url_changed(self, name, old, new):
349 349 if new and not self.enable_mathjax:
350 350 # enable_mathjax=False overrides mathjax_url
351 351 self.mathjax_url = u''
352 352 else:
353 353 self.log.info("Using MathJax: %s", new)
354 354
355 355 def parse_command_line(self, argv=None):
356 356 super(NotebookApp, self).parse_command_line(argv)
357 357 if argv is None:
358 358 argv = sys.argv[1:]
359 359
360 360 # Scrub frontend-specific flags
361 361 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
362 362 # Kernel should inherit default config file from frontend
363 363 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
364 364
365 365 def init_configurables(self):
366 366 # force Session default to be secure
367 367 default_secure(self.config)
368 368 # Create a KernelManager and start a kernel.
369 369 self.kernel_manager = MappingKernelManager(
370 370 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
371 371 connection_dir = self.profile_dir.security_dir,
372 372 )
373 373 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
374 374 self.notebook_manager.list_notebooks()
375 375
376 376 def init_logging(self):
377 377 super(NotebookApp, self).init_logging()
378 378 # This prevents double log messages because tornado use a root logger that
379 379 # self.log is a child of. The logging module dipatches log messages to a log
380 380 # and all of its ancenstors until propagate is set to False.
381 381 self.log.propagate = False
382 382
383 383 def init_webapp(self):
384 384 """initialize tornado webapp and httpserver"""
385 385 self.web_app = NotebookWebApplication(
386 386 self, self.kernel_manager, self.notebook_manager, self.log,
387 387 self.base_project_url, self.webapp_settings
388 388 )
389 389 if self.certfile:
390 390 ssl_options = dict(certfile=self.certfile)
391 391 if self.keyfile:
392 392 ssl_options['keyfile'] = self.keyfile
393 393 else:
394 394 ssl_options = None
395 395 self.web_app.password = self.password
396 396 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
397 397 if ssl_options is None and not self.ip and not (self.read_only and not self.password):
398 398 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
399 399 'but not using any encryption or authentication. This is highly '
400 400 'insecure and not recommended.')
401 401
402 402 # Try random ports centered around the default.
403 403 from random import randint
404 404 n = 50 # Max number of attempts, keep reasonably large.
405 405 for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
406 406 try:
407 407 self.http_server.listen(port, self.ip)
408 408 except socket.error, e:
409 409 if e.errno != errno.EADDRINUSE:
410 410 raise
411 411 self.log.info('The port %i is already in use, trying another random port.' % port)
412 412 else:
413 413 self.port = port
414 414 break
415 415
416 416 @catch_config_error
417 417 def initialize(self, argv=None):
418 418 super(NotebookApp, self).initialize(argv)
419 419 self.init_configurables()
420 420 self.init_webapp()
421 421
422 422 def cleanup_kernels(self):
423 423 """shutdown all kernels
424 424
425 425 The kernels will shutdown themselves when this process no longer exists,
426 426 but explicit shutdown allows the KernelManagers to cleanup the connection files.
427 427 """
428 428 self.log.info('Shutting down kernels')
429 429 km = self.kernel_manager
430 430 # copy list, since kill_kernel deletes keys
431 431 for kid in list(km.kernel_ids):
432 432 km.kill_kernel(kid)
433 433
434 434 def start(self):
435 435 ip = self.ip if self.ip else '[all ip addresses on your system]'
436 436 proto = 'https' if self.certfile else 'http'
437 437 info = self.log.info
438 438 info("The IPython Notebook is running at: %s://%s:%i%s" %
439 439 (proto, ip, self.port,self.base_project_url) )
440 440 info("Use Control-C to stop this server and shut down all kernels.")
441 441
442 442 if self.open_browser:
443 443 ip = self.ip or '127.0.0.1'
444 444 if self.browser:
445 browser = webbrowser.get()
446 else:
447 445 browser = webbrowser.get(self.browser)
446 else:
447 browser = webbrowser.get()
448 448 b = lambda : browser.open("%s://%s:%i%s" % (proto, ip, self.port,
449 449 self.base_project_url),
450 450 new=2)
451 451 threading.Thread(target=b).start()
452 452 try:
453 453 ioloop.IOLoop.instance().start()
454 454 except KeyboardInterrupt:
455 455 info("Interrupted...")
456 456 finally:
457 457 self.cleanup_kernels()
458 458
459 459
460 460 #-----------------------------------------------------------------------------
461 461 # Main entry point
462 462 #-----------------------------------------------------------------------------
463 463
464 464 def launch_new_instance():
465 465 app = NotebookApp.instance()
466 466 app.initialize()
467 467 app.start()
468 468
General Comments 0
You need to be logged in to leave comments. Login now