##// 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 except AttributeError:
222 except AttributeError:
223 return getcwd()
223 return getcwd()
224
224
225 # public checkpoint API
225 # ContentsManager-dependent checkpoint API
226 def create_file_checkpoint(self, content, format, path):
226 def create_checkpoint(self, contents_mgr, path):
227 """Create a checkpoint from the current content of a notebook."""
227 """
228 path = path.strip('/')
228 Create a checkpoint.
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)
248
229
249 # return the checkpoint info
230 If contents_mgr is backed by the local filesystem, just copy the
250 return self.checkpoint_model(checkpoint_id, os_checkpoint_path)
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):
247 def restore_checkpoint(self, contents_mgr, checkpoint_id, path):
253 """Get the content of a checkpoint.
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('/')
255 if contents_mgr.backend == 'local_file':
258 self.log.info("restoring %s from checkpoint %s", path, checkpoint_id)
256 # We know that the file is in the local filesystem, so just copy
259 os_checkpoint_path = self.checkpoint_path(checkpoint_id, path)
257 # from our base location to the location expected by content
260 if not os.path.isfile(os_checkpoint_path):
258 src_path = self.checkpoint_path(checkpoint_id, path)
261 self.no_such_checkpoint(path, checkpoint_id)
259 dest_path = contents_mgr._get_os_path(path)
262 if type == 'notebook':
260 self._copy(src_path, dest_path)
263 return {
264 'type': type,
265 'content': self._read_notebook(
266 os_checkpoint_path,
267 as_version=4,
268 ),
269 }
270 else:
261 else:
271 content, format = self._read_file(os_checkpoint_path, format=None)
262 super(FileCheckpointManager, self).restore_checkpoint(
272 return {
263 contents_mgr, checkpoint_id, path
273 'type': type,
264 )
274 'content': content,
275 'format': format,
276 }
277
265
266 # ContentsManager-independent checkpoint API
278 def rename_checkpoint(self, checkpoint_id, old_path, new_path):
267 def rename_checkpoint(self, checkpoint_id, old_path, new_path):
279 """Rename a checkpoint from old_path to new_path."""
268 """Rename a checkpoint from old_path to new_path."""
280 old_cp_path = self.checkpoint_path(checkpoint_id, old_path)
269 old_cp_path = self.checkpoint_path(checkpoint_id, old_path)
@@ -341,6 +330,64 b' class FileCheckpointManager(FileManagerMixin, CheckpointManager):'
341 )
330 )
342 return info
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 # Error Handling
391 # Error Handling
345 def no_such_checkpoint(self, path, checkpoint_id):
392 def no_such_checkpoint(self, path, checkpoint_id):
346 raise web.HTTPError(
393 raise web.HTTPError(
@@ -421,6 +468,9 b' class FileContentsManager(FileManagerMixin, ContentsManager):'
421 def _checkpoint_manager_class_default(self):
468 def _checkpoint_manager_class_default(self):
422 return FileCheckpointManager
469 return FileCheckpointManager
423
470
471 def _backend_default(self):
472 return 'local_file'
473
424 def is_hidden(self, path):
474 def is_hidden(self, path):
425 """Does the API style path correspond to a hidden directory or file?
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 self._save_notebook(os_path, nb)
731 self._save_notebook(os_path, nb)
682 # One checkpoint should always exist for notebooks.
732 # One checkpoint should always exist for notebooks.
683 if not self.checkpoint_manager.list_checkpoints(path):
733 if not self.checkpoint_manager.list_checkpoints(path):
684 self.checkpoint_manager.create_notebook_checkpoint(
734 self.create_checkpoint(path)
685 nb,
686 path,
687 )
688 elif model['type'] == 'file':
735 elif model['type'] == 'file':
689 # Missing format will be handled internally by _save_file.
736 # Missing format will be handled internally by _save_file.
690 self._save_file(os_path, model['content'], model.get('format'))
737 self._save_file(os_path, model['content'], model.get('format'))
@@ -11,7 +11,6 b' import re'
11
11
12 from tornado.web import HTTPError
12 from tornado.web import HTTPError
13
13
14 from IPython import nbformat
15 from IPython.config.configurable import LoggingConfigurable
14 from IPython.config.configurable import LoggingConfigurable
16 from IPython.nbformat import sign, validate, ValidationError
15 from IPython.nbformat import sign, validate, ValidationError
17 from IPython.nbformat.v4 import new_notebook
16 from IPython.nbformat.v4 import new_notebook
@@ -34,6 +33,28 b' class CheckpointManager(LoggingConfigurable):'
34 """
33 """
35 Base class for managing checkpoints for a ContentsManager.
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 def create_file_checkpoint(self, content, format, path):
58 def create_file_checkpoint(self, content, format, path):
38 """Create a checkpoint of the current state of a file
59 """Create a checkpoint of the current state of a file
39
60
@@ -159,6 +180,7 b' class ContentsManager(LoggingConfigurable):'
159 checkpoint_manager_class = Type(CheckpointManager, config=True)
180 checkpoint_manager_class = Type(CheckpointManager, config=True)
160 checkpoint_manager = Instance(CheckpointManager, config=True)
181 checkpoint_manager = Instance(CheckpointManager, config=True)
161 checkpoint_manager_kwargs = Dict(allow_none=False, config=True)
182 checkpoint_manager_kwargs = Dict(allow_none=False, config=True)
183 backend = Unicode(default_value="")
162
184
163 def _checkpoint_manager_default(self):
185 def _checkpoint_manager_default(self):
164 return self.checkpoint_manager_class(**self.checkpoint_manager_kwargs)
186 return self.checkpoint_manager_class(**self.checkpoint_manager_kwargs)
@@ -502,35 +524,16 b' class ContentsManager(LoggingConfigurable):'
502 # Part 3: Checkpoints API
524 # Part 3: Checkpoints API
503 def create_checkpoint(self, path):
525 def create_checkpoint(self, path):
504 """Create a checkpoint."""
526 """Create a checkpoint."""
505 model = self.get(path, content=True)
527 return self.checkpoint_manager.create_checkpoint(self, path)
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)
521
528
522 def restore_checkpoint(self, checkpoint_id, path):
529 def restore_checkpoint(self, checkpoint_id, path):
523 """
530 """
524 Restore a checkpoint.
531 Restore a checkpoint.
525 """
532 """
526 return self.save(
533 self.checkpoint_manager.restore_checkpoint(self, checkpoint_id, path)
527 model=self.checkpoint_manager.get_checkpoint(
534
528 checkpoint_id,
535 def list_checkpoints(self, path):
529 path,
536 return self.checkpoint_manager.list_checkpoints(path)
530 self.get(path, content=False)['type']
531 ),
532 path=path,
533 )
534
537
535 def delete_checkpoint(self, checkpoint_id, path):
538 def delete_checkpoint(self, checkpoint_id, path):
536 return self.checkpoint_manager.delete_checkpoint(checkpoint_id, path)
539 return self.checkpoint_manager.delete_checkpoint(checkpoint_id, path)
@@ -614,3 +614,25 b' class APITest(NotebookTestBase):'
614 with TemporaryDirectory() as td:
614 with TemporaryDirectory() as td:
615 with self.patch_cp_root(td):
615 with self.patch_cp_root(td):
616 self.test_file_checkpoints()
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