##// END OF EJS Templates
Better use Python slicing
Thomas Kluyver -
Show More
@@ -1,266 +1,265
1 1 """A base class notebook manager.
2 2
3 3 Authors:
4 4
5 5 * Brian Granger
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 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 os
20 20 import uuid
21 21
22 22 from tornado import web
23 23 from urllib import quote, unquote
24 24
25 25 from IPython.config.configurable import LoggingConfigurable
26 26 from IPython.nbformat import current
27 27 from IPython.utils.traitlets import List, Dict, Unicode, TraitError
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Classes
31 31 #-----------------------------------------------------------------------------
32 32
33 33 class NotebookManager(LoggingConfigurable):
34 34
35 35 # Todo:
36 36 # The notebook_dir attribute is used to mean a couple of different things:
37 37 # 1. Where the notebooks are stored if FileNotebookManager is used.
38 38 # 2. The cwd of the kernel for a project.
39 39 # Right now we use this attribute in a number of different places and
40 40 # we are going to have to disentangle all of this.
41 41 notebook_dir = Unicode(os.getcwdu(), config=True, help="""
42 42 The directory to use for notebooks.
43 43 """)
44 44
45 45 def named_notebook_path(self, notebook_path):
46 46
47 l = len(notebook_path)
48 47 names = notebook_path.split('/')
49 48 if len(names) > 1:
50 name = names[len(names)-1]
51 if name[(len(name)-6):(len(name))] == ".ipynb":
49 name = names[-1]
50 if name.endswith(".ipynb"):
52 51 name = name
53 path = notebook_path[0:l-len(name)-1]+'/'
52 path = notebook_path[0:-len(name)-1]+'/'
54 53 else:
55 54 name = None
56 55 path = notebook_path+'/'
57 56 else:
58 57 name = names[0]
59 if name[(len(name)-6):(len(name))] == ".ipynb":
58 if name.endswith(".ipynb"):
60 59 name = name
61 60 path = None
62 61 else:
63 62 name = None
64 63 path = notebook_path+'/'
65 64 return name, path
66 65
67 66 def url_encode(self, path):
68 67 parts = path.split('/')
69 68 path=""
70 69 for part in parts:
71 70 part = quote(part)
72 71 path = os.path.join(path,part)
73 72 return path
74 73
75 74 def url_decode(self, path):
76 75 parts = path.split('/')
77 76 path=""
78 77 for part in parts:
79 78 part = unquote(part)
80 79 path = os.path.join(path,part)
81 80 return path
82 81
83 82 def _notebook_dir_changed(self, new):
84 83 """do a bit of validation of the notebook dir"""
85 84 if not os.path.isabs(new):
86 85 # If we receive a non-absolute path, make it absolute.
87 86 abs_new = os.path.abspath(new)
88 87 #self.notebook_dir = os.path.dirname(abs_new)
89 88 return
90 89 if os.path.exists(new) and not os.path.isdir(new):
91 90 raise TraitError("notebook dir %r is not a directory" % new)
92 91 if not os.path.exists(new):
93 92 self.log.info("Creating notebook dir %s", new)
94 93 try:
95 94 os.mkdir(new)
96 95 except:
97 96 raise TraitError("Couldn't create notebook dir %r" % new)
98 97
99 98 allowed_formats = List([u'json',u'py'])
100 99
101 100 def add_new_folder(self, path=None):
102 101 new_path = os.path.join(self.notebook_dir, path)
103 102 if not os.path.exists(new_path):
104 103 os.makedirs(new_path)
105 104 else:
106 105 raise web.HTTPError(409, u'Directory already exists or creation permission not allowed.')
107 106
108 107 def load_notebook_names(self, path):
109 108 """Load the notebook names into memory.
110 109
111 110 This should be called once immediately after the notebook manager
112 111 is created to load the existing notebooks into the mapping in
113 112 memory.
114 113 """
115 114 self.list_notebooks(path)
116 115
117 116 def list_notebooks(self):
118 117 """List all notebooks.
119 118
120 119 This returns a list of dicts, each of the form::
121 120
122 121 dict(notebook_id=notebook,name=name)
123 122
124 123 This list of dicts should be sorted by name::
125 124
126 125 data = sorted(data, key=lambda item: item['name'])
127 126 """
128 127 raise NotImplementedError('must be implemented in a subclass')
129 128
130 129
131 130 def notebook_exists(self, notebook_path):
132 131 """Does a notebook exist?"""
133 132
134 133
135 134 def notebook_model(self, notebook_name, notebook_path=None, content=True):
136 135 """ Creates the standard notebook model """
137 136 last_modified, contents = self.read_notebook_object(notebook_name, notebook_path)
138 137 model = {"name": notebook_name,
139 138 "path": notebook_path,
140 139 "last_modified (UTC)": last_modified.ctime()}
141 140 if content == True:
142 141 model['content'] = contents
143 142 return model
144 143
145 144 def get_notebook(self, notebook_name, notebook_path=None, format=u'json'):
146 145 """Get the representation of a notebook in format by notebook_name."""
147 146 format = unicode(format)
148 147 if format not in self.allowed_formats:
149 148 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
150 149 kwargs = {}
151 150 last_mod, nb = self.read_notebook_object(notebook_name, notebook_path)
152 151 if format == 'json':
153 152 # don't split lines for sending over the wire, because it
154 153 # should match the Python in-memory format.
155 154 kwargs['split_lines'] = False
156 155 representation = current.writes(nb, format, **kwargs)
157 156 name = nb.metadata.get('name', 'notebook')
158 157 return last_mod, representation, name
159 158
160 159 def read_notebook_object(self, notebook_name, notebook_path=None):
161 160 """Get the object representation of a notebook by notebook_id."""
162 161 raise NotImplementedError('must be implemented in a subclass')
163 162
164 163 def save_new_notebook(self, data, notebook_path = None, name=None, format=u'json'):
165 164 """Save a new notebook and return its name.
166 165
167 166 If a name is passed in, it overrides any values in the notebook data
168 167 and the value in the data is updated to use that value.
169 168 """
170 169 if format not in self.allowed_formats:
171 170 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
172 171
173 172 try:
174 173 nb = current.reads(data.decode('utf-8'), format)
175 174 except:
176 175 raise web.HTTPError(400, u'Invalid JSON data')
177 176
178 177 if name is None:
179 178 try:
180 179 name = nb.metadata.name
181 180 except AttributeError:
182 181 raise web.HTTPError(400, u'Missing notebook name')
183 182 nb.metadata.name = name
184 183
185 184 notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
186 185 return notebook_name
187 186
188 187 def save_notebook(self, data, notebook_path=None, name=None, new_name=None, format=u'json'):
189 188 """Save an existing notebook by notebook_name."""
190 189 if format not in self.allowed_formats:
191 190 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
192 191
193 192 try:
194 193 nb = current.reads(data.decode('utf-8'), format)
195 194 except:
196 195 raise web.HTTPError(400, u'Invalid JSON data')
197 196
198 197 if name is not None:
199 198 nb.metadata.name = name
200 199 self.write_notebook_object(nb, name, notebook_path, new_name)
201 200
202 201 def write_notebook_object(self, nb, notebook_name=None, notebook_path=None, new_name=None):
203 202 """Write a notebook object and return its notebook_name.
204 203
205 204 If notebook_name is None, this method should create a new notebook_name.
206 205 If notebook_name is not None, this method should check to make sure it
207 206 exists and is valid.
208 207 """
209 208 raise NotImplementedError('must be implemented in a subclass')
210 209
211 210 def delete_notebook(self, notebook_name, notebook_path):
212 211 """Delete notebook by notebook_id."""
213 212 raise NotImplementedError('must be implemented in a subclass')
214 213
215 214 def increment_filename(self, name):
216 215 """Increment a filename to make it unique.
217 216
218 217 This exists for notebook stores that must have unique names. When a notebook
219 218 is created or copied this method constructs a unique filename, typically
220 219 by appending an integer to the name.
221 220 """
222 221 return name
223 222
224 223 def new_notebook(self, notebook_path=None):
225 224 """Create a new notebook and return its notebook_id."""
226 225 name = self.increment_filename('Untitled', notebook_path)
227 226 metadata = current.new_metadata(name=name)
228 227 nb = current.new_notebook(metadata=metadata)
229 228 notebook_name = self.write_notebook_object(nb, notebook_path=notebook_path)
230 229 return notebook_name
231 230
232 231 def copy_notebook(self, name, path=None):
233 232 """Copy an existing notebook and return its notebook_id."""
234 233 last_mod, nb = self.read_notebook_object(name, path)
235 234 name = nb.metadata.name + '-Copy'
236 235 name = self.increment_filename(name, path)
237 236 nb.metadata.name = name
238 237 notebook_name = self.write_notebook_object(nb, notebook_path = path)
239 238 return notebook_name
240 239
241 240 # Checkpoint-related
242 241
243 242 def create_checkpoint(self, notebook_name, notebook_path=None):
244 243 """Create a checkpoint of the current state of a notebook
245 244
246 245 Returns a checkpoint_id for the new checkpoint.
247 246 """
248 247 raise NotImplementedError("must be implemented in a subclass")
249 248
250 249 def list_checkpoints(self, notebook_name, notebook_path=None):
251 250 """Return a list of checkpoints for a given notebook"""
252 251 return []
253 252
254 253 def restore_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
255 254 """Restore a notebook from one of its checkpoints"""
256 255 raise NotImplementedError("must be implemented in a subclass")
257 256
258 257 def delete_checkpoint(self, notebook_name, checkpoint_id, notebook_path=None):
259 258 """delete a checkpoint for a notebook"""
260 259 raise NotImplementedError("must be implemented in a subclass")
261 260
262 261 def log_info(self):
263 262 self.log.info(self.info_string())
264 263
265 264 def info_string(self):
266 265 return "Serving notebooks"
General Comments 0
You need to be logged in to leave comments. Login now