##// END OF EJS Templates
refactoring of nbmanager and filenbmanager...
refactoring of nbmanager and filenbmanager major clean up of the two managers. We make sure to follow the standard models described in IPEP 16

File last commit:

r13046:116db313
r13046:116db313
Show More
nbmanager.py
223 lines | 8.1 KiB | text/x-python | PythonLexer
"""A base class notebook manager.
Authors:
* Brian Granger
* Zach Sailer
"""
#-----------------------------------------------------------------------------
# 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
#-----------------------------------------------------------------------------
import os
import uuid
from urllib import quote, unquote
from tornado import web
from IPython.html.utils import url_path_join
from IPython.config.configurable import LoggingConfigurable
from IPython.nbformat import current
from IPython.utils.traitlets import List, Dict, Unicode, TraitError
#-----------------------------------------------------------------------------
# Classes
#-----------------------------------------------------------------------------
class NotebookManager(LoggingConfigurable):
# 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
# we are going to have to disentangle all of this.
notebook_dir = Unicode(os.getcwdu(), config=True, help="""
The directory to use for notebooks.
""")
filename_ext = Unicode(u'.ipynb')
def named_notebook_path(self, notebook_path):
"""Given notebook_path (*always* a URL path to notebook), returns a
(name, path) tuple, where name is a .ipynb file, and path is the
URL path that describes the file system path for the file.
It *always* starts *and* ends with a '/' character.
Parameters
----------
notebook_path : string
A path that may be a .ipynb name or a directory
Returns
-------
name : string or None
the filename of the notebook, or None if not a .ipynb extension
path : string
the path to the directory which contains the notebook
"""
names = notebook_path.split('/')
names = [n for n in names if n != ''] # remove duplicate splits
names = [''] + names
if names and names[-1].endswith(".ipynb"):
name = names[-1]
path = "/".join(names[:-1]) + '/'
else:
name = None
path = "/".join(names) + '/'
return name, path
def get_os_path(self, fname=None, path='/'):
"""Given a notebook name and a URL path, return its file system
path.
Parameters
----------
fname : string
The name of a notebook file with the .ipynb extension
path : string
The relative URL path (with '/' as separator) to the named
notebook.
Returns
-------
path : string
A file system path that combines notebook_dir (location where
server started), the relative path, and the filename with the
current operating system's url.
"""
parts = path.split('/')
parts = [p for p in parts if p != ''] # remove duplicate splits
if fname is not None:
parts += [fname]
path = os.path.join(self.notebook_dir, *parts)
return path
def url_encode(self, path):
"""Takes a URL path with special characters and returns
the path with all these characters URL encoded"""
parts = path.split('/')
return '/'.join([quote(p) for p in parts])
def url_decode(self, path):
"""Takes a URL path with encoded special characters and
returns the URL with special characters decoded"""
parts = path.split('/')
return '/'.join([unquote(p) for p in parts])
def _notebook_dir_changed(self, name, old, new):
"""Do a bit of validation of the notebook dir."""
if not os.path.isabs(new):
# If we receive a non-absolute path, make it absolute.
abs_new = os.path.abspath(new)
self.notebook_dir = os.path.dirname(abs_new)
return
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)
# Main notebook API
def increment_filename(self, basename, path='/'):
"""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
"""
return basename
def list_notebooks(self):
"""Return a list of notebook dicts without content.
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')
def get_notebook_model(self, name, path='/', content=True):
"""Get the notebook model with or without content."""
raise NotImplementedError('must be implemented in a subclass')
def save_notebook_model(self, model, name, path='/'):
"""Save the notebook model and return the model with no content."""
raise NotImplementedError('must be implemented in a subclass')
def update_notebook_model(self, model, name, path='/'):
"""Update the notebook model and return the model with no content."""
raise NotImplementedError('must be implemented in a subclass')
def delete_notebook_model(self, name, path):
"""Delete notebook by name and path."""
raise NotImplementedError('must be implemented in a subclass')
def create_notebook_model(self, model=None, path='/'):
"""Create a new untitled notebook and return its model with no content."""
name = self.increment_filename('Untitled', path)
if model is None:
model = {}
metadata = current.new_metadata(name=u'')
nb = current.new_notebook(metadata=metadata)
model['content'] = nb
model['name'] = name
model['path'] = path
model = self.save_notebook_model(model, name, path)
return model
def copy_notebook(self, name, path='/', content=False):
"""Copy an existing notebook and return its new model."""
model = self.get_notebook_model(name, path)
name = os.path.splitext(name)[0] + '-Copy'
name = self.increment_filename(name, path) + self.filename_ext
model['name'] = name
model = self.save_notebook_model(model, name, path, content=content)
return model
# Checkpoint-related
def create_checkpoint(self, name, path='/'):
"""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")
def list_checkpoints(self, name, path='/'):
"""Return a list of checkpoints for a given notebook"""
return []
def restore_checkpoint(self, checkpoint_id, name, path='/'):
"""Restore a notebook from one of its checkpoints"""
raise NotImplementedError("must be implemented in a subclass")
def delete_checkpoint(self, checkpoint_id, name, path='/'):
"""delete a checkpoint for a notebook"""
raise NotImplementedError("must be implemented in a subclass")
def log_info(self):
self.log.info(self.info_string())
def info_string(self):
return "Serving notebooks"