nbmanager.py
174 lines
| 6.2 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r8180 | """A base class notebook manager. | ||
Authors: | ||||
* Brian Granger | ||||
Zachary Sailer
|
r13046 | * Zach Sailer | ||
Brian Granger
|
r8180 | """ | ||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2011 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r8181 | import os | ||
Brian Granger
|
r8180 | |||
from IPython.config.configurable import LoggingConfigurable | ||||
from IPython.nbformat import current | ||||
Thomas Kluyver
|
r13447 | from IPython.utils import py3compat | ||
from IPython.utils.traitlets import Unicode, TraitError | ||||
Brian Granger
|
r8180 | |||
#----------------------------------------------------------------------------- | ||||
# Classes | ||||
#----------------------------------------------------------------------------- | ||||
Brian Granger
|
r8194 | class NotebookManager(LoggingConfigurable): | ||
Brian Granger
|
r8180 | |||
Brian Granger
|
r8181 | # Todo: | ||
# The notebook_dir attribute is used to mean a couple of different things: | ||||
# 1. Where the notebooks are stored if FileNotebookManager is used. | ||||
# 2. The cwd of the kernel for a project. | ||||
# Right now we use this attribute in a number of different places and | ||||
MinRK
|
r10497 | # we are going to have to disentangle all of this. | ||
Thomas Kluyver
|
r13447 | notebook_dir = Unicode(py3compat.getcwd(), config=True, help=""" | ||
Zachary Sailer
|
r12984 | The directory to use for notebooks. | ||
""") | ||||
Paul Ivanov
|
r13026 | |||
Zachary Sailer
|
r13046 | filename_ext = Unicode(u'.ipynb') | ||
MinRK
|
r13067 | |||
def path_exists(self, path): | ||||
"""Does the API-style path (directory) actually exist? | ||||
MinRK
|
r13070 | Override this method in subclasses. | ||
MinRK
|
r13067 | |||
Paul Ivanov
|
r13026 | Parameters | ||
---------- | ||||
MinRK
|
r13067 | path : string | ||
The | ||||
Paul Ivanov
|
r13026 | Returns | ||
------- | ||||
MinRK
|
r13067 | exists : bool | ||
Whether the path does indeed exist. | ||||
Paul Ivanov
|
r13026 | """ | ||
MinRK
|
r13070 | raise NotImplementedError | ||
MinRK
|
r13067 | |||
Zachary Sailer
|
r13046 | def _notebook_dir_changed(self, name, old, new): | ||
"""Do a bit of validation of the notebook dir.""" | ||||
Ohad Ravid
|
r8453 | if not os.path.isabs(new): | ||
# If we receive a non-absolute path, make it absolute. | ||||
MinRK
|
r13066 | self.notebook_dir = os.path.abspath(new) | ||
Ohad Ravid
|
r8453 | return | ||
Brian Granger
|
r8181 | if os.path.exists(new) and not os.path.isdir(new): | ||
raise TraitError("notebook dir %r is not a directory" % new) | ||||
if not os.path.exists(new): | ||||
self.log.info("Creating notebook dir %s", new) | ||||
try: | ||||
os.mkdir(new) | ||||
except: | ||||
raise TraitError("Couldn't create notebook dir %r" % new) | ||||
Brian Granger
|
r8180 | |||
Zachary Sailer
|
r13046 | # Main notebook API | ||
Brian Granger
|
r8180 | |||
MinRK
|
r13067 | def increment_filename(self, basename, path=''): | ||
Zachary Sailer
|
r13046 | """Increment a notebook filename without the .ipynb to make it unique. | ||
Parameters | ||||
---------- | ||||
basename : unicode | ||||
The name of a notebook without the ``.ipynb`` file extension. | ||||
path : unicode | ||||
The URL path of the notebooks directory | ||||
Brian Granger
|
r8180 | """ | ||
Zachary Sailer
|
r13046 | return basename | ||
Brian Granger
|
r8180 | |||
MinRK
|
r13074 | def list_notebooks(self, path=''): | ||
Zachary Sailer
|
r13046 | """Return a list of notebook dicts without content. | ||
Brian Granger
|
r8180 | |||
This returns a list of dicts, each of the form:: | ||||
dict(notebook_id=notebook,name=name) | ||||
This list of dicts should be sorted by name:: | ||||
data = sorted(data, key=lambda item: item['name']) | ||||
""" | ||||
raise NotImplementedError('must be implemented in a subclass') | ||||
MinRK
|
r13067 | def get_notebook_model(self, name, path='', content=True): | ||
Zachary Sailer
|
r13046 | """Get the notebook model with or without content.""" | ||
Brian Granger
|
r8180 | raise NotImplementedError('must be implemented in a subclass') | ||
MinRK
|
r13067 | def save_notebook_model(self, model, name, path=''): | ||
Zachary Sailer
|
r13046 | """Save the notebook model and return the model with no content.""" | ||
raise NotImplementedError('must be implemented in a subclass') | ||||
Brian Granger
|
r8180 | |||
MinRK
|
r13067 | def update_notebook_model(self, model, name, path=''): | ||
Zachary Sailer
|
r13046 | """Update the notebook model and return the model with no content.""" | ||
Brian Granger
|
r8180 | raise NotImplementedError('must be implemented in a subclass') | ||
MinRK
|
r13074 | def delete_notebook_model(self, name, path=''): | ||
Zachary Sailer
|
r13046 | """Delete notebook by name and path.""" | ||
Brian Granger
|
r8180 | raise NotImplementedError('must be implemented in a subclass') | ||
MinRK
|
r13067 | def create_notebook_model(self, model=None, path=''): | ||
MinRK
|
r13245 | """Create a new notebook and return its model with no content.""" | ||
MinRK
|
r13078 | path = path.strip('/') | ||
Zachary Sailer
|
r13046 | if model is None: | ||
model = {} | ||||
MinRK
|
r13074 | if 'content' not in model: | ||
Zachary Sailer
|
r13046 | metadata = current.new_metadata(name=u'') | ||
MinRK
|
r13074 | model['content'] = current.new_notebook(metadata=metadata) | ||
if 'name' not in model: | ||||
model['name'] = self.increment_filename('Untitled', path) | ||||
model['path'] = path | ||||
model = self.save_notebook_model(model, model['name'], model['path']) | ||||
Zachary Sailer
|
r13046 | return model | ||
Brian Granger
|
r8180 | |||
MinRK
|
r13123 | def copy_notebook(self, from_name, to_name=None, path=''): | ||
"""Copy an existing notebook and return its new model. | ||||
If to_name not specified, increment `from_name-Copy#.ipynb`. | ||||
""" | ||||
MinRK
|
r13078 | path = path.strip('/') | ||
MinRK
|
r13123 | model = self.get_notebook_model(from_name, path) | ||
if not to_name: | ||||
base = os.path.splitext(from_name)[0] + '-Copy' | ||||
to_name = self.increment_filename(base, path) | ||||
model['name'] = to_name | ||||
model = self.save_notebook_model(model, to_name, path) | ||||
Zachary Sailer
|
r13046 | return model | ||
MinRK
|
r10497 | |||
# Checkpoint-related | ||||
MinRK
|
r13074 | def create_checkpoint(self, name, path=''): | ||
MinRK
|
r10497 | """Create a checkpoint of the current state of a notebook | ||
Returns a checkpoint_id for the new checkpoint. | ||||
""" | ||||
raise NotImplementedError("must be implemented in a subclass") | ||||
MinRK
|
r13074 | def list_checkpoints(self, name, path=''): | ||
MinRK
|
r10497 | """Return a list of checkpoints for a given notebook""" | ||
return [] | ||||
MinRK
|
r13074 | def restore_checkpoint(self, checkpoint_id, name, path=''): | ||
MinRK
|
r10497 | """Restore a notebook from one of its checkpoints""" | ||
raise NotImplementedError("must be implemented in a subclass") | ||||
Brian Granger
|
r8181 | |||
MinRK
|
r13074 | def delete_checkpoint(self, checkpoint_id, name, path=''): | ||
MinRK
|
r10497 | """delete a checkpoint for a notebook""" | ||
raise NotImplementedError("must be implemented in a subclass") | ||||
Brian Granger
|
r8181 | def log_info(self): | ||
Paul Ivanov
|
r10019 | self.log.info(self.info_string()) | ||
def info_string(self): | ||||
Brian E. Granger
|
r13051 | return "Serving notebooks" | ||