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