##// END OF EJS Templates
Merge pull request #1168 from fperez/nbscript...
Fernando Perez -
r5763:e953107c merge
parent child Browse files
Show More
@@ -1,378 +1,383 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, boolean_flag
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, settings_overrides):
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
114
115 # allow custom overrides for the tornado web app.
115 # allow custom overrides for the tornado web app.
116 settings.update(settings_overrides)
116 settings.update(settings_overrides)
117
117
118 super(NotebookWebApplication, self).__init__(handlers, **settings)
118 super(NotebookWebApplication, self).__init__(handlers, **settings)
119
119
120 self.kernel_manager = kernel_manager
120 self.kernel_manager = kernel_manager
121 self.log = log
121 self.log = log
122 self.notebook_manager = notebook_manager
122 self.notebook_manager = notebook_manager
123 self.ipython_app = ipython_app
123 self.ipython_app = ipython_app
124 self.read_only = self.ipython_app.read_only
124 self.read_only = self.ipython_app.read_only
125
125
126
126
127 #-----------------------------------------------------------------------------
127 #-----------------------------------------------------------------------------
128 # Aliases and Flags
128 # Aliases and Flags
129 #-----------------------------------------------------------------------------
129 #-----------------------------------------------------------------------------
130
130
131 flags = dict(ipkernel_flags)
131 flags = dict(ipkernel_flags)
132 flags['no-browser']=(
132 flags['no-browser']=(
133 {'NotebookApp' : {'open_browser' : False}},
133 {'NotebookApp' : {'open_browser' : False}},
134 "Don't open the notebook in a browser after startup."
134 "Don't open the notebook in a browser after startup."
135 )
135 )
136 flags['no-mathjax']=(
136 flags['no-mathjax']=(
137 {'NotebookApp' : {'enable_mathjax' : False}},
137 {'NotebookApp' : {'enable_mathjax' : False}},
138 """Disable MathJax
138 """Disable MathJax
139
139
140 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
141 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
142 connection, or for offline use of the notebook.
142 connection, or for offline use of the notebook.
143
143
144 When disabled, equations etc. will appear as their untransformed TeX source.
144 When disabled, equations etc. will appear as their untransformed TeX source.
145 """
145 """
146 )
146 )
147 flags['read-only'] = (
147 flags['read-only'] = (
148 {'NotebookApp' : {'read_only' : True}},
148 {'NotebookApp' : {'read_only' : True}},
149 """Allow read-only access to notebooks.
149 """Allow read-only access to notebooks.
150
150
151 When using a password to protect the notebook server, this flag
151 When using a password to protect the notebook server, this flag
152 allows unauthenticated clients to view the notebook list, and
152 allows unauthenticated clients to view the notebook list, and
153 individual notebooks, but not edit them, start kernels, or run
153 individual notebooks, but not edit them, start kernels, or run
154 code.
154 code.
155
155
156 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.
157 """
157 """
158 )
158 )
159
159
160 # Add notebook manager flags
161 flags.update(boolean_flag('script', 'NotebookManager.save_script',
162 'Auto-save a .py script everytime the .ipynb notebook is saved',
163 'Do not auto-save .py scripts for every notebook'))
164
160 # the flags that are specific to the frontend
165 # the flags that are specific to the frontend
161 # these must be scrubbed before being passed to the kernel,
166 # these must be scrubbed before being passed to the kernel,
162 # or it will raise an error on unrecognized flags
167 # or it will raise an error on unrecognized flags
163 notebook_flags = ['no-browser', 'no-mathjax', 'read-only']
168 notebook_flags = ['no-browser', 'no-mathjax', 'read-only', 'script', 'no-script']
164
169
165 aliases = dict(ipkernel_aliases)
170 aliases = dict(ipkernel_aliases)
166
171
167 aliases.update({
172 aliases.update({
168 'ip': 'NotebookApp.ip',
173 'ip': 'NotebookApp.ip',
169 'port': 'NotebookApp.port',
174 'port': 'NotebookApp.port',
170 'keyfile': 'NotebookApp.keyfile',
175 'keyfile': 'NotebookApp.keyfile',
171 'certfile': 'NotebookApp.certfile',
176 'certfile': 'NotebookApp.certfile',
172 'notebook-dir': 'NotebookManager.notebook_dir',
177 'notebook-dir': 'NotebookManager.notebook_dir',
173 })
178 })
174
179
175 # remove ipkernel flags that are singletons, and don't make sense in
180 # remove ipkernel flags that are singletons, and don't make sense in
176 # multi-kernel evironment:
181 # multi-kernel evironment:
177 aliases.pop('f', None)
182 aliases.pop('f', None)
178
183
179 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
184 notebook_aliases = [u'port', u'ip', u'keyfile', u'certfile',
180 u'notebook-dir']
185 u'notebook-dir']
181
186
182 #-----------------------------------------------------------------------------
187 #-----------------------------------------------------------------------------
183 # NotebookApp
188 # NotebookApp
184 #-----------------------------------------------------------------------------
189 #-----------------------------------------------------------------------------
185
190
186 class NotebookApp(BaseIPythonApplication):
191 class NotebookApp(BaseIPythonApplication):
187
192
188 name = 'ipython-notebook'
193 name = 'ipython-notebook'
189 default_config_file_name='ipython_notebook_config.py'
194 default_config_file_name='ipython_notebook_config.py'
190
195
191 description = """
196 description = """
192 The IPython HTML Notebook.
197 The IPython HTML Notebook.
193
198
194 This launches a Tornado based HTML Notebook Server that serves up an
199 This launches a Tornado based HTML Notebook Server that serves up an
195 HTML5/Javascript Notebook client.
200 HTML5/Javascript Notebook client.
196 """
201 """
197 examples = _examples
202 examples = _examples
198
203
199 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
204 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
200 MappingKernelManager, NotebookManager]
205 MappingKernelManager, NotebookManager]
201 flags = Dict(flags)
206 flags = Dict(flags)
202 aliases = Dict(aliases)
207 aliases = Dict(aliases)
203
208
204 kernel_argv = List(Unicode)
209 kernel_argv = List(Unicode)
205
210
206 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
211 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
207 default_value=logging.INFO,
212 default_value=logging.INFO,
208 config=True,
213 config=True,
209 help="Set the log level by value or name.")
214 help="Set the log level by value or name.")
210
215
211 # Network related information.
216 # Network related information.
212
217
213 ip = Unicode(LOCALHOST, config=True,
218 ip = Unicode(LOCALHOST, config=True,
214 help="The IP address the notebook server will listen on."
219 help="The IP address the notebook server will listen on."
215 )
220 )
216
221
217 def _ip_changed(self, name, old, new):
222 def _ip_changed(self, name, old, new):
218 if new == u'*': self.ip = u''
223 if new == u'*': self.ip = u''
219
224
220 port = Integer(8888, config=True,
225 port = Integer(8888, config=True,
221 help="The port the notebook server will listen on."
226 help="The port the notebook server will listen on."
222 )
227 )
223
228
224 certfile = Unicode(u'', config=True,
229 certfile = Unicode(u'', config=True,
225 help="""The full path to an SSL/TLS certificate file."""
230 help="""The full path to an SSL/TLS certificate file."""
226 )
231 )
227
232
228 keyfile = Unicode(u'', config=True,
233 keyfile = Unicode(u'', config=True,
229 help="""The full path to a private key file for usage with SSL/TLS."""
234 help="""The full path to a private key file for usage with SSL/TLS."""
230 )
235 )
231
236
232 password = Unicode(u'', config=True,
237 password = Unicode(u'', config=True,
233 help="""Hashed password to use for web authentication.
238 help="""Hashed password to use for web authentication.
234
239
235 To generate, type in a python/IPython shell:
240 To generate, type in a python/IPython shell:
236
241
237 from IPython.lib import passwd; passwd()
242 from IPython.lib import passwd; passwd()
238
243
239 The string should be of the form type:salt:hashed-password.
244 The string should be of the form type:salt:hashed-password.
240 """
245 """
241 )
246 )
242
247
243 open_browser = Bool(True, config=True,
248 open_browser = Bool(True, config=True,
244 help="Whether to open in a browser after starting.")
249 help="Whether to open in a browser after starting.")
245
250
246 read_only = Bool(False, config=True,
251 read_only = Bool(False, config=True,
247 help="Whether to prevent editing/execution of notebooks."
252 help="Whether to prevent editing/execution of notebooks."
248 )
253 )
249
254
250 webapp_settings = Dict(config=True,
255 webapp_settings = Dict(config=True,
251 help="Supply overrides for the tornado.web.Application that the "
256 help="Supply overrides for the tornado.web.Application that the "
252 "IPython notebook uses.")
257 "IPython notebook uses.")
253
258
254 enable_mathjax = Bool(True, config=True,
259 enable_mathjax = Bool(True, config=True,
255 help="""Whether to enable MathJax for typesetting math/TeX
260 help="""Whether to enable MathJax for typesetting math/TeX
256
261
257 MathJax is the javascript library IPython uses to render math/LaTeX. It is
262 MathJax is the javascript library IPython uses to render math/LaTeX. It is
258 very large, so you may want to disable it if you have a slow internet
263 very large, so you may want to disable it if you have a slow internet
259 connection, or for offline use of the notebook.
264 connection, or for offline use of the notebook.
260
265
261 When disabled, equations etc. will appear as their untransformed TeX source.
266 When disabled, equations etc. will appear as their untransformed TeX source.
262 """
267 """
263 )
268 )
264 def _enable_mathjax_changed(self, name, old, new):
269 def _enable_mathjax_changed(self, name, old, new):
265 """set mathjax url to empty if mathjax is disabled"""
270 """set mathjax url to empty if mathjax is disabled"""
266 if not new:
271 if not new:
267 self.mathjax_url = u''
272 self.mathjax_url = u''
268
273
269 mathjax_url = Unicode("", config=True,
274 mathjax_url = Unicode("", config=True,
270 help="""The url for MathJax.js."""
275 help="""The url for MathJax.js."""
271 )
276 )
272 def _mathjax_url_default(self):
277 def _mathjax_url_default(self):
273 if not self.enable_mathjax:
278 if not self.enable_mathjax:
274 return u''
279 return u''
275 static_path = self.webapp_settings.get("static_path", os.path.join(os.path.dirname(__file__), "static"))
280 static_path = self.webapp_settings.get("static_path", os.path.join(os.path.dirname(__file__), "static"))
276 if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")):
281 if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")):
277 self.log.info("Using local MathJax")
282 self.log.info("Using local MathJax")
278 return u"static/mathjax/MathJax.js"
283 return u"static/mathjax/MathJax.js"
279 else:
284 else:
280 self.log.info("Using MathJax from CDN")
285 self.log.info("Using MathJax from CDN")
281 return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js"
286 return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js"
282
287
283 def _mathjax_url_changed(self, name, old, new):
288 def _mathjax_url_changed(self, name, old, new):
284 if new and not self.enable_mathjax:
289 if new and not self.enable_mathjax:
285 # enable_mathjax=False overrides mathjax_url
290 # enable_mathjax=False overrides mathjax_url
286 self.mathjax_url = u''
291 self.mathjax_url = u''
287 else:
292 else:
288 self.log.info("Using MathJax: %s", new)
293 self.log.info("Using MathJax: %s", new)
289
294
290 def parse_command_line(self, argv=None):
295 def parse_command_line(self, argv=None):
291 super(NotebookApp, self).parse_command_line(argv)
296 super(NotebookApp, self).parse_command_line(argv)
292 if argv is None:
297 if argv is None:
293 argv = sys.argv[1:]
298 argv = sys.argv[1:]
294
299
295 # Scrub frontend-specific flags
300 # Scrub frontend-specific flags
296 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
301 self.kernel_argv = swallow_argv(argv, notebook_aliases, notebook_flags)
297 # Kernel should inherit default config file from frontend
302 # Kernel should inherit default config file from frontend
298 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
303 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
299
304
300 def init_configurables(self):
305 def init_configurables(self):
301 # Don't let Qt or ZMQ swallow KeyboardInterupts.
306 # Don't let Qt or ZMQ swallow KeyboardInterupts.
302 signal.signal(signal.SIGINT, signal.SIG_DFL)
307 signal.signal(signal.SIGINT, signal.SIG_DFL)
303
308
304 # force Session default to be secure
309 # force Session default to be secure
305 default_secure(self.config)
310 default_secure(self.config)
306 # Create a KernelManager and start a kernel.
311 # Create a KernelManager and start a kernel.
307 self.kernel_manager = MappingKernelManager(
312 self.kernel_manager = MappingKernelManager(
308 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
313 config=self.config, log=self.log, kernel_argv=self.kernel_argv,
309 connection_dir = self.profile_dir.security_dir,
314 connection_dir = self.profile_dir.security_dir,
310 )
315 )
311 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
316 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
312 self.notebook_manager.list_notebooks()
317 self.notebook_manager.list_notebooks()
313
318
314 def init_logging(self):
319 def init_logging(self):
315 super(NotebookApp, self).init_logging()
320 super(NotebookApp, self).init_logging()
316 # This prevents double log messages because tornado use a root logger that
321 # This prevents double log messages because tornado use a root logger that
317 # self.log is a child of. The logging module dipatches log messages to a log
322 # self.log is a child of. The logging module dipatches log messages to a log
318 # and all of its ancenstors until propagate is set to False.
323 # and all of its ancenstors until propagate is set to False.
319 self.log.propagate = False
324 self.log.propagate = False
320
325
321 @catch_config_error
326 @catch_config_error
322 def initialize(self, argv=None):
327 def initialize(self, argv=None):
323 super(NotebookApp, self).initialize(argv)
328 super(NotebookApp, self).initialize(argv)
324 self.init_configurables()
329 self.init_configurables()
325 self.web_app = NotebookWebApplication(
330 self.web_app = NotebookWebApplication(
326 self, self.kernel_manager, self.notebook_manager, self.log,
331 self, self.kernel_manager, self.notebook_manager, self.log,
327 self.webapp_settings
332 self.webapp_settings
328 )
333 )
329 if self.certfile:
334 if self.certfile:
330 ssl_options = dict(certfile=self.certfile)
335 ssl_options = dict(certfile=self.certfile)
331 if self.keyfile:
336 if self.keyfile:
332 ssl_options['keyfile'] = self.keyfile
337 ssl_options['keyfile'] = self.keyfile
333 else:
338 else:
334 ssl_options = None
339 ssl_options = None
335 self.web_app.password = self.password
340 self.web_app.password = self.password
336 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
341 self.http_server = httpserver.HTTPServer(self.web_app, ssl_options=ssl_options)
337 if ssl_options is None and not self.ip:
342 if ssl_options is None and not self.ip:
338 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
343 self.log.critical('WARNING: the notebook server is listening on all IP addresses '
339 'but not using any encryption or authentication. This is highly '
344 'but not using any encryption or authentication. This is highly '
340 'insecure and not recommended.')
345 'insecure and not recommended.')
341
346
342 # Try random ports centered around the default.
347 # Try random ports centered around the default.
343 from random import randint
348 from random import randint
344 n = 50 # Max number of attempts, keep reasonably large.
349 n = 50 # Max number of attempts, keep reasonably large.
345 for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
350 for port in range(self.port, self.port+5) + [self.port + randint(-2*n, 2*n) for i in range(n-5)]:
346 try:
351 try:
347 self.http_server.listen(port, self.ip)
352 self.http_server.listen(port, self.ip)
348 except socket.error, e:
353 except socket.error, e:
349 if e.errno != errno.EADDRINUSE:
354 if e.errno != errno.EADDRINUSE:
350 raise
355 raise
351 self.log.info('The port %i is already in use, trying another random port.' % port)
356 self.log.info('The port %i is already in use, trying another random port.' % port)
352 else:
357 else:
353 self.port = port
358 self.port = port
354 break
359 break
355
360
356 def start(self):
361 def start(self):
357 ip = self.ip if self.ip else '[all ip addresses on your system]'
362 ip = self.ip if self.ip else '[all ip addresses on your system]'
358 proto = 'https' if self.certfile else 'http'
363 proto = 'https' if self.certfile else 'http'
359 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
364 self.log.info("The IPython Notebook is running at: %s://%s:%i" % (proto,
360 ip,
365 ip,
361 self.port))
366 self.port))
362 if self.open_browser:
367 if self.open_browser:
363 ip = self.ip or '127.0.0.1'
368 ip = self.ip or '127.0.0.1'
364 b = lambda : webbrowser.open("%s://%s:%i" % (proto, ip, self.port),
369 b = lambda : webbrowser.open("%s://%s:%i" % (proto, ip, self.port),
365 new=2)
370 new=2)
366 threading.Thread(target=b).start()
371 threading.Thread(target=b).start()
367
372
368 ioloop.IOLoop.instance().start()
373 ioloop.IOLoop.instance().start()
369
374
370 #-----------------------------------------------------------------------------
375 #-----------------------------------------------------------------------------
371 # Main entry point
376 # Main entry point
372 #-----------------------------------------------------------------------------
377 #-----------------------------------------------------------------------------
373
378
374 def launch_new_instance():
379 def launch_new_instance():
375 app = NotebookApp()
380 app = NotebookApp()
376 app.initialize()
381 app.initialize()
377 app.start()
382 app.start()
378
383
@@ -1,255 +1,255 b''
1 """A notebook manager that uses the local file system for storage.
1 """A notebook manager that uses the local file system for storage.
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 import datetime
19 import datetime
20 import os
20 import os
21 import uuid
21 import uuid
22 import glob
22 import glob
23
23
24 from tornado import web
24 from tornado import web
25
25
26 from IPython.config.configurable import LoggingConfigurable
26 from IPython.config.configurable import LoggingConfigurable
27 from IPython.nbformat import current
27 from IPython.nbformat import current
28 from IPython.utils.traitlets import Unicode, List, Dict, Bool
28 from IPython.utils.traitlets import Unicode, List, Dict, Bool
29
29
30
31 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
32 # Code
31 # Classes
33 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
34
33
35
36 class NotebookManager(LoggingConfigurable):
34 class NotebookManager(LoggingConfigurable):
37
35
38 notebook_dir = Unicode(os.getcwd(), config=True, help="""
36 notebook_dir = Unicode(os.getcwd(), config=True, help="""
39 The directory to use for notebooks.
37 The directory to use for notebooks.
40 """)
38 """)
41
39
42 save_script = Bool(False, config=True,
40 save_script = Bool(False, config=True,
43 help="""Also save notebooks as a Python script.
41 help="""Automatically create a Python script when saving the notebook.
44
42
45 For easier use of import/%loadpy across notebooks, a <notebook-name>.py
43 For easier use of import, %run and %loadpy across notebooks, a
46 script will be created next to any <notebook-name>.ipynb on each save.
44 <notebook-name>.py script will be created next to any
45 <notebook-name>.ipynb on each save. This can also be set with the
46 short `--script` flag.
47 """
47 """
48 )
48 )
49
49
50 filename_ext = Unicode(u'.ipynb')
50 filename_ext = Unicode(u'.ipynb')
51 allowed_formats = List([u'json',u'py'])
51 allowed_formats = List([u'json',u'py'])
52
52
53 # Map notebook_ids to notebook names
53 # Map notebook_ids to notebook names
54 mapping = Dict()
54 mapping = Dict()
55 # Map notebook names to notebook_ids
55 # Map notebook names to notebook_ids
56 rev_mapping = Dict()
56 rev_mapping = Dict()
57
57
58 def list_notebooks(self):
58 def list_notebooks(self):
59 """List all notebooks in the notebook dir.
59 """List all notebooks in the notebook dir.
60
60
61 This returns a list of dicts of the form::
61 This returns a list of dicts of the form::
62
62
63 dict(notebook_id=notebook,name=name)
63 dict(notebook_id=notebook,name=name)
64 """
64 """
65 names = glob.glob(os.path.join(self.notebook_dir,
65 names = glob.glob(os.path.join(self.notebook_dir,
66 '*' + self.filename_ext))
66 '*' + self.filename_ext))
67 names = [os.path.splitext(os.path.basename(name))[0]
67 names = [os.path.splitext(os.path.basename(name))[0]
68 for name in names]
68 for name in names]
69
69
70 data = []
70 data = []
71 for name in names:
71 for name in names:
72 if name not in self.rev_mapping:
72 if name not in self.rev_mapping:
73 notebook_id = self.new_notebook_id(name)
73 notebook_id = self.new_notebook_id(name)
74 else:
74 else:
75 notebook_id = self.rev_mapping[name]
75 notebook_id = self.rev_mapping[name]
76 data.append(dict(notebook_id=notebook_id,name=name))
76 data.append(dict(notebook_id=notebook_id,name=name))
77 data = sorted(data, key=lambda item: item['name'])
77 data = sorted(data, key=lambda item: item['name'])
78 return data
78 return data
79
79
80 def new_notebook_id(self, name):
80 def new_notebook_id(self, name):
81 """Generate a new notebook_id for a name and store its mappings."""
81 """Generate a new notebook_id for a name and store its mappings."""
82 # TODO: the following will give stable urls for notebooks, but unless
82 # TODO: the following will give stable urls for notebooks, but unless
83 # the notebooks are immediately redirected to their new urls when their
83 # the notebooks are immediately redirected to their new urls when their
84 # filemname changes, nasty inconsistencies result. So for now it's
84 # filemname changes, nasty inconsistencies result. So for now it's
85 # disabled and instead we use a random uuid4() call. But we leave the
85 # disabled and instead we use a random uuid4() call. But we leave the
86 # logic here so that we can later reactivate it, whhen the necessary
86 # logic here so that we can later reactivate it, whhen the necessary
87 # url redirection code is written.
87 # url redirection code is written.
88 #notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
88 #notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
89 # 'file://'+self.get_path_by_name(name).encode('utf-8')))
89 # 'file://'+self.get_path_by_name(name).encode('utf-8')))
90
90
91 notebook_id = unicode(uuid.uuid4())
91 notebook_id = unicode(uuid.uuid4())
92
92
93 self.mapping[notebook_id] = name
93 self.mapping[notebook_id] = name
94 self.rev_mapping[name] = notebook_id
94 self.rev_mapping[name] = notebook_id
95 return notebook_id
95 return notebook_id
96
96
97 def delete_notebook_id(self, notebook_id):
97 def delete_notebook_id(self, notebook_id):
98 """Delete a notebook's id only. This doesn't delete the actual notebook."""
98 """Delete a notebook's id only. This doesn't delete the actual notebook."""
99 name = self.mapping[notebook_id]
99 name = self.mapping[notebook_id]
100 del self.mapping[notebook_id]
100 del self.mapping[notebook_id]
101 del self.rev_mapping[name]
101 del self.rev_mapping[name]
102
102
103 def notebook_exists(self, notebook_id):
103 def notebook_exists(self, notebook_id):
104 """Does a notebook exist?"""
104 """Does a notebook exist?"""
105 if notebook_id not in self.mapping:
105 if notebook_id not in self.mapping:
106 return False
106 return False
107 path = self.get_path_by_name(self.mapping[notebook_id])
107 path = self.get_path_by_name(self.mapping[notebook_id])
108 return os.path.isfile(path)
108 return os.path.isfile(path)
109
109
110 def find_path(self, notebook_id):
110 def find_path(self, notebook_id):
111 """Return a full path to a notebook given its notebook_id."""
111 """Return a full path to a notebook given its notebook_id."""
112 try:
112 try:
113 name = self.mapping[notebook_id]
113 name = self.mapping[notebook_id]
114 except KeyError:
114 except KeyError:
115 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
115 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
116 return self.get_path_by_name(name)
116 return self.get_path_by_name(name)
117
117
118 def get_path_by_name(self, name):
118 def get_path_by_name(self, name):
119 """Return a full path to a notebook given its name."""
119 """Return a full path to a notebook given its name."""
120 filename = name + self.filename_ext
120 filename = name + self.filename_ext
121 path = os.path.join(self.notebook_dir, filename)
121 path = os.path.join(self.notebook_dir, filename)
122 return path
122 return path
123
123
124 def get_notebook(self, notebook_id, format=u'json'):
124 def get_notebook(self, notebook_id, format=u'json'):
125 """Get the representation of a notebook in format by notebook_id."""
125 """Get the representation of a notebook in format by notebook_id."""
126 format = unicode(format)
126 format = unicode(format)
127 if format not in self.allowed_formats:
127 if format not in self.allowed_formats:
128 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
128 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
129 last_modified, nb = self.get_notebook_object(notebook_id)
129 last_modified, nb = self.get_notebook_object(notebook_id)
130 kwargs = {}
130 kwargs = {}
131 if format == 'json':
131 if format == 'json':
132 # don't split lines for sending over the wire, because it
132 # don't split lines for sending over the wire, because it
133 # should match the Python in-memory format.
133 # should match the Python in-memory format.
134 kwargs['split_lines'] = False
134 kwargs['split_lines'] = False
135 data = current.writes(nb, format, **kwargs)
135 data = current.writes(nb, format, **kwargs)
136 name = nb.get('name','notebook')
136 name = nb.get('name','notebook')
137 return last_modified, name, data
137 return last_modified, name, data
138
138
139 def get_notebook_object(self, notebook_id):
139 def get_notebook_object(self, notebook_id):
140 """Get the NotebookNode representation of a notebook by notebook_id."""
140 """Get the NotebookNode representation of a notebook by notebook_id."""
141 path = self.find_path(notebook_id)
141 path = self.find_path(notebook_id)
142 if not os.path.isfile(path):
142 if not os.path.isfile(path):
143 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
143 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
144 info = os.stat(path)
144 info = os.stat(path)
145 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
145 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
146 with open(path,'r') as f:
146 with open(path,'r') as f:
147 s = f.read()
147 s = f.read()
148 try:
148 try:
149 # v1 and v2 and json in the .ipynb files.
149 # v1 and v2 and json in the .ipynb files.
150 nb = current.reads(s, u'json')
150 nb = current.reads(s, u'json')
151 except:
151 except:
152 raise web.HTTPError(500, u'Unreadable JSON notebook.')
152 raise web.HTTPError(500, u'Unreadable JSON notebook.')
153 if 'name' not in nb:
153 if 'name' not in nb:
154 nb.name = os.path.split(path)[-1].split(u'.')[0]
154 nb.name = os.path.split(path)[-1].split(u'.')[0]
155 return last_modified, nb
155 return last_modified, nb
156
156
157 def save_new_notebook(self, data, name=None, format=u'json'):
157 def save_new_notebook(self, data, name=None, format=u'json'):
158 """Save a new notebook and return its notebook_id.
158 """Save a new notebook and return its notebook_id.
159
159
160 If a name is passed in, it overrides any values in the notebook data
160 If a name is passed in, it overrides any values in the notebook data
161 and the value in the data is updated to use that value.
161 and the value in the data is updated to use that value.
162 """
162 """
163 if format not in self.allowed_formats:
163 if format not in self.allowed_formats:
164 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
164 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
165
165
166 try:
166 try:
167 nb = current.reads(data.decode('utf-8'), format)
167 nb = current.reads(data.decode('utf-8'), format)
168 except:
168 except:
169 raise web.HTTPError(400, u'Invalid JSON data')
169 raise web.HTTPError(400, u'Invalid JSON data')
170
170
171 if name is None:
171 if name is None:
172 try:
172 try:
173 name = nb.metadata.name
173 name = nb.metadata.name
174 except AttributeError:
174 except AttributeError:
175 raise web.HTTPError(400, u'Missing notebook name')
175 raise web.HTTPError(400, u'Missing notebook name')
176 nb.metadata.name = name
176 nb.metadata.name = name
177
177
178 notebook_id = self.new_notebook_id(name)
178 notebook_id = self.new_notebook_id(name)
179 self.save_notebook_object(notebook_id, nb)
179 self.save_notebook_object(notebook_id, nb)
180 return notebook_id
180 return notebook_id
181
181
182 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
182 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
183 """Save an existing notebook by notebook_id."""
183 """Save an existing notebook by notebook_id."""
184 if format not in self.allowed_formats:
184 if format not in self.allowed_formats:
185 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
185 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
186
186
187 try:
187 try:
188 nb = current.reads(data.decode('utf-8'), format)
188 nb = current.reads(data.decode('utf-8'), format)
189 except:
189 except:
190 raise web.HTTPError(400, u'Invalid JSON data')
190 raise web.HTTPError(400, u'Invalid JSON data')
191
191
192 if name is not None:
192 if name is not None:
193 nb.metadata.name = name
193 nb.metadata.name = name
194 self.save_notebook_object(notebook_id, nb)
194 self.save_notebook_object(notebook_id, nb)
195
195
196 def save_notebook_object(self, notebook_id, nb):
196 def save_notebook_object(self, notebook_id, nb):
197 """Save an existing notebook object by notebook_id."""
197 """Save an existing notebook object by notebook_id."""
198 if notebook_id not in self.mapping:
198 if notebook_id not in self.mapping:
199 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
199 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
200 old_name = self.mapping[notebook_id]
200 old_name = self.mapping[notebook_id]
201 try:
201 try:
202 new_name = nb.metadata.name
202 new_name = nb.metadata.name
203 except AttributeError:
203 except AttributeError:
204 raise web.HTTPError(400, u'Missing notebook name')
204 raise web.HTTPError(400, u'Missing notebook name')
205 path = self.get_path_by_name(new_name)
205 path = self.get_path_by_name(new_name)
206 try:
206 try:
207 with open(path,'w') as f:
207 with open(path,'w') as f:
208 current.write(nb, f, u'json')
208 current.write(nb, f, u'json')
209 except Exception as e:
209 except Exception as e:
210 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
210 raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
211 # save .py script as well
211 # save .py script as well
212 if self.save_script:
212 if self.save_script:
213 pypath = os.path.splitext(path)[0] + '.py'
213 pypath = os.path.splitext(path)[0] + '.py'
214 try:
214 try:
215 with open(pypath,'w') as f:
215 with open(pypath,'w') as f:
216 current.write(nb, f, u'py')
216 current.write(nb, f, u'py')
217 except Exception as e:
217 except Exception as e:
218 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
218 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
219
219
220 if old_name != new_name:
220 if old_name != new_name:
221 old_path = self.get_path_by_name(old_name)
221 old_path = self.get_path_by_name(old_name)
222 if os.path.isfile(old_path):
222 if os.path.isfile(old_path):
223 os.unlink(old_path)
223 os.unlink(old_path)
224 if self.save_script:
224 if self.save_script:
225 old_pypath = os.path.splitext(old_path)[0] + '.py'
225 old_pypath = os.path.splitext(old_path)[0] + '.py'
226 if os.path.isfile(old_pypath):
226 if os.path.isfile(old_pypath):
227 os.unlink(old_pypath)
227 os.unlink(old_pypath)
228 self.mapping[notebook_id] = new_name
228 self.mapping[notebook_id] = new_name
229 self.rev_mapping[new_name] = notebook_id
229 self.rev_mapping[new_name] = notebook_id
230
230
231 def delete_notebook(self, notebook_id):
231 def delete_notebook(self, notebook_id):
232 """Delete notebook by notebook_id."""
232 """Delete notebook by notebook_id."""
233 path = self.find_path(notebook_id)
233 path = self.find_path(notebook_id)
234 if not os.path.isfile(path):
234 if not os.path.isfile(path):
235 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
235 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
236 os.unlink(path)
236 os.unlink(path)
237 self.delete_notebook_id(notebook_id)
237 self.delete_notebook_id(notebook_id)
238
238
239 def new_notebook(self):
239 def new_notebook(self):
240 """Create a new notebook and returns its notebook_id."""
240 """Create a new notebook and returns its notebook_id."""
241 i = 0
241 i = 0
242 while True:
242 while True:
243 name = u'Untitled%i' % i
243 name = u'Untitled%i' % i
244 path = self.get_path_by_name(name)
244 path = self.get_path_by_name(name)
245 if not os.path.isfile(path):
245 if not os.path.isfile(path):
246 break
246 break
247 else:
247 else:
248 i = i+1
248 i = i+1
249 notebook_id = self.new_notebook_id(name)
249 notebook_id = self.new_notebook_id(name)
250 metadata = current.new_metadata(name=name)
250 metadata = current.new_metadata(name=name)
251 nb = current.new_notebook(metadata=metadata)
251 nb = current.new_notebook(metadata=metadata)
252 with open(path,'w') as f:
252 with open(path,'w') as f:
253 current.write(nb, f, u'json')
253 current.write(nb, f, u'json')
254 return notebook_id
254 return notebook_id
255
255
@@ -1,383 +1,417 b''
1 .. _htmlnotebook:
1 .. _htmlnotebook:
2
2
3 =========================
3 =========================
4 An HTML Notebook IPython
4 An HTML Notebook IPython
5 =========================
5 =========================
6
6
7 .. seealso::
7 .. seealso::
8
8
9 :ref:`Installation requirements <installnotebook>` for the Notebook.
9 :ref:`Installation requirements <installnotebook>` for the Notebook.
10
10
11 The IPython Notebook consists of two related components:
11 The IPython Notebook consists of two related components:
12
12
13 * An JSON based Notebook document format for recording and distributing
13 * An JSON based Notebook document format for recording and distributing
14 Python code and rich text.
14 Python code and rich text.
15 * A web-based user interface for authoring and running notebook documents.
15 * A web-based user interface for authoring and running notebook documents.
16
16
17 The Notebook can be used by starting the Notebook server with the
17 The Notebook can be used by starting the Notebook server with the
18 command::
18 command::
19
19
20 $ ipython notebook
20 $ ipython notebook
21
21
22 Note that by default, the notebook doesn't load pylab, it's just a normal
22 Note that by default, the notebook doesn't load pylab, it's just a normal
23 IPython session like any other. If you want pylab support, you must use::
23 IPython session like any other. If you want pylab support, you must use::
24
24
25 $ ipython notebook --pylab
25 $ ipython notebook --pylab
26
26
27 which will behave similar to the terminal and Qt console versions, using your
27 which will behave similar to the terminal and Qt console versions, using your
28 default matplotlib backend and providing floating interactive plot windows. If
28 default matplotlib backend and providing floating interactive plot windows. If
29 you want inline figures, you must manually select the ``inline`` backend::
29 you want inline figures, you must manually select the ``inline`` backend::
30
30
31 $ ipython notebook --pylab inline
31 $ ipython notebook --pylab inline
32
32
33 This server uses the same ZeroMQ-based two process kernel architecture as
33 This server uses the same ZeroMQ-based two process kernel architecture as
34 the QT Console as well Tornado for serving HTTP/S requests. Some of the main
34 the QT Console as well Tornado for serving HTTP/S requests. Some of the main
35 features of the Notebook include:
35 features of the Notebook include:
36
36
37 * Display rich data (png/html/latex/svg) in the browser as a result of
37 * Display rich data (png/html/latex/svg) in the browser as a result of
38 computations.
38 computations.
39 * Compose text cells using HTML and Markdown.
39 * Compose text cells using HTML and Markdown.
40 * Import and export notebook documents in range of formats (.ipynb, .py).
40 * Import and export notebook documents in range of formats (.ipynb, .py).
41 * In browser syntax highlighting, tab completion and autoindentation.
41 * In browser syntax highlighting, tab completion and autoindentation.
42 * Inline matplotlib plots that can be stored in Notebook documents and opened
42 * Inline matplotlib plots that can be stored in Notebook documents and opened
43 later.
43 later.
44
44
45 See :ref:`our installation documentation <install_index>` for directions on
45 See :ref:`our installation documentation <install_index>` for directions on
46 how to install the notebook and its dependencies.
46 how to install the notebook and its dependencies.
47
47
48 .. note::
48 .. note::
49
49
50 You can start more than one notebook server at the same time, if you want to
50 You can start more than one notebook server at the same time, if you want to
51 work on notebooks in different directories. By default the first notebook
51 work on notebooks in different directories. By default the first notebook
52 server starts in port 8888, later notebooks search for random ports near
52 server starts in port 8888, later notebooks search for random ports near
53 that one. You can also manually specify the port with the ``--port``
53 that one. You can also manually specify the port with the ``--port``
54 option.
54 option.
55
55
56
56
57 Basic Usage
57 Basic Usage
58 ===========
58 ===========
59
59
60 The landing page of the notebook server application, which we call the IPython
60 The landing page of the notebook server application, which we call the IPython
61 Notebook *dashboard*, shows the notebooks currently available in the directory
61 Notebook *dashboard*, shows the notebooks currently available in the directory
62 in which the application was started, and allows you to create new notebooks.
62 in which the application was started, and allows you to create new notebooks.
63
63
64 A notebook is a combination of two things:
64 A notebook is a combination of two things:
65
65
66 1. An interactive session connected to an IPython kernel, controlled by a web
66 1. An interactive session connected to an IPython kernel, controlled by a web
67 application that can send input to the console and display many types of
67 application that can send input to the console and display many types of
68 output (text, graphics, mathematics and more). This is the same kernel used
68 output (text, graphics, mathematics and more). This is the same kernel used
69 by the :ref:`Qt console <qtconsole>`, but in this case the web console sends
69 by the :ref:`Qt console <qtconsole>`, but in this case the web console sends
70 input in persistent cells that you can edit in-place instead of the
70 input in persistent cells that you can edit in-place instead of the
71 vertically scrolling terminal style used by the Qt console.
71 vertically scrolling terminal style used by the Qt console.
72
72
73 2. A document that can save the inputs and outputs of the session as well as
73 2. A document that can save the inputs and outputs of the session as well as
74 additional text that accompanies the code but is not meant for execution.
74 additional text that accompanies the code but is not meant for execution.
75 In this way, notebook files serve as a complete computational record of a
75 In this way, notebook files serve as a complete computational record of a
76 session including explanatory text and mathematics, code and resulting
76 session including explanatory text and mathematics, code and resulting
77 figures. These documents are internally JSON files and are saved with the
77 figures. These documents are internally JSON files and are saved with the
78 ``.ipynb`` extension.
78 ``.ipynb`` extension.
79
79
80 If you have ever used the Mathematica or Sage notebooks (the latter is also
80 If you have ever used the Mathematica or Sage notebooks (the latter is also
81 web-based__) you should feel right at home. If you have not, you should be
81 web-based__) you should feel right at home. If you have not, you should be
82 able to learn how to use it in just a few minutes.
82 able to learn how to use it in just a few minutes.
83
83
84 .. __: http://sagenb.org
84 .. __: http://sagenb.org
85
85
86
86
87 Creating and editing notebooks
87 Creating and editing notebooks
88 ------------------------------
88 ------------------------------
89
89
90 You can create new notebooks from the dashboard with the ``New Notebook``
90 You can create new notebooks from the dashboard with the ``New Notebook``
91 button or open existing ones by clicking on their name. Once in a notebook,
91 button or open existing ones by clicking on their name. Once in a notebook,
92 your browser tab will reflect the name of that notebook (prefixed with "IPy:").
92 your browser tab will reflect the name of that notebook (prefixed with "IPy:").
93 The URL for that notebook is not meant to be human-readable and is *not*
93 The URL for that notebook is not meant to be human-readable and is *not*
94 persistent across invocations of the notebook server.
94 persistent across invocations of the notebook server.
95
95
96 You can also drag and drop into the area listing files any python file: it
96 You can also drag and drop into the area listing files any python file: it
97 will be imported into a notebook with the same name (but ``.ipynb`` extension)
97 will be imported into a notebook with the same name (but ``.ipynb`` extension)
98 located in the directory where the notebook server was started. This notebook
98 located in the directory where the notebook server was started. This notebook
99 will consist of a single cell with all the code in the file, which you can
99 will consist of a single cell with all the code in the file, which you can
100 later manually partition into individual cells for gradual execution, add text
100 later manually partition into individual cells for gradual execution, add text
101 and graphics, etc.
101 and graphics, etc.
102
102
103
103
104 Workflow and limitations
104 Workflow and limitations
105 ------------------------
105 ------------------------
106
106
107 The normal workflow in a notebook is quite similar to a normal IPython session,
107 The normal workflow in a notebook is quite similar to a normal IPython session,
108 with the difference that you can edit a cell in-place multiple times until you
108 with the difference that you can edit a cell in-place multiple times until you
109 obtain the desired results rather than having to rerun separate scripts with
109 obtain the desired results rather than having to rerun separate scripts with
110 the ``%run`` magic (though magics also work in the notebook). Typically
110 the ``%run`` magic (though magics also work in the notebook). Typically
111 you'll work on a problem in pieces, organizing related pieces into cells and
111 you'll work on a problem in pieces, organizing related pieces into cells and
112 moving forward as previous parts work correctly. This is much more convenient
112 moving forward as previous parts work correctly. This is much more convenient
113 for interactive exploration than breaking up a computation into scripts that
113 for interactive exploration than breaking up a computation into scripts that
114 must be executed together, especially if parts of them take a long time to run
114 must be executed together, especially if parts of them take a long time to run
115 (In the traditional terminal-based IPython, you can use tricks with namespaces
115 (In the traditional terminal-based IPython, you can use tricks with namespaces
116 and ``%run -i`` to achieve this capability, but we think the notebook is a more
116 and ``%run -i`` to achieve this capability, but we think the notebook is a more
117 natural solution for that kind of problem).
117 natural solution for that kind of problem).
118
118
119 The only significant limitation the notebook currently has, compared to the qt
119 The only significant limitation the notebook currently has, compared to the qt
120 console, is that it can not run any code that expects input from the kernel
120 console, is that it can not run any code that expects input from the kernel
121 (such as scripts that call :func:`raw_input`). Very importantly, this means
121 (such as scripts that call :func:`raw_input`). Very importantly, this means
122 that the ``%debug`` magic does *not* work in the notebook! We intend to
122 that the ``%debug`` magic does *not* work in the notebook! We intend to
123 correct this limitation, but in the meantime, there is a way to debug problems
123 correct this limitation, but in the meantime, there is a way to debug problems
124 in the notebook: you can attach a Qt console to your existing notebook kernel,
124 in the notebook: you can attach a Qt console to your existing notebook kernel,
125 and run ``%debug`` from the Qt console. If your notebook is running on a local
125 and run ``%debug`` from the Qt console. If your notebook is running on a local
126 computer (i.e. if you are accessing it via your localhost address at
126 computer (i.e. if you are accessing it via your localhost address at
127 127.0.0.1), you can just type ``%qtconsole`` in the notebook and a Qt console
127 127.0.0.1), you can just type ``%qtconsole`` in the notebook and a Qt console
128 will open up connected to that same kernel.
128 will open up connected to that same kernel.
129
129
130 In general, the notebook server prints the full details of how to connect to
130 In general, the notebook server prints the full details of how to connect to
131 each kernel at the terminal, with lines like::
131 each kernel at the terminal, with lines like::
132
132
133 [IPKernelApp] To connect another client to this kernel, use:
133 [IPKernelApp] To connect another client to this kernel, use:
134 [IPKernelApp] --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
134 [IPKernelApp] --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
135
135
136 This is the name of a JSON file that contains all the port and validation
136 This is the name of a JSON file that contains all the port and validation
137 information necessary to connect to the kernel. You can manually start a
137 information necessary to connect to the kernel. You can manually start a
138 qt console with::
138 qt console with::
139
139
140 ipython qtconsole --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
140 ipython qtconsole --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
141
141
142 and if you only have a single kernel running, simply typing::
142 and if you only have a single kernel running, simply typing::
143
143
144 ipython qtconsole --existing
144 ipython qtconsole --existing
145
145
146 will automatically find it (it will always find the most recently started
146 will automatically find it (it will always find the most recently started
147 kernel if there is more than one). You can also request this connection data
147 kernel if there is more than one). You can also request this connection data
148 by typing ``%connect_info``; this will print the same file information as well
148 by typing ``%connect_info``; this will print the same file information as well
149 as the content of the JSON data structure it contains.
149 as the content of the JSON data structure it contains.
150
150
151
151
152 Text input
152 Text input
153 ----------
153 ----------
154
154
155 In addition to code cells and the output they produce (such as figures), you
155 In addition to code cells and the output they produce (such as figures), you
156 can also type text not meant for execution. To type text, change the type of a
156 can also type text not meant for execution. To type text, change the type of a
157 cell from ``Code`` to ``Markdown`` by using the button or the :kbd:`Ctrl-m m`
157 cell from ``Code`` to ``Markdown`` by using the button or the :kbd:`Ctrl-m m`
158 keybinding (see below). You can then type any text in Markdown_ syntax, as
158 keybinding (see below). You can then type any text in Markdown_ syntax, as
159 well as mathematical expressions if you use ``$...$`` for inline math or
159 well as mathematical expressions if you use ``$...$`` for inline math or
160 ``$$...$$`` for displayed math.
160 ``$$...$$`` for displayed math.
161
161
162
162
163 Exporting a notebook and importing existing scripts
163 Exporting a notebook and importing existing scripts
164 ---------------------------------------------------
164 ---------------------------------------------------
165
165
166 If you want to provide others with a static HTML or PDF view of your notebook,
166 If you want to provide others with a static HTML or PDF view of your notebook,
167 use the ``Print`` button. This opens a static view of the document, which you
167 use the ``Print`` button. This opens a static view of the document, which you
168 can print to PDF using your operating system's facilities, or save to a file
168 can print to PDF using your operating system's facilities, or save to a file
169 with your web browser's 'Save' option (note that typically, this will create
169 with your web browser's 'Save' option (note that typically, this will create
170 both an html file *and* a directory called `notebook_name_files` next to it
170 both an html file *and* a directory called `notebook_name_files` next to it
171 that contains all the necessary style information, so if you intend to share
171 that contains all the necessary style information, so if you intend to share
172 this, you must send the directory along with the main html file).
172 this, you must send the directory along with the main html file).
173
173
174 The `Download` button lets you save a notebook file to the Download area
174 The `Download` button lets you save a notebook file to the Download area
175 configured by your web browser (particularly useful if you are running the
175 configured by your web browser (particularly useful if you are running the
176 notebook server on a remote host and need a file locally). The notebook is
176 notebook server on a remote host and need a file locally). The notebook is
177 saved by default with the ``.ipynb`` extension and the files contain JSON data
177 saved by default with the ``.ipynb`` extension and the files contain JSON data
178 that is not meant for human editing or consumption. But you can always export
178 that is not meant for human editing or consumption. But you can always export
179 the input part of a notebook to a plain python script by choosing Python format
179 the input part of a notebook to a plain python script by choosing Python format
180 in the `Download` drop list. This removes all output and saves the text cells
180 in the `Download` drop list. This removes all output and saves the text cells
181 in comment areas. See ref:`below <notebook_format>` for more details on the
181 in comment areas. See ref:`below <notebook_format>` for more details on the
182 notebook format.
182 notebook format.
183
183
184 The notebook can also *import* ``.py`` files as notebooks, by dragging and
184 The notebook can also *import* ``.py`` files as notebooks, by dragging and
185 dropping the file into the notebook dashboard file list area. By default, the
185 dropping the file into the notebook dashboard file list area. By default, the
186 entire contents of the file will be loaded into a single code cell. But if
186 entire contents of the file will be loaded into a single code cell. But if
187 prior to import, you manually add the ``# <nbformat>2</nbformat>`` marker at
187 prior to import, you manually add the ``# <nbformat>2</nbformat>`` marker at
188 the start and then add separators for text/code cells, you can get a cleaner
188 the start and then add separators for text/code cells, you can get a cleaner
189 import with the file broken into individual cells.
189 import with the file broken into individual cells.
190
190
191 If you want use notebooks as scripts a lot, then you can set::
192
193 c.NotebookManager.save_script=True
194
195 which will instruct the notebook server to save the ``.py`` export of each
196 notebook adjacent to the ``.ipynb`` at every save. Then these can be ``%run``
197 or imported from regular IPython sessions or other notebooks.
198
199 .. warning::
191 .. warning::
200
192
201 While in simple cases you can roundtrip a notebook to Python, edit the
193 While in simple cases you can roundtrip a notebook to Python, edit the
202 python file and import it back without loss of main content, this is in
194 python file and import it back without loss of main content, this is in
203 general *not guaranteed to work at all*. First, there is extra metadata
195 general *not guaranteed to work at all*. First, there is extra metadata
204 saved in the notebook that may not be saved to the ``.py`` format. And as
196 saved in the notebook that may not be saved to the ``.py`` format. And as
205 the notebook format evolves in complexity, there will be attributes of the
197 the notebook format evolves in complexity, there will be attributes of the
206 notebook that will not survive a roundtrip through the Python form. You
198 notebook that will not survive a roundtrip through the Python form. You
207 should think of the Python format as a way to output a script version of a
199 should think of the Python format as a way to output a script version of a
208 notebook and the import capabilities as a way to load existing code to get a
200 notebook and the import capabilities as a way to load existing code to get a
209 notebook started. But the Python version is *not* an alternate notebook
201 notebook started. But the Python version is *not* an alternate notebook
210 format.
202 format.
211
203
204
205 Importing or executing a notebook as a normal Python file
206 ---------------------------------------------------------
207
208 The native format of the notebook, a file with a ``.ipynb`` extension, is a
209 JSON container of all the input and output of the notebook, and therefore not
210 valid Python by itself. This means that by default, you can not import a
211 notebook or execute it as a normal python script. But if you want use
212 notebooks as regular Python files, you can start the notebook server with::
213
214 ipython notebook --script
215
216 or you can set this option permanently in your configuration file with::
217
218 c.NotebookManager.save_script=True
219
220 This will instruct the notebook server to save the ``.py`` export of each
221 notebook adjacent to the ``.ipynb`` at every save. These files can be
222 ``%run``, imported from regular IPython sessions or other notebooks, or
223 executed at the command-line as normal Python files. Since we export the raw
224 code you have typed, for these files to be importable from other code you will
225 have to avoid using syntax such as ``%magics`` and other IPython-specific
226 extensions to the language.
227
228 In regular practice, the standard way to differentiate importable code from the
229 'executable' part of a script is to put at the bottom::
230
231 if __name__ == '__main__':
232 # rest of the code...
233
234 Since all cells in the notebook are run as top-level code, you'll need to
235 similarly protect *all* cells that you do not want executed when other scripts
236 try to import your notebook. A convenient shortand for this is to define early
237 on::
238
239 script = __name__ == '__main__':
240
241 and then on any cell that you need to protect, use::
242
243 if script:
244 # rest of the cell...
245
212
246
213 Keyboard use
247 Keyboard use
214 ------------
248 ------------
215
249
216 All actions in the notebook can be achieved with the mouse, but we have also
250 All actions in the notebook can be achieved with the mouse, but we have also
217 added keyboard shortcuts for the most common ones, so that productive use of
251 added keyboard shortcuts for the most common ones, so that productive use of
218 the notebook can be achieved with minimal mouse intervention. The main
252 the notebook can be achieved with minimal mouse intervention. The main
219 key bindings you need to remember are:
253 key bindings you need to remember are:
220
254
221 * :kbd:`Shift-Enter`: execute the current cell (similar to the Qt console),
255 * :kbd:`Shift-Enter`: execute the current cell (similar to the Qt console),
222 show output (if any) and create a new cell below. Note that in the notebook,
256 show output (if any) and create a new cell below. Note that in the notebook,
223 simply using :kbd:`Enter` *never* forces execution, it simply inserts a new
257 simply using :kbd:`Enter` *never* forces execution, it simply inserts a new
224 line in the current cell. Therefore, in the notebook you must always use
258 line in the current cell. Therefore, in the notebook you must always use
225 :kbd:`Shift-Enter` to get execution (or use the mouse and click on the ``Run
259 :kbd:`Shift-Enter` to get execution (or use the mouse and click on the ``Run
226 Selected`` button).
260 Selected`` button).
227
261
228 * :kbd:`Ctrl-Enter`: execute the current cell in "terminal mode", where any
262 * :kbd:`Ctrl-Enter`: execute the current cell in "terminal mode", where any
229 output is shown but the cursor stays in the current cell, whose input
263 output is shown but the cursor stays in the current cell, whose input
230 area is flushed empty. This is convenient to do quick in-place experiments
264 area is flushed empty. This is convenient to do quick in-place experiments
231 or query things like filesystem content without creating additional cells you
265 or query things like filesystem content without creating additional cells you
232 may not want saved in your notebook.
266 may not want saved in your notebook.
233
267
234 * :kbd:`Ctrl-m`: this is the prefix for all other keybindings, which consist
268 * :kbd:`Ctrl-m`: this is the prefix for all other keybindings, which consist
235 of an additional single letter. Type :kbd:`Ctrl-m h` (that is, the sole
269 of an additional single letter. Type :kbd:`Ctrl-m h` (that is, the sole
236 letter :kbd:`h` after :kbd:`Ctrl-m`) and IPython will show you the remaining
270 letter :kbd:`h` after :kbd:`Ctrl-m`) and IPython will show you the remaining
237 available keybindings.
271 available keybindings.
238
272
239
273
240 .. _notebook_security:
274 .. _notebook_security:
241
275
242 Security
276 Security
243 ========
277 ========
244
278
245 You can protect your notebook server with a simple single-password by
279 You can protect your notebook server with a simple single-password by
246 setting the :attr:`NotebookApp.password` configurable. You can prepare a
280 setting the :attr:`NotebookApp.password` configurable. You can prepare a
247 hashed password using the function :func:`IPython.lib.security.passwd`:
281 hashed password using the function :func:`IPython.lib.security.passwd`:
248
282
249 .. sourcecode:: ipython
283 .. sourcecode:: ipython
250
284
251 In [1]: from IPython.lib import passwd
285 In [1]: from IPython.lib import passwd
252 In [2]: passwd()
286 In [2]: passwd()
253 Enter password:
287 Enter password:
254 Verify password:
288 Verify password:
255 Out[2]: 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
289 Out[2]: 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
256
290
257 .. note::
291 .. note::
258
292
259 :func:`~IPython.lib.security.passwd` can also take the password as a string
293 :func:`~IPython.lib.security.passwd` can also take the password as a string
260 argument. **Do not** pass it as an argument inside an IPython session, as it
294 argument. **Do not** pass it as an argument inside an IPython session, as it
261 will be saved in your input history.
295 will be saved in your input history.
262
296
263 You can then add this to your :file:`ipython_notebook_config.py`, e.g.::
297 You can then add this to your :file:`ipython_notebook_config.py`, e.g.::
264
298
265 # Password to use for web authentication
299 # Password to use for web authentication
266 c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
300 c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
267
301
268 When using a password, it is a good idea to also use SSL, so that your password
302 When using a password, it is a good idea to also use SSL, so that your password
269 is not sent unencrypted by your browser. You can start the notebook to
303 is not sent unencrypted by your browser. You can start the notebook to
270 communicate via a secure protocol mode using a self-signed certificate by
304 communicate via a secure protocol mode using a self-signed certificate by
271 typing::
305 typing::
272
306
273 $ ipython notebook --certfile=mycert.pem
307 $ ipython notebook --certfile=mycert.pem
274
308
275 .. note::
309 .. note::
276
310
277 A self-signed certificate can be generated with openssl. For example, the
311 A self-signed certificate can be generated with openssl. For example, the
278 following command will create a certificate valid for 365 days with both
312 following command will create a certificate valid for 365 days with both
279 the key and certificate data written to the same file::
313 the key and certificate data written to the same file::
280
314
281 $ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
315 $ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
282
316
283 Your browser will warn you of a dangerous certificate because it is
317 Your browser will warn you of a dangerous certificate because it is
284 self-signed. If you want to have a fully compliant certificate that will not
318 self-signed. If you want to have a fully compliant certificate that will not
285 raise warnings, it is possible (but rather involved) to obtain one for free,
319 raise warnings, it is possible (but rather involved) to obtain one for free,
286 `as explained in detailed in this tutorial`__.
320 `as explained in detailed in this tutorial`__.
287
321
288 .. __: http://arstechnica.com/security/news/2009/12/how-to-get-set-with-a-secure-sertificate-for-free.ars
322 .. __: http://arstechnica.com/security/news/2009/12/how-to-get-set-with-a-secure-sertificate-for-free.ars
289
323
290 Keep in mind that when you enable SSL support, you'll need to access the
324 Keep in mind that when you enable SSL support, you'll need to access the
291 notebook server over ``https://``, not over plain ``http://``. The startup
325 notebook server over ``https://``, not over plain ``http://``. The startup
292 message from the server prints this, but it's easy to overlook and think the
326 message from the server prints this, but it's easy to overlook and think the
293 server is for some reason non-responsive.
327 server is for some reason non-responsive.
294
328
295
329
296 Quick Howto: running a public notebook server
330 Quick Howto: running a public notebook server
297 =============================================
331 =============================================
298
332
299 If you want to access your notebook server remotely with just a web browser,
333 If you want to access your notebook server remotely with just a web browser,
300 here is a quick set of instructions. Start by creating a certificate file and
334 here is a quick set of instructions. Start by creating a certificate file and
301 a hashed password as explained above. Then, create a custom profile for the
335 a hashed password as explained above. Then, create a custom profile for the
302 notebook. At the command line, type::
336 notebook. At the command line, type::
303
337
304 ipython profile create nbserver
338 ipython profile create nbserver
305
339
306 In the profile directory, edit the file ``ipython_notebook_config.py``. By
340 In the profile directory, edit the file ``ipython_notebook_config.py``. By
307 default the file has all fields commented, the minimum set you need to
341 default the file has all fields commented, the minimum set you need to
308 uncomment and edit is here::
342 uncomment and edit is here::
309
343
310 c = get_config()
344 c = get_config()
311
345
312 # Kernel config
346 # Kernel config
313 c.IPKernelApp.pylab = 'inline' # if you want plotting support always
347 c.IPKernelApp.pylab = 'inline' # if you want plotting support always
314
348
315 # Notebook config
349 # Notebook config
316 c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
350 c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
317 c.NotebookApp.ip = '*'
351 c.NotebookApp.ip = '*'
318 c.NotebookApp.open_browser = False
352 c.NotebookApp.open_browser = False
319 c.NotebookApp.password = u'sha1:bcd259ccf...your hashed password here'
353 c.NotebookApp.password = u'sha1:bcd259ccf...your hashed password here'
320 # It's a good idea to put it on a known, fixed port
354 # It's a good idea to put it on a known, fixed port
321 c.NotebookApp.port = 9999
355 c.NotebookApp.port = 9999
322
356
323 You can then start the notebook and access it later by pointing your browser to
357 You can then start the notebook and access it later by pointing your browser to
324 ``https://your.host.com:9999``.
358 ``https://your.host.com:9999``.
325
359
326 .. _notebook_format:
360 .. _notebook_format:
327
361
328 The notebook format
362 The notebook format
329 ===================
363 ===================
330
364
331 The notebooks themselves are JSON files with an ``ipynb`` extension, formatted
365 The notebooks themselves are JSON files with an ``ipynb`` extension, formatted
332 as legibly as possible with minimal extra indentation and cell content broken
366 as legibly as possible with minimal extra indentation and cell content broken
333 across lines to make them reasonably friendly to use in version-control
367 across lines to make them reasonably friendly to use in version-control
334 workflows. You should be very careful if you ever edit manually this JSON
368 workflows. You should be very careful if you ever edit manually this JSON
335 data, as it is extremely easy to corrupt its internal structure and make the
369 data, as it is extremely easy to corrupt its internal structure and make the
336 file impossible to load. In general, you should consider the notebook as a
370 file impossible to load. In general, you should consider the notebook as a
337 file meant only to be edited by IPython itself, not for hand-editing.
371 file meant only to be edited by IPython itself, not for hand-editing.
338
372
339 .. note::
373 .. note::
340
374
341 Binary data such as figures are directly saved in the JSON file. This
375 Binary data such as figures are directly saved in the JSON file. This
342 provides convenient single-file portability but means the files can be
376 provides convenient single-file portability but means the files can be
343 large and diffs of binary data aren't very meaningful. Since the binary
377 large and diffs of binary data aren't very meaningful. Since the binary
344 blobs are encoded in a single line they only affect one line of the diff
378 blobs are encoded in a single line they only affect one line of the diff
345 output, but they are typically very long lines. You can use the
379 output, but they are typically very long lines. You can use the
346 'ClearAll' button to remove all output from a notebook prior to
380 'ClearAll' button to remove all output from a notebook prior to
347 committing it to version control, if this is a concern.
381 committing it to version control, if this is a concern.
348
382
349 The notebook server can also generate a pure-python version of your notebook,
383 The notebook server can also generate a pure-python version of your notebook,
350 by clicking on the 'Download' button and selecting ``py`` as the format. This
384 by clicking on the 'Download' button and selecting ``py`` as the format. This
351 file will contain all the code cells from your notebook verbatim, and all text
385 file will contain all the code cells from your notebook verbatim, and all text
352 cells prepended with a comment marker. The separation between code and text
386 cells prepended with a comment marker. The separation between code and text
353 cells is indicated with special comments and there is a header indicating the
387 cells is indicated with special comments and there is a header indicating the
354 format version. All output is stripped out when exporting to python.
388 format version. All output is stripped out when exporting to python.
355
389
356 Here is an example of a simple notebook with one text cell and one code input
390 Here is an example of a simple notebook with one text cell and one code input
357 cell, when exported to python format::
391 cell, when exported to python format::
358
392
359 # <nbformat>2</nbformat>
393 # <nbformat>2</nbformat>
360
394
361 # <markdowncell>
395 # <markdowncell>
362
396
363 # A text cell
397 # A text cell
364
398
365 # <codecell>
399 # <codecell>
366
400
367 print "hello IPython"
401 print "hello IPython"
368
402
369
403
370 Known Issues
404 Known Issues
371 ============
405 ============
372
406
373 When behind a proxy, especially if your system or browser is set to autodetect
407 When behind a proxy, especially if your system or browser is set to autodetect
374 the proxy, the html notebook might fail to connect to the server's websockets,
408 the proxy, the html notebook might fail to connect to the server's websockets,
375 and present you with a warning at startup. In this case, you need to configure
409 and present you with a warning at startup. In this case, you need to configure
376 your system not to use the proxy for the server's address.
410 your system not to use the proxy for the server's address.
377
411
378 In Firefox, for example, go to the Preferences panel, Advanced section,
412 In Firefox, for example, go to the Preferences panel, Advanced section,
379 Network tab, click 'Settings...', and add the address of the notebook server
413 Network tab, click 'Settings...', and add the address of the notebook server
380 to the 'No proxy for' field.
414 to the 'No proxy for' field.
381
415
382
416
383 .. _Markdown: http://daringfireball.net/projects/markdown/basics
417 .. _Markdown: http://daringfireball.net/projects/markdown/basics
General Comments 0
You need to be logged in to leave comments. Login now