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