##// END OF EJS Templates
Merge PR 1135...
MinRK -
r5701:f1fd9c88 merge
parent child Browse files
Show More
@@ -1,369 +1,378 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 def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
92 def __init__(self, ipython_app, kernel_manager, notebook_manager, log, settings_overrides):
93 handlers = [
93 handlers = [
94 (r"/", ProjectDashboardHandler),
94 (r"/", ProjectDashboardHandler),
95 (r"/login", LoginHandler),
95 (r"/login", LoginHandler),
96 (r"/logout", LogoutHandler),
96 (r"/logout", LogoutHandler),
97 (r"/new", NewHandler),
97 (r"/new", NewHandler),
98 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
98 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
99 (r"/kernels", MainKernelHandler),
99 (r"/kernels", MainKernelHandler),
100 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
100 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
101 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
101 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
102 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
102 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
103 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
103 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
104 (r"/notebooks", NotebookRootHandler),
104 (r"/notebooks", NotebookRootHandler),
105 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
105 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
106 (r"/rstservice/render", RSTHandler)
106 (r"/rstservice/render", RSTHandler)
107 ]
107 ]
108 settings = dict(
108 settings = dict(
109 template_path=os.path.join(os.path.dirname(__file__), "templates"),
109 template_path=os.path.join(os.path.dirname(__file__), "templates"),
110 static_path=os.path.join(os.path.dirname(__file__), "static"),
110 static_path=os.path.join(os.path.dirname(__file__), "static"),
111 cookie_secret=os.urandom(1024),
111 cookie_secret=os.urandom(1024),
112 login_url="/login",
112 login_url="/login",
113 )
113 )
114 web.Application.__init__(self, handlers, **settings)
114
115 # allow custom overrides for the tornado web app.
116 settings.update(settings_overrides)
117
118 super(NotebookWebApplication, self).__init__(handlers, **settings)
115
119
116 self.kernel_manager = kernel_manager
120 self.kernel_manager = kernel_manager
117 self.log = log
121 self.log = log
118 self.notebook_manager = notebook_manager
122 self.notebook_manager = notebook_manager
119 self.ipython_app = ipython_app
123 self.ipython_app = ipython_app
120 self.read_only = self.ipython_app.read_only
124 self.read_only = self.ipython_app.read_only
121
125
122
126
123 #-----------------------------------------------------------------------------
127 #-----------------------------------------------------------------------------
124 # Aliases and Flags
128 # Aliases and Flags
125 #-----------------------------------------------------------------------------
129 #-----------------------------------------------------------------------------
126
130
127 flags = dict(ipkernel_flags)
131 flags = dict(ipkernel_flags)
128 flags['no-browser']=(
132 flags['no-browser']=(
129 {'NotebookApp' : {'open_browser' : False}},
133 {'NotebookApp' : {'open_browser' : False}},
130 "Don't open the notebook in a browser after startup."
134 "Don't open the notebook in a browser after startup."
131 )
135 )
132 flags['no-mathjax']=(
136 flags['no-mathjax']=(
133 {'NotebookApp' : {'enable_mathjax' : False}},
137 {'NotebookApp' : {'enable_mathjax' : False}},
134 """Disable MathJax
138 """Disable MathJax
135
139
136 MathJax is the javascript library IPython uses to render math/LaTeX. It is
140 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
141 very large, so you may want to disable it if you have a slow internet
138 connection, or for offline use of the notebook.
142 connection, or for offline use of the notebook.
139
143
140 When disabled, equations etc. will appear as their untransformed TeX source.
144 When disabled, equations etc. will appear as their untransformed TeX source.
141 """
145 """
142 )
146 )
143 flags['read-only'] = (
147 flags['read-only'] = (
144 {'NotebookApp' : {'read_only' : True}},
148 {'NotebookApp' : {'read_only' : True}},
145 """Allow read-only access to notebooks.
149 """Allow read-only access to notebooks.
146
150
147 When using a password to protect the notebook server, this flag
151 When using a password to protect the notebook server, this flag
148 allows unauthenticated clients to view the notebook list, and
152 allows unauthenticated clients to view the notebook list, and
149 individual notebooks, but not edit them, start kernels, or run
153 individual notebooks, but not edit them, start kernels, or run
150 code.
154 code.
151
155
152 If no password is set, the server will be entirely read-only.
156 If no password is set, the server will be entirely read-only.
153 """
157 """
154 )
158 )
155
159
156 # the flags that are specific to the frontend
160 # the flags that are specific to the frontend
157 # these must be scrubbed before being passed to the kernel,
161 # these must be scrubbed before being passed to the kernel,
158 # or it will raise an error on unrecognized flags
162 # or it will raise an error on unrecognized flags
159 notebook_flags = ['no-browser', 'no-mathjax', 'read-only']
163 notebook_flags = ['no-browser', 'no-mathjax', 'read-only']
160
164
161 aliases = dict(ipkernel_aliases)
165 aliases = dict(ipkernel_aliases)
162
166
163 aliases.update({
167 aliases.update({
164 'ip': 'NotebookApp.ip',
168 'ip': 'NotebookApp.ip',
165 'port': 'NotebookApp.port',
169 'port': 'NotebookApp.port',
166 'keyfile': 'NotebookApp.keyfile',
170 'keyfile': 'NotebookApp.keyfile',
167 'certfile': 'NotebookApp.certfile',
171 'certfile': 'NotebookApp.certfile',
168 'notebook-dir': 'NotebookManager.notebook_dir',
172 'notebook-dir': 'NotebookManager.notebook_dir',
169 })
173 })
170
174
171 # remove ipkernel flags that are singletons, and don't make sense in
175 # remove ipkernel flags that are singletons, and don't make sense in
172 # multi-kernel evironment:
176 # multi-kernel evironment:
173 aliases.pop('f', None)
177 aliases.pop('f', None)
174
178
175 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
179 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
176 u'notebook-dir']
180 u'notebook-dir']
177
181
178 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
179 # NotebookApp
183 # NotebookApp
180 #-----------------------------------------------------------------------------
184 #-----------------------------------------------------------------------------
181
185
182 class NotebookApp(BaseIPythonApplication):
186 class NotebookApp(BaseIPythonApplication):
183
187
184 name = 'ipython-notebook'
188 name = 'ipython-notebook'
185 default_config_file_name='ipython_notebook_config.py'
189 default_config_file_name='ipython_notebook_config.py'
186
190
187 description = """
191 description = """
188 The IPython HTML Notebook.
192 The IPython HTML Notebook.
189
193
190 This launches a Tornado based HTML Notebook Server that serves up an
194 This launches a Tornado based HTML Notebook Server that serves up an
191 HTML5/Javascript Notebook client.
195 HTML5/Javascript Notebook client.
192 """
196 """
193 examples = _examples
197 examples = _examples
194
198
195 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
199 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
196 MappingKernelManager, NotebookManager]
200 MappingKernelManager, NotebookManager]
197 flags = Dict(flags)
201 flags = Dict(flags)
198 aliases = Dict(aliases)
202 aliases = Dict(aliases)
199
203
200 kernel_argv = List(Unicode)
204 kernel_argv = List(Unicode)
201
205
202 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
206 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
203 default_value=logging.INFO,
207 default_value=logging.INFO,
204 config=True,
208 config=True,
205 help="Set the log level by value or name.")
209 help="Set the log level by value or name.")
206
210
207 # Network related information.
211 # Network related information.
208
212
209 ip = Unicode(LOCALHOST, config=True,
213 ip = Unicode(LOCALHOST, config=True,
210 help="The IP address the notebook server will listen on."
214 help="The IP address the notebook server will listen on."
211 )
215 )
212
216
213 def _ip_changed(self, name, old, new):
217 def _ip_changed(self, name, old, new):
214 if new == u'*': self.ip = u''
218 if new == u'*': self.ip = u''
215
219
216 port = Integer(8888, config=True,
220 port = Integer(8888, config=True,
217 help="The port the notebook server will listen on."
221 help="The port the notebook server will listen on."
218 )
222 )
219
223
220 certfile = Unicode(u'', config=True,
224 certfile = Unicode(u'', config=True,
221 help="""The full path to an SSL/TLS certificate file."""
225 help="""The full path to an SSL/TLS certificate file."""
222 )
226 )
223
227
224 keyfile = Unicode(u'', config=True,
228 keyfile = Unicode(u'', config=True,
225 help="""The full path to a private key file for usage with SSL/TLS."""
229 help="""The full path to a private key file for usage with SSL/TLS."""
226 )
230 )
227
231
228 password = Unicode(u'', config=True,
232 password = Unicode(u'', config=True,
229 help="""Hashed password to use for web authentication.
233 help="""Hashed password to use for web authentication.
230
234
231 To generate, type in a python/IPython shell:
235 To generate, type in a python/IPython shell:
232
236
233 from IPython.lib import passwd; passwd()
237 from IPython.lib import passwd; passwd()
234
238
235 The string should be of the form type:salt:hashed-password.
239 The string should be of the form type:salt:hashed-password.
236 """
240 """
237 )
241 )
238
242
239 open_browser = Bool(True, config=True,
243 open_browser = Bool(True, config=True,
240 help="Whether to open in a browser after starting.")
244 help="Whether to open in a browser after starting.")
241
245
242 read_only = Bool(False, config=True,
246 read_only = Bool(False, config=True,
243 help="Whether to prevent editing/execution of notebooks."
247 help="Whether to prevent editing/execution of notebooks."
244 )
248 )
245
249
250 webapp_settings = Dict(config=True,
251 help="Supply overrides for the tornado.web.Application that the "
252 "IPython notebook uses.")
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 = self.webapp_settings.get("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,
327 self.webapp_settings
319 )
328 )
320 if self.certfile:
329 if self.certfile:
321 ssl_options = dict(certfile=self.certfile)
330 ssl_options = dict(certfile=self.certfile)
322 if self.keyfile:
331 if self.keyfile:
323 ssl_options['keyfile'] = self.keyfile
332 ssl_options['keyfile'] = self.keyfile
324 else:
333 else:
325 ssl_options = None
334 ssl_options = None
326 self.web_app.password = self.password
335 self.web_app.password = self.password
327 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
336 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
328 if ssl_options is None and not self.ip:
337 if ssl_options is None and not self.ip:
329 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
338 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
330 'but not using any encryption or authentication. This is highly '
339 'but not using any encryption or authentication. This is highly '
331 'insecure and not recommended.')
340 'insecure and not recommended.')
332
341
333 # Try random ports centered around the default.
342 # Try random ports centered around the default.
334 from random import randint
343 from random import randint
335 n = 50 # Max number of attempts, keep reasonably large.
344 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)]:
345 for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
337 try:
346 try:
338 self.http_server.listen(port, self.ip)
347 self.http_server.listen(port, self.ip)
339 except socket.error, e:
348 except socket.error, e:
340 if e.errno != errno.EADDRINUSE:
349 if e.errno != errno.EADDRINUSE:
341 raise
350 raise
342 self.log.info('The port %i is already in use, trying another random port.' % port)
351 self.log.info('The port %i is already in use, trying another random port.' % port)
343 else:
352 else:
344 self.port = port
353 self.port = port
345 break
354 break
346
355
347 def start(self):
356 def start(self):
348 ip = self.ip if self.ip else '[all ip addresses on your system]'
357 ip = self.ip if self.ip else '[all ip addresses on your system]'
349 proto = 'https' if self.certfile else 'http'
358 proto = 'https' if self.certfile else 'http'
350 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
359 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
351 ip,
360 ip,
352 self.port))
361 self.port))
353 if self.open_browser:
362 if self.open_browser:
354 ip = self.ip or '127.0.0.1'
363 ip = self.ip or '127.0.0.1'
355 b = lambda : webbrowser.open("%s://%s:%i" % (proto, ip, self.port),
364 b = lambda : webbrowser.open("%s://%s:%i" % (proto, ip, self.port),
356 new=2)
365 new=2)
357 threading.Thread(target=b).start()
366 threading.Thread(target=b).start()
358
367
359 ioloop.IOLoop.instance().start()
368 ioloop.IOLoop.instance().start()
360
369
361 #-----------------------------------------------------------------------------
370 #-----------------------------------------------------------------------------
362 # Main entry point
371 # Main entry point
363 #-----------------------------------------------------------------------------
372 #-----------------------------------------------------------------------------
364
373
365 def launch_new_instance():
374 def launch_new_instance():
366 app = NotebookApp()
375 app = NotebookApp()
367 app.initialize()
376 app.initialize()
368 app.start()
377 app.start()
369
378
General Comments 0
You need to be logged in to leave comments. Login now