##// END OF EJS Templates
Modifying CodeMirror focus hack to work better....
Modifying CodeMirror focus hack to work better. I had removed a text area focus event earlier today and that broke the . key. We are still having problems with CodeMirror elements gaining focus, but this seems to fix the issue.

File last commit:

r4609:a661b7c0
r4616:7421012b
Show More
notebookmanager.py
232 lines | 7.9 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
"""
Brian E. Granger
Massive work on the notebook document format....
r4484 #-----------------------------------------------------------------------------
Brian E. Granger
More review changes....
r4609 # Copyright (C) 2008-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
import os
import uuid
from tornado import web
Brian E. Granger
Adding kernel/notebook associations.
r4494 from IPython.config.configurable import LoggingConfigurable
Brian E. Granger
Massive work on the notebook document format....
r4484 from IPython.nbformat import current
from IPython.utils.traitlets import Unicode, List, Dict
#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------
Brian E. Granger
Adding kernel/notebook associations.
r4494 class NotebookManager(LoggingConfigurable):
Brian E. Granger
Massive work on the notebook document format....
r4484
Brian E. Granger
Minor fixes to config system for notebook.
r4515 notebook_dir = Unicode(os.getcwd(), config=True, help="""
The directory to use for notebooks.
""")
Brian E. Granger
Massive work on the notebook document format....
r4484 filename_ext = Unicode(u'.ipynb')
allowed_formats = List([u'json',u'xml',u'py'])
# Map notebook_ids to notebook names
mapping = Dict()
# Map notebook names to notebook_ids
rev_mapping = Dict()
def list_notebooks(self):
"""List all notebooks in the notebook dir.
This returns a list of dicts of the form::
dict(notebook_id=notebook,name=name)
"""
names = os.listdir(self.notebook_dir)
Brian E. Granger
More review changes....
r4609 names = [name.split(u'.')[0]
Brian E. Granger
Massive work on the notebook document format....
r4484 for name in names if name.endswith(self.filename_ext)]
data = []
for name in names:
if name not in self.rev_mapping:
notebook_id = self.new_notebook_id(name)
else:
notebook_id = self.rev_mapping[name]
data.append(dict(notebook_id=notebook_id,name=name))
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 data = sorted(data, key=lambda item: item['name'])
Brian E. Granger
Massive work on the notebook document format....
r4484 return data
def new_notebook_id(self, name):
"""Generate a new notebook_id for a name and store its mappings."""
notebook_id = unicode(uuid.uuid4())
self.mapping[notebook_id] = name
self.rev_mapping[name] = notebook_id
return notebook_id
def delete_notebook_id(self, notebook_id):
"""Delete a notebook's id only. This doesn't delete the actual notebook."""
name = self.mapping[notebook_id]
del self.mapping[notebook_id]
del self.rev_mapping[name]
def notebook_exists(self, notebook_id):
"""Does a notebook exist?"""
if notebook_id not in self.mapping:
return False
path = self.get_path_by_name(self.mapping[notebook_id])
Brian E. Granger
More review changes....
r4609 return os.path.isfile(path)
Brian E. Granger
Massive work on the notebook document format....
r4484
def find_path(self, notebook_id):
"""Return a full path to a notebook given its notebook_id."""
try:
name = self.mapping[notebook_id]
except KeyError:
raise web.HTTPError(404)
return self.get_path_by_name(name)
def get_path_by_name(self, name):
"""Return a full path to a notebook given its name."""
filename = name + self.filename_ext
path = os.path.join(self.notebook_dir, filename)
return path
def get_notebook(self, notebook_id, format=u'json'):
"""Get the representation of a notebook in format by notebook_id."""
format = unicode(format)
if format not in self.allowed_formats:
raise web.HTTPError(415)
last_modified, nb = self.get_notebook_object(notebook_id)
data = current.writes(nb, format)
name = nb.get('name','notebook')
return last_modified, name, data
def get_notebook_object(self, notebook_id):
"""Get the NotebookNode representation of a notebook by notebook_id."""
path = self.find_path(notebook_id)
if not os.path.isfile(path):
raise web.HTTPError(404)
info = os.stat(path)
last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
try:
with open(path,'r') as f:
s = f.read()
try:
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 # v2 and later have xml in the .ipynb files.
Brian E. Granger
Massive work on the notebook document format....
r4484 nb = current.reads(s, 'xml')
except:
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 # v1 had json in the .ipynb files.
Brian E. Granger
Massive work on the notebook document format....
r4484 nb = current.reads(s, 'json')
Brian E. Granger
Implemented basic notebook browser and fixed numerous bugs.
r4488 # v1 notebooks don't have a name field, so use the filename.
nb.name = os.path.split(path)[-1].split(u'.')[0]
Brian E. Granger
Massive work on the notebook document format....
r4484 except:
raise web.HTTPError(404)
return last_modified, nb
Brian E. Granger
File upload/import working from notebook browser.
r4491 def save_new_notebook(self, data, name=None, format=u'json'):
"""Save a new notebook and return its notebook_id.
If a name is passed in, it overrides any values in the notebook data
and the value in the data is updated to use that value.
"""
Brian E. Granger
Massive work on the notebook document format....
r4484 if format not in self.allowed_formats:
raise web.HTTPError(415)
Brian E. Granger
File upload/import working from notebook browser.
r4491
Brian E. Granger
Massive work on the notebook document format....
r4484 try:
nb = current.reads(data, format)
except:
Brian E. Granger
File upload/import working from notebook browser.
r4491 if format == u'xml':
# v1 notebooks might come in with a format='xml' but be json.
try:
nb = current.reads(data, u'json')
except:
raise web.HTTPError(400)
else:
raise web.HTTPError(400)
if name is None:
try:
name = nb.name
except AttributeError:
raise web.HTTPError(400)
nb.name = name
Brian E. Granger
Massive work on the notebook document format....
r4484 notebook_id = self.new_notebook_id(name)
self.save_notebook_object(notebook_id, nb)
return notebook_id
Brian E. Granger
File upload/import working from notebook browser.
r4491 def save_notebook(self, notebook_id, data, name=None, format=u'json'):
Brian E. Granger
Massive work on the notebook document format....
r4484 """Save an existing notebook by notebook_id."""
if format not in self.allowed_formats:
raise web.HTTPError(415)
Brian E. Granger
File upload/import working from notebook browser.
r4491
Brian E. Granger
Massive work on the notebook document format....
r4484 try:
nb = current.reads(data, format)
except:
Brian E. Granger
File upload/import working from notebook browser.
r4491 if format == u'xml':
# v1 notebooks might come in with a format='xml' but be json.
try:
nb = current.reads(data, u'json')
except:
raise web.HTTPError(400)
else:
raise web.HTTPError(400)
if name is not None:
nb.name = name
Brian E. Granger
Massive work on the notebook document format....
r4484 self.save_notebook_object(notebook_id, nb)
def save_notebook_object(self, notebook_id, nb):
"""Save an existing notebook object by notebook_id."""
if notebook_id not in self.mapping:
raise web.HTTPError(404)
old_name = self.mapping[notebook_id]
try:
new_name = nb.name
except AttributeError:
raise web.HTTPError(400)
path = self.get_path_by_name(new_name)
try:
with open(path,'w') as f:
current.write(nb, f, u'xml')
except:
raise web.HTTPError(400)
if old_name != new_name:
old_path = self.get_path_by_name(old_name)
if os.path.isfile(old_path):
os.unlink(old_path)
self.mapping[notebook_id] = new_name
self.rev_mapping[new_name] = notebook_id
def delete_notebook(self, notebook_id):
"""Delete notebook by notebook_id."""
path = self.find_path(notebook_id)
if not os.path.isfile(path):
raise web.HTTPError(404)
os.unlink(path)
self.delete_notebook_id(notebook_id)
def new_notebook(self):
"""Create a new notebook and returns its notebook_id."""
i = 0
while True:
name = u'Untitled%i' % i
path = self.get_path_by_name(name)
if not os.path.isfile(path):
break
else:
i = i+1
notebook_id = self.new_notebook_id(name)
Brian E. Granger
A single tab-completion match is now automatically selected.
r4554 nb = current.new_notebook(name=name)
Brian E. Granger
Massive work on the notebook document format....
r4484 with open(path,'w') as f:
current.write(nb, f, u'xml')
return notebook_id