##// END OF EJS Templates
Add ability to open the notebook in a browser when it starts.
Thomas Kluyver -
Show More
@@ -1,304 +1,315
1 1 """A tornado based IPython notebook server.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2008-2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 19 import errno
20 20 import logging
21 21 import os
22 22 import signal
23 23 import socket
24 24 import sys
25 import webbrowser
25 26
26 27 import zmq
27 28
28 29 # Install the pyzmq ioloop. This has to be done before anything else from
29 30 # tornado is imported.
30 31 from zmq.eventloop import ioloop
31 32 import tornado.ioloop
32 33 tornado.ioloop.IOLoop = ioloop.IOLoop
33 34
34 35 from tornado import httpserver
35 36 from tornado import web
36 37
37 38 from .kernelmanager import MappingKernelManager
38 39 from .handlers import (LoginHandler,
39 40 NBBrowserHandler, NewHandler, NamedNotebookHandler,
40 41 MainKernelHandler, KernelHandler, KernelActionHandler, IOPubHandler,
41 42 ShellHandler, NotebookRootHandler, NotebookHandler, RSTHandler
42 43 )
43 44 from .notebookmanager import NotebookManager
44 45
45 46 from IPython.core.application import BaseIPythonApplication
46 47 from IPython.core.profiledir import ProfileDir
47 48 from IPython.zmq.session import Session, default_secure
48 49 from IPython.zmq.zmqshell import ZMQInteractiveShell
49 50 from IPython.zmq.ipkernel import (
50 51 flags as ipkernel_flags,
51 52 aliases as ipkernel_aliases,
52 53 IPKernelApp
53 54 )
54 from IPython.utils.traitlets import Dict, Unicode, Int, List, Enum
55 from IPython.utils.traitlets import Dict, Unicode, Int, List, Enum, Bool
55 56
56 57 #-----------------------------------------------------------------------------
57 58 # Module globals
58 59 #-----------------------------------------------------------------------------
59 60
60 61 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
61 62 _kernel_action_regex = r"(?P<action>restart|interrupt)"
62 63 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
63 64
64 65 LOCALHOST = '127.0.0.1'
65 66
66 67 _examples = """
67 68 ipython notebook # start the notebook
68 69 ipython notebook --profile=sympy # use the sympy profile
69 70 ipython notebook --pylab=inline # pylab in inline plotting mode
70 71 ipython notebook --certfile=mycert.pem # use SSL/TLS certificate
71 72 ipython notebook --port=5555 --ip=* # Listen on port 5555, all interfaces
72 73 """
73 74
74 75 #-----------------------------------------------------------------------------
75 76 # The Tornado web application
76 77 #-----------------------------------------------------------------------------
77 78
78 79 class NotebookWebApplication(web.Application):
79 80
80 81 def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
81 82 handlers = [
82 83 (r"/", NBBrowserHandler),
83 84 (r"/login", LoginHandler),
84 85 (r"/new", NewHandler),
85 86 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
86 87 (r"/kernels", MainKernelHandler),
87 88 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
88 89 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
89 90 (r"/kernels/%s/iopub" % _kernel_id_regex, IOPubHandler),
90 91 (r"/kernels/%s/shell" % _kernel_id_regex, ShellHandler),
91 92 (r"/notebooks", NotebookRootHandler),
92 93 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
93 94 (r"/rstservice/render", RSTHandler)
94 95 ]
95 96 settings = dict(
96 97 template_path=os.path.join(os.path.dirname(__file__), "templates"),
97 98 static_path=os.path.join(os.path.dirname(__file__), "static"),
98 99 cookie_secret=os.urandom(1024),
99 100 login_url="/login",
100 101 )
101 102 web.Application.__init__(self, handlers, **settings)
102 103
103 104 self.kernel_manager = kernel_manager
104 105 self.log = log
105 106 self.notebook_manager = notebook_manager
106 107 self.ipython_app = ipython_app
107 108
108 109
109 110 #-----------------------------------------------------------------------------
110 111 # Aliases and Flags
111 112 #-----------------------------------------------------------------------------
112 113
113 114 flags = dict(ipkernel_flags)
115 flags['no-browser']=(
116 {'IPythonNotebookApp' : {'open_browser' : False}},
117 "Don't open the notebook in a browser after startup."
118 )
114 119
115 120 # the flags that are specific to the frontend
116 121 # these must be scrubbed before being passed to the kernel,
117 122 # or it will raise an error on unrecognized flags
118 notebook_flags = []
123 notebook_flags = ['no-browser']
119 124
120 125 aliases = dict(ipkernel_aliases)
121 126
122 127 aliases.update({
123 128 'ip': 'IPythonNotebookApp.ip',
124 129 'port': 'IPythonNotebookApp.port',
125 130 'keyfile': 'IPythonNotebookApp.keyfile',
126 131 'certfile': 'IPythonNotebookApp.certfile',
127 132 'ws-hostname': 'IPythonNotebookApp.ws_hostname',
128 133 'notebook-dir': 'NotebookManager.notebook_dir',
129 134 })
130 135
131 136 # remove ipkernel flags that are singletons, and don't make sense in
132 137 # multi-kernel evironment:
133 138 aliases.pop('f', None)
134 139
135 140 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile', u'ws-hostname',
136 141 u'notebook-dir']
137 142
138 143 #-----------------------------------------------------------------------------
139 144 # IPythonNotebookApp
140 145 #-----------------------------------------------------------------------------
141 146
142 147 class IPythonNotebookApp(BaseIPythonApplication):
143 148
144 149 name = 'ipython-notebook'
145 150 default_config_file_name='ipython_notebook_config.py'
146 151
147 152 description = """
148 153 The IPython HTML Notebook.
149 154
150 155 This launches a Tornado based HTML Notebook Server that serves up an
151 156 HTML5/Javascript Notebook client.
152 157 """
153 158 examples = _examples
154 159
155 160 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
156 161 MappingKernelManager, NotebookManager]
157 162 flags = Dict(flags)
158 163 aliases = Dict(aliases)
159 164
160 165 kernel_argv = List(Unicode)
161 166
162 167 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
163 168 default_value=logging.INFO,
164 169 config=True,
165 170 help="Set the log level by value or name.")
166 171
167 172 # Network related information.
168 173
169 174 ip = Unicode(LOCALHOST, config=True,
170 175 help="The IP address the notebook server will listen on."
171 176 )
172 177
173 178 def _ip_changed(self, name, old, new):
174 179 if new == u'*': self.ip = u''
175 180
176 181 port = Int(8888, config=True,
177 182 help="The port the notebook server will listen on."
178 183 )
179 184
180 185 ws_hostname = Unicode(LOCALHOST, config=True,
181 186 help="""The FQDN or IP for WebSocket connections. The default will work
182 187 fine when the server is listening on localhost, but this needs to
183 188 be set if the ip option is used. It will be used as the hostname part
184 189 of the WebSocket url: ws://hostname/path."""
185 190 )
186 191
187 192 certfile = Unicode(u'', config=True,
188 193 help="""The full path to an SSL/TLS certificate file."""
189 194 )
190 195
191 196 keyfile = Unicode(u'', config=True,
192 197 help="""The full path to a private key file for usage with SSL/TLS."""
193 198 )
194 199
195 200 password = Unicode(u'', config=True,
196 201 help="""Password to use for web authentication"""
197 202 )
203
204 open_browser = Bool(True, config=True,
205 help="Whether to open in a browser after starting.")
198 206
199 207 def get_ws_url(self):
200 208 """Return the WebSocket URL for this server."""
201 209 if self.certfile:
202 210 prefix = u'wss://'
203 211 else:
204 212 prefix = u'ws://'
205 213 return prefix + self.ws_hostname + u':' + unicode(self.port)
206 214
207 215 def parse_command_line(self, argv=None):
208 216 super(IPythonNotebookApp, self).parse_command_line(argv)
209 217 if argv is None:
210 218 argv = sys.argv[1:]
211 219
212 220 self.kernel_argv = list(argv) # copy
213 221 # Kernel should inherit default config file from frontend
214 222 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
215 223 # Scrub frontend-specific flags
216 224 for a in argv:
217 225 if a.startswith('-') and a.lstrip('-') in notebook_flags:
218 226 self.kernel_argv.remove(a)
219 227 swallow_next = False
220 228 for a in argv:
221 229 if swallow_next:
222 230 self.kernel_argv.remove(a)
223 231 swallow_next = False
224 232 continue
225 233 if a.startswith('-'):
226 234 split = a.lstrip('-').split('=')
227 235 alias = split[0]
228 236 if alias in notebook_aliases:
229 237 self.kernel_argv.remove(a)
230 238 if len(split) == 1:
231 239 # alias passed with arg via space
232 240 swallow_next = True
233 241
234 242 def init_configurables(self):
235 243 # Don't let Qt or ZMQ swallow KeyboardInterupts.
236 244 signal.signal(signal.SIGINT, signal.SIG_DFL)
237 245
238 246 # force Session default to be secure
239 247 default_secure(self.config)
240 248 # Create a KernelManager and start a kernel.
241 249 self.kernel_manager = MappingKernelManager(
242 250 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
243 251 connection_dir = self.profile_dir.security_dir,
244 252 )
245 253 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
246 254 self.notebook_manager.list_notebooks()
247 255
248 256 def init_logging(self):
249 257 super(IPythonNotebookApp, self).init_logging()
250 258 # This prevents double log messages because tornado use a root logger that
251 259 # self.log is a child of. The logging module dipatches log messages to a log
252 260 # and all of its ancenstors until propagate is set to False.
253 261 self.log.propagate = False
254 262
255 263 def initialize(self, argv=None):
256 264 super(IPythonNotebookApp, self).initialize(argv)
257 265 self.init_configurables()
258 266 self.web_app = NotebookWebApplication(
259 267 self, self.kernel_manager, self.notebook_manager, self.log
260 268 )
261 269 if self.certfile:
262 270 ssl_options = dict(certfile=self.certfile)
263 271 if self.keyfile:
264 272 ssl_options['keyfile'] = self.keyfile
265 273 else:
266 274 ssl_options = None
267 275 self.web_app.password = self.password
268 276 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
269 277 if ssl_options is None and not self.ip:
270 278 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
271 279 'but not using any encryption or authentication. This is highly '
272 280 'insecure and not recommended.')
273 281
274 282 # Try random ports centered around the default.
275 283 from random import randint
276 284 n = 50 # Max number of attempts, keep reasonably large.
277 285 for port in [self.port] + [self.port + randint(-2*n, 2*n) for i in range(n)]:
278 286 try:
279 287 self.http_server.listen(port, self.ip)
280 288 except socket.error, e:
281 289 if e.errno != errno.EADDRINUSE:
282 290 raise
283 291 self.log.info('The port %i is already in use, trying another random port.' % port)
284 292 else:
285 293 self.port = port
286 294 break
287 295
288 296 def start(self):
289 297 ip = self.ip if self.ip else '[all ip addresses on your system]'
290 298 proto = 'https' if self.certfile else 'http'
291 299 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
292 300 ip,
293 301 self.port))
302 if self.open_browser:
303 ip = self.ip or '127.0.0.1'
304 webbrowser.open("%s://%s:%i" % (proto, ip, self.port), new=2)
294 305 ioloop.IOLoop.instance().start()
295 306
296 307 #-----------------------------------------------------------------------------
297 308 # Main entry point
298 309 #-----------------------------------------------------------------------------
299 310
300 311 def launch_new_instance():
301 312 app = IPythonNotebookApp()
302 313 app.initialize()
303 314 app.start()
304 315
General Comments 0
You need to be logged in to leave comments. Login now