##// END OF EJS Templates
fix `--notebook-dir` configurable when there is no trailing slash
fix `--notebook-dir` configurable when there is no trailing slash

File last commit:

r13059:ee964465
r13066:99675b00
Show More
filenbmanager.py
355 lines | 13.4 KiB | text/x-python | PythonLexer
Brian E. Granger
More review changes....
r4609 """A notebook manager that uses the local file system for storage.
Authors:
* Brian Granger
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 * Zach Sailer
Brian E. Granger
More review changes....
r4609 """
Brian E. Granger
Massive work on the notebook document format....
r4484 #-----------------------------------------------------------------------------
Brian Granger
Refactoring notebook managers and adding Azure backed storage....
r8180 # Copyright (C) 2011 The IPython Development Team
Brian E. Granger
Massive work on the notebook document format....
r4484 #
# Distributed under the terms of the BSD License. The full license is in
Brian E. Granger
More review changes....
r4609 # the file COPYING, distributed as part of this software.
Brian E. Granger
Massive work on the notebook document format....
r4484 #-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Imports
#-----------------------------------------------------------------------------
import datetime
Thomas Kluyver
Save notebook as script using unicode file handle....
r6030 import io
Brian E. Granger
Massive work on the notebook document format....
r4484 import os
Stefan van der Walt
Move glob to global level import.
r4624 import glob
MinRK
add checkpoint API to FileNBManager
r10497 import shutil
Zachary Sailer
manual rebase notebooks web services
r12984
MinRK
normalize unicode notebook filenames...
r10777 from unicodedata import normalize
Brian E. Granger
Massive work on the notebook document format....
r4484
from tornado import web
Brian Granger
Renaming BaseNotebookManager->NotebookManager to preserve config.
r8194 from .nbmanager import NotebookManager
Brian E. Granger
Massive work on the notebook document format....
r4484 from IPython.nbformat import current
Brian Granger
Refactoring notebook managers and adding Azure backed storage....
r8180 from IPython.utils.traitlets import Unicode, Dict, Bool, TraitError
MinRK
add timezone info to `last_modified` in notebook managers...
r11145 from IPython.utils import tz
Brian E. Granger
Massive work on the notebook document format....
r4484
#-----------------------------------------------------------------------------
Fernando Perez
Add --script flag as shorthand for the script autosave notebook option.
r5758 # Classes
#-----------------------------------------------------------------------------
Brian E. Granger
Massive work on the notebook document format....
r4484
Brian Granger
Renaming BaseNotebookManager->NotebookManager to preserve config.
r8194 class FileNotebookManager(NotebookManager):
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653
save_script = Bool(False, config=True,
Fernando Perez
Fix typo in help string
r5760 help="""Automatically create a Python script when saving the notebook.
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653
Matthias BUSSONNIER
remove references to loadpy...
r6765 For easier use of import, %run and %load across notebooks, a
Fernando Perez
Add --script flag as shorthand for the script autosave notebook option.
r5758 <notebook-name>.py script will be created next to any
<notebook-name>.ipynb on each save. This can also be set with the
short `--script` flag.
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653 """
)
MinRK
add checkpoint API to FileNBManager
r10497 checkpoint_dir = Unicode(config=True,
help="""The location in which to keep notebook checkpoints
By default, it is notebook-dir/.ipynb_checkpoints
"""
)
def _checkpoint_dir_default(self):
return os.path.join(self.notebook_dir, '.ipynb_checkpoints')
def _checkpoint_dir_changed(self, name, old, new):
"""do a bit of validation of the checkpoint dir"""
if not os.path.isabs(new):
# If we receive a non-absolute path, make it absolute.
abs_new = os.path.abspath(new)
self.checkpoint_dir = abs_new
return
if os.path.exists(new) and not os.path.isdir(new):
raise TraitError("checkpoint dir %r is not a directory" % new)
if not os.path.exists(new):
self.log.info("Creating checkpoint dir %s", new)
try:
os.mkdir(new)
except:
raise TraitError("Couldn't create checkpoint dir %r" % new)
Brian E. Granger
Massive work on the notebook document format....
r4484
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 def get_notebook_names(self, path='/'):
"""List all notebook names in the notebook dir and path."""
Zachary Sailer
fixing broken links from recent changes....
r13033 names = glob.glob(self.get_os_path('*'+self.filename_ext, path))
Zachary Sailer
manual rebase notebooks web services
r12984 names = [os.path.basename(name)
Stefan van der Walt
Allow period characters in notebook names.
r4623 for name in names]
Brian Granger
Refactoring notebook managers and adding Azure backed storage....
r8180 return names
Zachary Sailer
Add 'patch' to session & notebook, rename working
r12997
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def increment_filename(self, basename, path='/'):
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 """Return a non-used filename of the form basename<int>."""
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 i = 0
while True:
name = u'%s%i.ipynb' % (basename,i)
os_path = self.get_os_path(name, path)
if not os.path.isfile(os_path):
break
else:
i = i+1
return name
Brian E. Granger
Massive work on the notebook document format....
r4484
Zachary Sailer
add tests to notebooks api...
r13059 def os_path_exists(self, path):
"""Check that the given file system path is valid on this machine."""
if os.path.exists(path) is False:
raise web.HTTPError(404, "No file or directory found.")
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 def notebook_exists(self, name, path='/'):
Zachary Sailer
handle path separators with os.sep and add tests...
r13032 """Returns a True if the notebook exists. Else, returns False.
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046
Zachary Sailer
handle path separators with os.sep and add tests...
r13032 Parameters
----------
name : string
The name of the notebook you are checking.
path : string
The relative path to the notebook (with '/' as separator)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046
Zachary Sailer
handle path separators with os.sep and add tests...
r13032 Returns
-------
bool
"""
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 path = self.get_os_path(name, path='/')
Zachary Sailer
handle path separators with os.sep and add tests...
r13032 return os.path.isfile(path)
Zachary Sailer
manual rebase notebooks web services
r12984
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def list_notebooks(self, path):
Zachary Sailer
Code review changes....
r13048 """Returns a list of dictionaries that are the standard model
for all notebooks in the relative 'path'.
Parameters
----------
path : str
the URL path that describes the relative path for the
listed notebooks
Returns
-------
notebooks : list of dicts
a list of the notebook models without 'content'
"""
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 notebook_names = self.get_notebook_names(path)
notebooks = []
for name in notebook_names:
model = self.get_notebook_model(name, path, content=False)
notebooks.append(model)
notebooks = sorted(notebooks, key=lambda item: item['name'])
return notebooks
def get_notebook_model(self, name, path='/', content=True):
Zachary Sailer
Code review changes....
r13048 """ Takes a path and name for a notebook and returns it's model
Parameters
----------
name : str
the name of the notebook
path : str
the URL path that describes the relative path for
the notebook
Returns
-------
model : dict
the notebook model. If contents=True, returns the 'contents'
dict in the model as well.
"""
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 os_path = self.get_os_path(name, path)
if not os.path.isfile(os_path):
raise web.HTTPError(404, u'Notebook does not exist: %s' % name)
info = os.stat(os_path)
MinRK
add timezone info to `last_modified` in notebook managers...
r11145 last_modified = tz.utcfromtimestamp(info.st_mtime)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 # Create the notebook model.
model ={}
model['name'] = name
model['path'] = path
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 model['last_modified'] = last_modified
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 if content is True:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 with open(os_path, 'r') as f:
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 try:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 nb = current.read(f, u'json')
except Exception as e:
raise web.HTTPError(400, u"Unreadable Notebook: %s %s" % (os_path, e))
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 model['content'] = nb
return model
Zachary Sailer
Add 'patch' to session & notebook, rename working
r12997
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def save_notebook_model(self, model, name, path='/'):
"""Save the notebook model and return the model with no content."""
if 'content' not in model:
raise web.HTTPError(400, u'No notebook JSON data provided')
new_path = model.get('path', path)
new_name = model.get('name', name)
if path != new_path or name != new_name:
self.rename_notebook(name, path, new_name, new_path)
Brian E. Granger
When a notebook is written to file, name the metadata name u''.
r11052
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 # Save the notebook file
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 os_path = self.get_os_path(new_name, new_path)
nb = current.to_notebook_json(model['content'])
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 if 'name' in nb['metadata']:
nb['metadata']['name'] = u''
Brian E. Granger
Massive work on the notebook document format....
r4484 try:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 self.log.debug("Autosaving notebook %s", os_path)
with open(os_path, 'w') as f:
Brian E. Granger
Making JSON the default .ipynb format.
r4633 current.write(nb, f, u'json')
MinRK
include error in 'Unexpected error' message.
r5709 except Exception as e:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s %s' % (os_path, e))
Brian Granger
Refactoring notebook managers and adding Azure backed storage....
r8180
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 # Save .py script as well
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653 if self.save_script:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 py_path = os.path.splitext(os_path)[0] + '.py'
self.log.debug("Writing script %s", py_path)
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653 try:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 with io.open(py_path, 'w', encoding='utf-8') as f:
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 current.write(model, f, u'py')
MinRK
include error in 'Unexpected error' message.
r5709 except Exception as e:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s %s' % (py_path, e))
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046
model = self.get_notebook_model(name, path, content=False)
return model
def update_notebook_model(self, model, name, path='/'):
"""Update the notebook's path and/or name"""
new_name = model.get('name', name)
new_path = model.get('path', path)
if path != new_path or name != new_name:
self.rename_notebook(name, path, new_name, new_path)
model = self.get_notebook_model(new_name, new_path, content=False)
return model
def delete_notebook_model(self, name, path='/'):
"""Delete notebook by name and path."""
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 os_path = self.get_os_path(name, path)
if not os.path.isfile(os_path):
raise web.HTTPError(404, u'Notebook does not exist: %s' % os_path)
MinRK
add checkpoint API to FileNBManager
r10497
# clear checkpoints
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 for checkpoint in self.list_checkpoints(name, path):
MinRK
deleting a notebook deletes its checkpoints...
r10518 checkpoint_id = checkpoint['checkpoint_id']
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
if os.path.isfile(cp_path):
self.log.debug("Unlinking checkpoint %s", cp_path)
os.unlink(cp_path)
MinRK
deleting a notebook deletes its checkpoints...
r10518
Zachary Sailer
add error catching to kernel manager...
r13052 self.log.debug("Unlinking notebook %s", os_path)
os.unlink(os_path)
Brian E. Granger
Massive work on the notebook document format....
r4484
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def rename_notebook(self, old_name, old_path, new_name, new_path):
"""Rename a notebook."""
if new_name == old_name and new_path == old_path:
return
Brian Granger
Fixing docstring in the notebook manager.
r5877
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 new_os_path = self.get_os_path(new_name, new_path)
old_os_path = self.get_os_path(old_name, old_path)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046
# Should we proceed with the move?
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 if os.path.isfile(new_os_path):
Zachary Sailer
fixed notebook rename after nbmanager refactor
r13056 raise web.HTTPError(409, u'Notebook with name already exists: %s' % new_os_path)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 if self.save_script:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 old_py_path = os.path.splitext(old_os_path)[0] + '.py'
new_py_path = os.path.splitext(new_os_path)[0] + '.py'
if os.path.isfile(new_py_path):
raise web.HTTPError(409, u'Python script with name already exists: %s' % new_py_path)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046
# Move the notebook file
try:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 os.rename(old_os_path, new_os_path)
except Exception as e:
raise web.HTTPError(400, u'Unknown error renaming notebook: %s %s' % (old_os_path, e))
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046
# Move the checkpoints
old_checkpoints = self.list_checkpoints(old_name, old_path)
for cp in old_checkpoints:
checkpoint_id = cp['checkpoint_id']
Zachary Sailer
fixed notebook rename after nbmanager refactor
r13056 old_cp_path = self.get_checkpoint_path(checkpoint_id, old_name, old_path)
new_cp_path = self.get_checkpoint_path(checkpoint_id, new_name, new_path)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 if os.path.isfile(old_cp_path):
self.log.debug("Renaming checkpoint %s -> %s", old_cp_path, new_cp_path)
os.rename(old_cp_path, new_cp_path)
# Move the .py script
if self.save_script:
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 os.rename(old_py_path, new_py_path)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046
MinRK
add checkpoint API to FileNBManager
r10497 # Checkpoint-related utilities
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def get_checkpoint_path(self, checkpoint_id, name, path='/'):
"""find the path to a checkpoint"""
MinRK
normalize unicode notebook filenames...
r10777 filename = u"{name}-{checkpoint_id}{ext}".format(
MinRK
add checkpoint API to FileNBManager
r10497 name=name,
checkpoint_id=checkpoint_id,
ext=self.filename_ext,
)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 cp_path = os.path.join(path, self.checkpoint_dir, filename)
return cp_path
def get_checkpoint_model(self, checkpoint_id, name, path='/'):
MinRK
checkpoint info is a dict...
r10500 """construct the info dict for a given checkpoint"""
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
stats = os.stat(cp_path)
MinRK
add timezone info to `last_modified` in notebook managers...
r11145 last_modified = tz.utcfromtimestamp(stats.st_mtime)
MinRK
checkpoint info is a dict...
r10500 info = dict(
checkpoint_id = checkpoint_id,
last_modified = last_modified,
)
return info
MinRK
add checkpoint API to FileNBManager
r10497 # public checkpoint API
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def create_checkpoint(self, name, path='/'):
MinRK
add checkpoint API to FileNBManager
r10497 """Create a checkpoint from the current state of a notebook"""
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 nb_path = self.get_os_path(name, path)
MinRK
checkpoint info is a dict...
r10500 # only the one checkpoint ID:
MinRK
normalize unicode notebook filenames...
r10777 checkpoint_id = u"checkpoint"
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
self.log.debug("creating checkpoint for notebook %s", name)
MinRK
add checkpoint API to FileNBManager
r10497 if not os.path.exists(self.checkpoint_dir):
os.mkdir(self.checkpoint_dir)
shutil.copy2(nb_path, cp_path)
MinRK
checkpoint info is a dict...
r10500
# return the checkpoint info
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 return self.get_checkpoint_model(checkpoint_id, name, path)
MinRK
add checkpoint API to FileNBManager
r10497
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def list_checkpoints(self, name, path='/'):
MinRK
add checkpoint API to FileNBManager
r10497 """list the checkpoints for a given notebook
Paul Ivanov
print info string on interrupt, log it on startup
r10019
MinRK
add checkpoint API to FileNBManager
r10497 This notebook manager currently only supports one checkpoint per notebook.
"""
Zachary Sailer
manual rebase notebooks web services
r12984 checkpoint_id = "checkpoint"
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 path = self.get_checkpoint_path(checkpoint_id, name, path)
MinRK
checkpoint info is a dict...
r10500 if not os.path.exists(path):
MinRK
add checkpoint API to FileNBManager
r10497 return []
MinRK
checkpoint info is a dict...
r10500 else:
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 return [self.get_checkpoint_model(checkpoint_id, name, path)]
MinRK
checkpoint info is a dict...
r10500
MinRK
add checkpoint API to FileNBManager
r10497
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def restore_checkpoint(self, checkpoint_id, name, path='/'):
MinRK
add checkpoint API to FileNBManager
r10497 """restore a notebook to a checkpointed state"""
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 self.log.info("restoring Notebook %s from checkpoint %s", name, checkpoint_id)
nb_path = self.get_os_path(name, path)
cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
MinRK
add checkpoint API to FileNBManager
r10497 if not os.path.isfile(cp_path):
MinRK
checkpoint info is a dict...
r10500 self.log.debug("checkpoint file does not exist: %s", cp_path)
MinRK
add checkpoint API to FileNBManager
r10497 raise web.HTTPError(404,
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 u'Notebook checkpoint does not exist: %s-%s' % (name, checkpoint_id)
MinRK
add checkpoint API to FileNBManager
r10497 )
# ensure notebook is readable (never restore from an unreadable notebook)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 with file(cp_path, 'r') as f:
nb = current.read(f, u'json')
MinRK
add checkpoint API to FileNBManager
r10497 shutil.copy2(cp_path, nb_path)
self.log.debug("copying %s -> %s", cp_path, nb_path)
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 def delete_checkpoint(self, checkpoint_id, name, path='/'):
MinRK
add checkpoint API to FileNBManager
r10497 """delete a notebook's checkpoint"""
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 cp_path = self.get_checkpoint_path(checkpoint_id, name, path)
if not os.path.isfile(cp_path):
MinRK
add checkpoint API to FileNBManager
r10497 raise web.HTTPError(404,
Brian E. Granger
Review and refactoring of notebooks web service.
r13051 u'Notebook checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id)
MinRK
add checkpoint API to FileNBManager
r10497 )
Zachary Sailer
refactoring of nbmanager and filenbmanager...
r13046 self.log.debug("unlinking %s", cp_path)
os.unlink(cp_path)
MinRK
add checkpoint API to FileNBManager
r10497
Paul Ivanov
print info string on interrupt, log it on startup
r10019 def info_string(self):
return "Serving notebooks from local directory: %s" % self.notebook_dir