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