##// END OF EJS Templates
DEV: Allow CheckpointManagers to optimize for shared backends....
Scott Sanderson -
Show More
@@ -222,59 +222,48 b' class FileCheckpointManager(FileManagerMixin, CheckpointManager):'
222 222 except AttributeError:
223 223 return getcwd()
224 224
225 # public checkpoint API
226 def create_file_checkpoint(self, content, format, path):
227 """Create a checkpoint from the current content of a notebook."""
228 path = path.strip('/')
229 # only the one checkpoint ID:
230 checkpoint_id = u"checkpoint"
231 os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
232 self.log.debug("creating checkpoint for %s", path)
233 with self.perm_to_403():
234 self._save_file(os_checkpoint_path, content, format=format)
235
236 # return the checkpoint info
237 return self.checkpoint_model(checkpoint_id, os_checkpoint_path)
238
239 def create_notebook_checkpoint(self, nb, path):
240 """Create a checkpoint from the current content of a notebook."""
241 path = path.strip('/')
242 # only the one checkpoint ID:
243 checkpoint_id = u"checkpoint"
244 os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
245 self.log.debug("creating checkpoint for %s", path)
246 with self.perm_to_403():
247 self._save_notebook(os_checkpoint_path, nb)
225 # ContentsManager-dependent checkpoint API
226 def create_checkpoint(self, contents_mgr, path):
227 """
228 Create a checkpoint.
248 229
249 # return the checkpoint info
250 return self.checkpoint_model(checkpoint_id, os_checkpoint_path)
230 If contents_mgr is backed by the local filesystem, just copy the
231 appropriate file to the checkpoint directory. Otherwise, ask the
232 ContentsManager for a model and write it ourselves.
233 """
234 if contents_mgr.backend == 'local_file':
235 # We know that the file is in the local filesystem, so just copy
236 # from the base location to our location.
237 checkpoint_id = u'checkpoint'
238 src_path = contents_mgr._get_os_path(path)
239 dest_path = self.checkpoint_path(checkpoint_id, path)
240 self._copy(src_path, dest_path)
241 return self.checkpoint_model(checkpoint_id, dest_path)
242 else:
243 return super(FileCheckpointManager, self).create_checkpoint(
244 contents_mgr, path,
245 )
251 246
252 def get_checkpoint(self, checkpoint_id, path, type):
253 """Get the content of a checkpoint.
247 def restore_checkpoint(self, contents_mgr, checkpoint_id, path):
248 """
249 Restore a checkpoint.
254 250
255 Returns a model suitable for passing to ContentsManager.save.
251 If contents_mgr is backed by the local filesystem, just copy the
252 appropriate file from the checkpoint directory. Otherwise, load the
253 model and pass it to ContentsManager.save.
256 254 """
257 path = path.strip('/')
258 self.log.info("restoring %s from checkpoint %s", path, checkpoint_id)
259 os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
260 if not os.path.isfile(os_checkpoint_path):
261 self.no_such_checkpoint(path, checkpoint_id)
262 if type == 'notebook':
263 return {
264 'type': type,
265 'content': self._read_notebook(
266 os_checkpoint_path,
267 as_version=4,
268 ),
269 }
255 if contents_mgr.backend == 'local_file':
256 # We know that the file is in the local filesystem, so just copy
257 # from our base location to the location expected by content
258 src_path = self.checkpoint_path(checkpoint_id, path)
259 dest_path = contents_mgr._get_os_path(path)
260 self._copy(src_path, dest_path)
270 261 else:
271 content, format = self._read_file(os_checkpoint_path, format=None)
272 return {
273 'type': type,
274 'content': content,
275 'format': format,
276 }
262 super(FileCheckpointManager, self).restore_checkpoint(
263 contents_mgr, checkpoint_id, path
264 )
277 265
266 # ContentsManager-independent checkpoint API
278 267 def rename_checkpoint(self, checkpoint_id, old_path, new_path):
279 268 """Rename a checkpoint from old_path to new_path."""
280 269 old_cp_path = self.checkpoint_path(checkpoint_id, old_path)
@@ -341,6 +330,64 b' class FileCheckpointManager(FileManagerMixin, CheckpointManager):'
341 330 )
342 331 return info
343 332
333 def create_file_checkpoint(self, content, format, path):
334 """Create a checkpoint from the current content of a notebook."""
335 path = path.strip('/')
336 # only the one checkpoint ID:
337 checkpoint_id = u"checkpoint"
338 os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
339 self.log.debug("creating checkpoint for %s", path)
340 with self.perm_to_403():
341 self._save_file(os_checkpoint_path, content, format=format)
342
343 # return the checkpoint info
344 return self.checkpoint_model(checkpoint_id, os_checkpoint_path)
345
346 def create_notebook_checkpoint(self, nb, path):
347 """Create a checkpoint from the current content of a notebook."""
348 path = path.strip('/')
349 # only the one checkpoint ID:
350 checkpoint_id = u"checkpoint"
351 os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
352 self.log.debug("creating checkpoint for %s", path)
353 with self.perm_to_403():
354 self._save_notebook(os_checkpoint_path, nb)
355
356 # return the checkpoint info
357 return self.checkpoint_model(checkpoint_id, os_checkpoint_path)
358
359 def get_checkpoint(self, checkpoint_id, path, type):
360 """Get the content of a checkpoint.
361
362 Returns a model suitable for passing to ContentsManager.save.
363 """
364 path = path.strip('/')
365 self.log.info("restoring %s from checkpoint %s", path, checkpoint_id)
366 os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
367 if not os.path.isfile(os_checkpoint_path):
368 self.no_such_checkpoint(path, checkpoint_id)
369
370 if type == 'notebook':
371 return {
372 'type': type,
373 'content': self._read_notebook(
374 os_checkpoint_path,
375 as_version=4,
376 ),
377 }
378 elif type == 'file':
379 content, format = self._read_file(os_checkpoint_path, format=None)
380 return {
381 'type': type,
382 'content': content,
383 'format': format,
384 }
385 else:
386 raise web.HTTPError(
387 500,
388 u'Unexpected type %s' % type
389 )
390
344 391 # Error Handling
345 392 def no_such_checkpoint(self, path, checkpoint_id):
346 393 raise web.HTTPError(
@@ -421,6 +468,9 b' class FileContentsManager(FileManagerMixin, ContentsManager):'
421 468 def _checkpoint_manager_class_default(self):
422 469 return FileCheckpointManager
423 470
471 def _backend_default(self):
472 return 'local_file'
473
424 474 def is_hidden(self, path):
425 475 """Does the API style path correspond to a hidden directory or file?
426 476
@@ -681,10 +731,7 b' class FileContentsManager(FileManagerMixin, ContentsManager):'
681 731 self._save_notebook(os_path, nb)
682 732 # One checkpoint should always exist for notebooks.
683 733 if not self.checkpoint_manager.list_checkpoints(path):
684 self.checkpoint_manager.create_notebook_checkpoint(
685 nb,
686 path,
687 )
734 self.create_checkpoint(path)
688 735 elif model['type'] == 'file':
689 736 # Missing format will be handled internally by _save_file.
690 737 self._save_file(os_path, model['content'], model.get('format'))
@@ -11,7 +11,6 b' import re'
11 11
12 12 from tornado.web import HTTPError
13 13
14 from IPython import nbformat
15 14 from IPython.config.configurable import LoggingConfigurable
16 15 from IPython.nbformat import sign, validate, ValidationError
17 16 from IPython.nbformat.v4 import new_notebook
@@ -34,6 +33,28 b' class CheckpointManager(LoggingConfigurable):'
34 33 """
35 34 Base class for managing checkpoints for a ContentsManager.
36 35 """
36
37 def create_checkpoint(self, contents_mgr, path):
38 model = contents_mgr.get(path, content=True)
39 type = model['type']
40 if type == 'notebook':
41 return self.create_notebook_checkpoint(
42 model['content'],
43 path,
44 )
45 elif type == 'file':
46 return self.create_file_checkpoint(
47 model['content'],
48 model['format'],
49 path,
50 )
51
52 def restore_checkpoint(self, contents_mgr, checkpoint_id, path):
53 """Restore a checkpoint."""
54 type = contents_mgr.get(path, content=False)['type']
55 model = self.get_checkpoint(checkpoint_id, path, type)
56 contents_mgr.save(model, path)
57
37 58 def create_file_checkpoint(self, content, format, path):
38 59 """Create a checkpoint of the current state of a file
39 60
@@ -159,6 +180,7 b' class ContentsManager(LoggingConfigurable):'
159 180 checkpoint_manager_class = Type(CheckpointManager, config=True)
160 181 checkpoint_manager = Instance(CheckpointManager, config=True)
161 182 checkpoint_manager_kwargs = Dict(allow_none=False, config=True)
183 backend = Unicode(default_value="")
162 184
163 185 def _checkpoint_manager_default(self):
164 186 return self.checkpoint_manager_class(**self.checkpoint_manager_kwargs)
@@ -502,35 +524,16 b' class ContentsManager(LoggingConfigurable):'
502 524 # Part 3: Checkpoints API
503 525 def create_checkpoint(self, path):
504 526 """Create a checkpoint."""
505 model = self.get(path, content=True)
506 type = model['type']
507 if type == 'notebook':
508 return self.checkpoint_manager.create_notebook_checkpoint(
509 model['content'],
510 path,
511 )
512 elif type == 'file':
513 return self.checkpoint_manager.create_file_checkpoint(
514 model['content'],
515 model['format'],
516 path,
517 )
518
519 def list_checkpoints(self, path):
520 return self.checkpoint_manager.list_checkpoints(path)
527 return self.checkpoint_manager.create_checkpoint(self, path)
521 528
522 529 def restore_checkpoint(self, checkpoint_id, path):
523 530 """
524 531 Restore a checkpoint.
525 532 """
526 return self.save(
527 model=self.checkpoint_manager.get_checkpoint(
528 checkpoint_id,
529 path,
530 self.get(path, content=False)['type']
531 ),
532 path=path,
533 )
533 self.checkpoint_manager.restore_checkpoint(self, checkpoint_id, path)
534
535 def list_checkpoints(self, path):
536 return self.checkpoint_manager.list_checkpoints(path)
534 537
535 538 def delete_checkpoint(self, checkpoint_id, path):
536 539 return self.checkpoint_manager.delete_checkpoint(checkpoint_id, path)
@@ -614,3 +614,25 b' class APITest(NotebookTestBase):'
614 614 with TemporaryDirectory() as td:
615 615 with self.patch_cp_root(td):
616 616 self.test_file_checkpoints()
617
618 @contextmanager
619 def patch_cm_backend(self):
620 """
621 Temporarily patch our ContentsManager to present a different backend.
622 """
623 mgr = self.notebook.contents_manager
624 old_backend = mgr.backend
625 mgr.backend = ""
626 try:
627 yield
628 finally:
629 mgr.backend = old_backend
630
631 def test_checkpoints_empty_backend(self):
632 with self.patch_cm_backend():
633 self.test_checkpoints()
634
635 with self.patch_cm_backend():
636 self.test_file_checkpoints()
637
638
General Comments 0
You need to be logged in to leave comments. Login now