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