##// END OF EJS Templates
When a notebook is written to file, name the metadata name u''.
Brian E. Granger -
Show More
@@ -1,347 +1,357
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) 2011 The IPython Development Team
9 # Copyright (C) 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 io
20 import io
21 import os
21 import os
22 import glob
22 import glob
23 import shutil
23 import shutil
24 from unicodedata import normalize
24 from unicodedata import normalize
25
25
26 from tornado import web
26 from tornado import web
27
27
28 from .nbmanager import NotebookManager
28 from .nbmanager import NotebookManager
29 from IPython.nbformat import current
29 from IPython.nbformat import current
30 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
30 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Classes
33 # Classes
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 class FileNotebookManager(NotebookManager):
36 class FileNotebookManager(NotebookManager):
37
37
38 save_script = Bool(False, config=True,
38 save_script = Bool(False, config=True,
39 help="""Automatically create a Python script when saving the notebook.
39 help="""Automatically create a Python script when saving the notebook.
40
40
41 For easier use of import, %run and %load across notebooks, a
41 For easier use of import, %run and %load across notebooks, a
42 <notebook-name>.py script will be created next to any
42 <notebook-name>.py script will be created next to any
43 <notebook-name>.ipynb on each save. This can also be set with the
43 <notebook-name>.ipynb on each save. This can also be set with the
44 short `--script` flag.
44 short `--script` flag.
45 """
45 """
46 )
46 )
47
47
48 checkpoint_dir = Unicode(config=True,
48 checkpoint_dir = Unicode(config=True,
49 help="""The location in which to keep notebook checkpoints
49 help="""The location in which to keep notebook checkpoints
50
50
51 By default, it is notebook-dir/.ipynb_checkpoints
51 By default, it is notebook-dir/.ipynb_checkpoints
52 """
52 """
53 )
53 )
54 def _checkpoint_dir_default(self):
54 def _checkpoint_dir_default(self):
55 return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
55 return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
56
56
57 def _checkpoint_dir_changed(self, name, old, new):
57 def _checkpoint_dir_changed(self, name, old, new):
58 """do a bit of validation of the checkpoint dir"""
58 """do a bit of validation of the checkpoint dir"""
59 if not os.path.isabs(new):
59 if not os.path.isabs(new):
60 # If we receive a non-absolute path, make it absolute.
60 # If we receive a non-absolute path, make it absolute.
61 abs_new = os.path.abspath(new)
61 abs_new = os.path.abspath(new)
62 self.checkpoint_dir = abs_new
62 self.checkpoint_dir = abs_new
63 return
63 return
64 if os.path.exists(new) and not os.path.isdir(new):
64 if os.path.exists(new) and not os.path.isdir(new):
65 raise TraitError("checkpoint dir %r is not a directory" % new)
65 raise TraitError("checkpoint dir %r is not a directory" % new)
66 if not os.path.exists(new):
66 if not os.path.exists(new):
67 self.log.info("Creating checkpoint dir %s", new)
67 self.log.info("Creating checkpoint dir %s", new)
68 try:
68 try:
69 os.mkdir(new)
69 os.mkdir(new)
70 except:
70 except:
71 raise TraitError("Couldn't create checkpoint dir %r" % new)
71 raise TraitError("Couldn't create checkpoint dir %r" % new)
72
72
73 filename_ext = Unicode(u'.ipynb')
73 filename_ext = Unicode(u'.ipynb')
74
74
75 # Map notebook names to notebook_ids
75 # Map notebook names to notebook_ids
76 rev_mapping = Dict()
76 rev_mapping = Dict()
77
77
78 def get_notebook_names(self):
78 def get_notebook_names(self):
79 """List all notebook names in the notebook dir."""
79 """List all notebook names in the notebook dir."""
80 names = glob.glob(os.path.join(self.notebook_dir,
80 names = glob.glob(os.path.join(self.notebook_dir,
81 '*' + self.filename_ext))
81 '*' + self.filename_ext))
82 names = [normalize('NFC', os.path.splitext(os.path.basename(name))[0])
82 names = [normalize('NFC', os.path.splitext(os.path.basename(name))[0])
83 for name in names]
83 for name in names]
84 return names
84 return names
85
85
86 def list_notebooks(self):
86 def list_notebooks(self):
87 """List all notebooks in the notebook dir."""
87 """List all notebooks in the notebook dir."""
88 names = self.get_notebook_names()
88 names = self.get_notebook_names()
89
89
90 data = []
90 data = []
91 for name in names:
91 for name in names:
92 if name not in self.rev_mapping:
92 if name not in self.rev_mapping:
93 notebook_id = self.new_notebook_id(name)
93 notebook_id = self.new_notebook_id(name)
94 else:
94 else:
95 notebook_id = self.rev_mapping[name]
95 notebook_id = self.rev_mapping[name]
96 data.append(dict(notebook_id=notebook_id,name=name))
96 data.append(dict(notebook_id=notebook_id,name=name))
97 data = sorted(data, key=lambda item: item['name'])
97 data = sorted(data, key=lambda item: item['name'])
98 return data
98 return data
99
99
100 def new_notebook_id(self, name):
100 def new_notebook_id(self, name):
101 """Generate a new notebook_id for a name and store its mappings."""
101 """Generate a new notebook_id for a name and store its mappings."""
102 notebook_id = super(FileNotebookManager, self).new_notebook_id(name)
102 notebook_id = super(FileNotebookManager, self).new_notebook_id(name)
103 self.rev_mapping[name] = notebook_id
103 self.rev_mapping[name] = notebook_id
104 return notebook_id
104 return notebook_id
105
105
106 def delete_notebook_id(self, notebook_id):
106 def delete_notebook_id(self, notebook_id):
107 """Delete a notebook's id in the mapping."""
107 """Delete a notebook's id in the mapping."""
108 name = self.mapping[notebook_id]
108 name = self.mapping[notebook_id]
109 super(FileNotebookManager, self).delete_notebook_id(notebook_id)
109 super(FileNotebookManager, self).delete_notebook_id(notebook_id)
110 del self.rev_mapping[name]
110 del self.rev_mapping[name]
111
111
112 def notebook_exists(self, notebook_id):
112 def notebook_exists(self, notebook_id):
113 """Does a notebook exist?"""
113 """Does a notebook exist?"""
114 exists = super(FileNotebookManager, self).notebook_exists(notebook_id)
114 exists = super(FileNotebookManager, self).notebook_exists(notebook_id)
115 if not exists:
115 if not exists:
116 return False
116 return False
117 path = self.get_path_by_name(self.mapping[notebook_id])
117 path = self.get_path_by_name(self.mapping[notebook_id])
118 return os.path.isfile(path)
118 return os.path.isfile(path)
119
119
120 def get_name(self, notebook_id):
120 def get_name(self, notebook_id):
121 """get a notebook name, raising 404 if not found"""
121 """get a notebook name, raising 404 if not found"""
122 try:
122 try:
123 name = self.mapping[notebook_id]
123 name = self.mapping[notebook_id]
124 except KeyError:
124 except KeyError:
125 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
125 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
126 return name
126 return name
127
127
128 def get_path(self, notebook_id):
128 def get_path(self, notebook_id):
129 """Return a full path to a notebook given its notebook_id."""
129 """Return a full path to a notebook given its notebook_id."""
130 name = self.get_name(notebook_id)
130 name = self.get_name(notebook_id)
131 return self.get_path_by_name(name)
131 return self.get_path_by_name(name)
132
132
133 def get_path_by_name(self, name):
133 def get_path_by_name(self, name):
134 """Return a full path to a notebook given its name."""
134 """Return a full path to a notebook given its name."""
135 filename = name + self.filename_ext
135 filename = name + self.filename_ext
136 path = os.path.join(self.notebook_dir, filename)
136 path = os.path.join(self.notebook_dir, filename)
137 return path
137 return path
138
138
139 def read_notebook_object_from_path(self, path):
139 def read_notebook_object_from_path(self, path):
140 """read a notebook object from a path"""
140 """read a notebook object from a path"""
141 info = os.stat(path)
141 info = os.stat(path)
142 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
142 last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
143 with open(path,'r') as f:
143 with open(path,'r') as f:
144 s = f.read()
144 s = f.read()
145 try:
145 try:
146 # v1 and v2 and json in the .ipynb files.
146 # v1 and v2 and json in the .ipynb files.
147 nb = current.reads(s, u'json')
147 nb = current.reads(s, u'json')
148 except Exception as e:
148 except Exception as e:
149 raise web.HTTPError(500, u'Unreadable JSON notebook: %s' % e)
149 raise web.HTTPError(500, u'Unreadable JSON notebook: %s' % e)
150 return last_modified, nb
150 return last_modified, nb
151
151
152 def read_notebook_object(self, notebook_id):
152 def read_notebook_object(self, notebook_id):
153 """Get the Notebook representation of a notebook by notebook_id."""
153 """Get the Notebook representation of a notebook by notebook_id."""
154 path = self.get_path(notebook_id)
154 path = self.get_path(notebook_id)
155 if not os.path.isfile(path):
155 if not os.path.isfile(path):
156 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
156 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
157 last_modified, nb = self.read_notebook_object_from_path(path)
157 last_modified, nb = self.read_notebook_object_from_path(path)
158 # Always use the filename as the notebook name.
158 # Always use the filename as the notebook name.
159 # Eventually we will get rid of the notebook name in the metadata
160 # but for now, that name is just an empty string. Until the notebooks
161 # web service knows about names in URLs we still pass the name
162 # back to the web app using the metadata though.
159 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
163 nb.metadata.name = os.path.splitext(os.path.basename(path))[0]
160 return last_modified, nb
164 return last_modified, nb
161
165
162 def write_notebook_object(self, nb, notebook_id=None):
166 def write_notebook_object(self, nb, notebook_id=None):
163 """Save an existing notebook object by notebook_id."""
167 """Save an existing notebook object by notebook_id."""
164 try:
168 try:
165 new_name = normalize('NFC', nb.metadata.name)
169 new_name = normalize('NFC', nb.metadata.name)
166 except AttributeError:
170 except AttributeError:
167 raise web.HTTPError(400, u'Missing notebook name')
171 raise web.HTTPError(400, u'Missing notebook name')
168
172
169 if notebook_id is None:
173 if notebook_id is None:
170 notebook_id = self.new_notebook_id(new_name)
174 notebook_id = self.new_notebook_id(new_name)
171
175
172 if notebook_id not in self.mapping:
176 if notebook_id not in self.mapping:
173 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
177 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
174
178
175 old_name = self.mapping[notebook_id]
179 old_name = self.mapping[notebook_id]
176 old_checkpoints = self.list_checkpoints(notebook_id)
180 old_checkpoints = self.list_checkpoints(notebook_id)
177
178 path = self.get_path_by_name(new_name)
181 path = self.get_path_by_name(new_name)
182
183 # Right before we save the notebook, we write an empty string as the
184 # notebook name in the metadata. This is to prepare for removing
185 # this attribute entirely post 1.0. The web app still uses the metadata
186 # name for now.
187 nb.metadata.name = u''
188
179 try:
189 try:
180 self.log.debug("Autosaving notebook %s", path)
190 self.log.debug("Autosaving notebook %s", path)
181 with open(path,'w') as f:
191 with open(path,'w') as f:
182 current.write(nb, f, u'json')
192 current.write(nb, f, u'json')
183 except Exception as e:
193 except Exception as e:
184 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s' % e)
194 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s' % e)
185
195
186 # save .py script as well
196 # save .py script as well
187 if self.save_script:
197 if self.save_script:
188 pypath = os.path.splitext(path)[0] + '.py'
198 pypath = os.path.splitext(path)[0] + '.py'
189 self.log.debug("Writing script %s", pypath)
199 self.log.debug("Writing script %s", pypath)
190 try:
200 try:
191 with io.open(pypath,'w', encoding='utf-8') as f:
201 with io.open(pypath,'w', encoding='utf-8') as f:
192 current.write(nb, f, u'py')
202 current.write(nb, f, u'py')
193 except Exception as e:
203 except Exception as e:
194 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
204 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
195
205
196 # remove old files if the name changed
206 # remove old files if the name changed
197 if old_name != new_name:
207 if old_name != new_name:
198 # update mapping
208 # update mapping
199 self.mapping[notebook_id] = new_name
209 self.mapping[notebook_id] = new_name
200 self.rev_mapping[new_name] = notebook_id
210 self.rev_mapping[new_name] = notebook_id
201 del self.rev_mapping[old_name]
211 del self.rev_mapping[old_name]
202
212
203 # remove renamed original, if it exists
213 # remove renamed original, if it exists
204 old_path = self.get_path_by_name(old_name)
214 old_path = self.get_path_by_name(old_name)
205 if os.path.isfile(old_path):
215 if os.path.isfile(old_path):
206 self.log.debug("unlinking notebook %s", old_path)
216 self.log.debug("unlinking notebook %s", old_path)
207 os.unlink(old_path)
217 os.unlink(old_path)
208
218
209 # cleanup old script, if it exists
219 # cleanup old script, if it exists
210 if self.save_script:
220 if self.save_script:
211 old_pypath = os.path.splitext(old_path)[0] + '.py'
221 old_pypath = os.path.splitext(old_path)[0] + '.py'
212 if os.path.isfile(old_pypath):
222 if os.path.isfile(old_pypath):
213 self.log.debug("unlinking script %s", old_pypath)
223 self.log.debug("unlinking script %s", old_pypath)
214 os.unlink(old_pypath)
224 os.unlink(old_pypath)
215
225
216 # rename checkpoints to follow file
226 # rename checkpoints to follow file
217 for cp in old_checkpoints:
227 for cp in old_checkpoints:
218 checkpoint_id = cp['checkpoint_id']
228 checkpoint_id = cp['checkpoint_id']
219 old_cp_path = self.get_checkpoint_path_by_name(old_name, checkpoint_id)
229 old_cp_path = self.get_checkpoint_path_by_name(old_name, checkpoint_id)
220 new_cp_path = self.get_checkpoint_path_by_name(new_name, checkpoint_id)
230 new_cp_path = self.get_checkpoint_path_by_name(new_name, checkpoint_id)
221 if os.path.isfile(old_cp_path):
231 if os.path.isfile(old_cp_path):
222 self.log.debug("renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
232 self.log.debug("renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
223 os.rename(old_cp_path, new_cp_path)
233 os.rename(old_cp_path, new_cp_path)
224
234
225 return notebook_id
235 return notebook_id
226
236
227 def delete_notebook(self, notebook_id):
237 def delete_notebook(self, notebook_id):
228 """Delete notebook by notebook_id."""
238 """Delete notebook by notebook_id."""
229 nb_path = self.get_path(notebook_id)
239 nb_path = self.get_path(notebook_id)
230 if not os.path.isfile(nb_path):
240 if not os.path.isfile(nb_path):
231 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
241 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
232
242
233 # clear checkpoints
243 # clear checkpoints
234 for checkpoint in self.list_checkpoints(notebook_id):
244 for checkpoint in self.list_checkpoints(notebook_id):
235 checkpoint_id = checkpoint['checkpoint_id']
245 checkpoint_id = checkpoint['checkpoint_id']
236 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
246 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
237 self.log.debug(path)
247 self.log.debug(path)
238 if os.path.isfile(path):
248 if os.path.isfile(path):
239 self.log.debug("unlinking checkpoint %s", path)
249 self.log.debug("unlinking checkpoint %s", path)
240 os.unlink(path)
250 os.unlink(path)
241
251
242 self.log.debug("unlinking notebook %s", nb_path)
252 self.log.debug("unlinking notebook %s", nb_path)
243 os.unlink(nb_path)
253 os.unlink(nb_path)
244 self.delete_notebook_id(notebook_id)
254 self.delete_notebook_id(notebook_id)
245
255
246 def increment_filename(self, basename):
256 def increment_filename(self, basename):
247 """Return a non-used filename of the form basename<int>.
257 """Return a non-used filename of the form basename<int>.
248
258
249 This searches through the filenames (basename0, basename1, ...)
259 This searches through the filenames (basename0, basename1, ...)
250 until is find one that is not already being used. It is used to
260 until is find one that is not already being used. It is used to
251 create Untitled and Copy names that are unique.
261 create Untitled and Copy names that are unique.
252 """
262 """
253 i = 0
263 i = 0
254 while True:
264 while True:
255 name = u'%s%i' % (basename,i)
265 name = u'%s%i' % (basename,i)
256 path = self.get_path_by_name(name)
266 path = self.get_path_by_name(name)
257 if not os.path.isfile(path):
267 if not os.path.isfile(path):
258 break
268 break
259 else:
269 else:
260 i = i+1
270 i = i+1
261 return name
271 return name
262
272
263 # Checkpoint-related utilities
273 # Checkpoint-related utilities
264
274
265 def get_checkpoint_path_by_name(self, name, checkpoint_id):
275 def get_checkpoint_path_by_name(self, name, checkpoint_id):
266 """Return a full path to a notebook checkpoint, given its name and checkpoint id."""
276 """Return a full path to a notebook checkpoint, given its name and checkpoint id."""
267 filename = u"{name}-{checkpoint_id}{ext}".format(
277 filename = u"{name}-{checkpoint_id}{ext}".format(
268 name=name,
278 name=name,
269 checkpoint_id=checkpoint_id,
279 checkpoint_id=checkpoint_id,
270 ext=self.filename_ext,
280 ext=self.filename_ext,
271 )
281 )
272 path = os.path.join(self.checkpoint_dir, filename)
282 path = os.path.join(self.checkpoint_dir, filename)
273 return path
283 return path
274
284
275 def get_checkpoint_path(self, notebook_id, checkpoint_id):
285 def get_checkpoint_path(self, notebook_id, checkpoint_id):
276 """find the path to a checkpoint"""
286 """find the path to a checkpoint"""
277 name = self.get_name(notebook_id)
287 name = self.get_name(notebook_id)
278 return self.get_checkpoint_path_by_name(name, checkpoint_id)
288 return self.get_checkpoint_path_by_name(name, checkpoint_id)
279
289
280 def get_checkpoint_info(self, notebook_id, checkpoint_id):
290 def get_checkpoint_info(self, notebook_id, checkpoint_id):
281 """construct the info dict for a given checkpoint"""
291 """construct the info dict for a given checkpoint"""
282 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
292 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
283 stats = os.stat(path)
293 stats = os.stat(path)
284 last_modified = datetime.datetime.utcfromtimestamp(stats.st_mtime)
294 last_modified = datetime.datetime.utcfromtimestamp(stats.st_mtime)
285 info = dict(
295 info = dict(
286 checkpoint_id = checkpoint_id,
296 checkpoint_id = checkpoint_id,
287 last_modified = last_modified,
297 last_modified = last_modified,
288 )
298 )
289
299
290 return info
300 return info
291
301
292 # public checkpoint API
302 # public checkpoint API
293
303
294 def create_checkpoint(self, notebook_id):
304 def create_checkpoint(self, notebook_id):
295 """Create a checkpoint from the current state of a notebook"""
305 """Create a checkpoint from the current state of a notebook"""
296 nb_path = self.get_path(notebook_id)
306 nb_path = self.get_path(notebook_id)
297 # only the one checkpoint ID:
307 # only the one checkpoint ID:
298 checkpoint_id = u"checkpoint"
308 checkpoint_id = u"checkpoint"
299 cp_path = self.get_checkpoint_path(notebook_id, checkpoint_id)
309 cp_path = self.get_checkpoint_path(notebook_id, checkpoint_id)
300 self.log.debug("creating checkpoint for notebook %s", notebook_id)
310 self.log.debug("creating checkpoint for notebook %s", notebook_id)
301 if not os.path.exists(self.checkpoint_dir):
311 if not os.path.exists(self.checkpoint_dir):
302 os.mkdir(self.checkpoint_dir)
312 os.mkdir(self.checkpoint_dir)
303 shutil.copy2(nb_path, cp_path)
313 shutil.copy2(nb_path, cp_path)
304
314
305 # return the checkpoint info
315 # return the checkpoint info
306 return self.get_checkpoint_info(notebook_id, checkpoint_id)
316 return self.get_checkpoint_info(notebook_id, checkpoint_id)
307
317
308 def list_checkpoints(self, notebook_id):
318 def list_checkpoints(self, notebook_id):
309 """list the checkpoints for a given notebook
319 """list the checkpoints for a given notebook
310
320
311 This notebook manager currently only supports one checkpoint per notebook.
321 This notebook manager currently only supports one checkpoint per notebook.
312 """
322 """
313 checkpoint_id = u"checkpoint"
323 checkpoint_id = u"checkpoint"
314 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
324 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
315 if not os.path.exists(path):
325 if not os.path.exists(path):
316 return []
326 return []
317 else:
327 else:
318 return [self.get_checkpoint_info(notebook_id, checkpoint_id)]
328 return [self.get_checkpoint_info(notebook_id, checkpoint_id)]
319
329
320
330
321 def restore_checkpoint(self, notebook_id, checkpoint_id):
331 def restore_checkpoint(self, notebook_id, checkpoint_id):
322 """restore a notebook to a checkpointed state"""
332 """restore a notebook to a checkpointed state"""
323 self.log.info("restoring Notebook %s from checkpoint %s", notebook_id, checkpoint_id)
333 self.log.info("restoring Notebook %s from checkpoint %s", notebook_id, checkpoint_id)
324 nb_path = self.get_path(notebook_id)
334 nb_path = self.get_path(notebook_id)
325 cp_path = self.get_checkpoint_path(notebook_id, checkpoint_id)
335 cp_path = self.get_checkpoint_path(notebook_id, checkpoint_id)
326 if not os.path.isfile(cp_path):
336 if not os.path.isfile(cp_path):
327 self.log.debug("checkpoint file does not exist: %s", cp_path)
337 self.log.debug("checkpoint file does not exist: %s", cp_path)
328 raise web.HTTPError(404,
338 raise web.HTTPError(404,
329 u'Notebook checkpoint does not exist: %s-%s' % (notebook_id, checkpoint_id)
339 u'Notebook checkpoint does not exist: %s-%s' % (notebook_id, checkpoint_id)
330 )
340 )
331 # ensure notebook is readable (never restore from an unreadable notebook)
341 # ensure notebook is readable (never restore from an unreadable notebook)
332 last_modified, nb = self.read_notebook_object_from_path(cp_path)
342 last_modified, nb = self.read_notebook_object_from_path(cp_path)
333 shutil.copy2(cp_path, nb_path)
343 shutil.copy2(cp_path, nb_path)
334 self.log.debug("copying %s -> %s", cp_path, nb_path)
344 self.log.debug("copying %s -> %s", cp_path, nb_path)
335
345
336 def delete_checkpoint(self, notebook_id, checkpoint_id):
346 def delete_checkpoint(self, notebook_id, checkpoint_id):
337 """delete a notebook's checkpoint"""
347 """delete a notebook's checkpoint"""
338 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
348 path = self.get_checkpoint_path(notebook_id, checkpoint_id)
339 if not os.path.isfile(path):
349 if not os.path.isfile(path):
340 raise web.HTTPError(404,
350 raise web.HTTPError(404,
341 u'Notebook checkpoint does not exist: %s-%s' % (notebook_id, checkpoint_id)
351 u'Notebook checkpoint does not exist: %s-%s' % (notebook_id, checkpoint_id)
342 )
352 )
343 self.log.debug("unlinking %s", path)
353 self.log.debug("unlinking %s", path)
344 os.unlink(path)
354 os.unlink(path)
345
355
346 def info_string(self):
356 def info_string(self):
347 return "Serving notebooks from local directory: %s" % self.notebook_dir
357 return "Serving notebooks from local directory: %s" % self.notebook_dir
General Comments 0
You need to be logged in to leave comments. Login now