##// END OF EJS Templates
Strip out encoding declaration in %loadpy...
Strip out encoding declaration in %loadpy Closes gh-1103

File last commit:

r5709:04e15aa0
r5750:0c6c8795
Show More
notebookmanager.py
255 lines | 9.5 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
Stefan van der Walt
Move glob to global level import.
r4624 import glob
Brian E. Granger
Massive work on the notebook document format....
r4484
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
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653 from IPython.utils.traitlets import Unicode, List, Dict, Bool
Brian E. Granger
Massive work on the notebook document format....
r4484
#-----------------------------------------------------------------------------
# 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.
""")
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653
save_script = Bool(False, config=True,
help="""Also save notebooks as a Python script.
For easier use of import/%loadpy across notebooks, a <notebook-name>.py
script will be created next to any <notebook-name>.ipynb on each save.
"""
)
Brian E. Granger
Massive work on the notebook document format....
r4484 filename_ext = Unicode(u'.ipynb')
Brian E. Granger
Making JSON the default .ipynb format.
r4633 allowed_formats = List([u'json',u'py'])
Brian E. Granger
Massive work on the notebook document format....
r4484
# 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)
"""
Stefan van der Walt
Allow period characters in notebook names.
r4623 names = glob.glob(os.path.join(self.notebook_dir,
'*' + self.filename_ext))
names = [os.path.splitext(os.path.basename(name))[0]
for name in names]
Brian E. Granger
Massive work on the notebook document format....
r4484 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."""
Fernando Perez
Leave commented-out stable url code for reference, with TODO.
r4678 # TODO: the following will give stable urls for notebooks, but unless
# the notebooks are immediately redirected to their new urls when their
# filemname changes, nasty inconsistencies result. So for now it's
# disabled and instead we use a random uuid4() call. But we leave the
# logic here so that we can later reactivate it, whhen the necessary
# url redirection code is written.
#notebook_id = unicode(uuid.uuid5(uuid.NAMESPACE_URL,
# 'file://'+self.get_path_by_name(name).encode('utf-8')))
Brian E. Granger
Going back to using uuid.uuid4() for notebook ids....
r4674 notebook_id = unicode(uuid.uuid4())
Fernando Perez
Leave commented-out stable url code for reference, with TODO.
r4678
Brian E. Granger
Massive work on the notebook document format....
r4484 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:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
Brian E. Granger
Massive work on the notebook document format....
r4484 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:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
Brian E. Granger
Massive work on the notebook document format....
r4484 last_modified, nb = self.get_notebook_object(notebook_id)
MinRK
split likely multiline strings when writing to/from JSON
r5278 kwargs = {}
if format == 'json':
# don't split lines for sending over the wire, because it
# should match the Python in-memory format.
kwargs['split_lines'] = False
data = current.writes(nb, format, **kwargs)
Brian E. Granger
Massive work on the notebook document format....
r4484 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):
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
Brian E. Granger
Massive work on the notebook document format....
r4484 info = os.stat(path)
last_modified = datetime.datetime.utcfromtimestamp(info.st_mtime)
Brian E. Granger
Making JSON the default .ipynb format.
r4633 with open(path,'r') as f:
s = f.read()
try:
# v1 and v2 and json in the .ipynb files.
nb = current.reads(s, u'json')
except:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(500, u'Unreadable JSON notebook.')
Brian E. Granger
Making JSON the default .ipynb format.
r4633 if 'name' not in nb:
nb.name = os.path.split(path)[-1].split(u'.')[0]
Brian E. Granger
Massive work on the notebook document format....
r4484 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:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
Brian E. Granger
File upload/import working from notebook browser.
r4491
Brian E. Granger
Massive work on the notebook document format....
r4484 try:
Thomas Kluyver
Decode data for saving notebook, allowing saving in Python 3.
r4869 nb = current.reads(data.decode('utf-8'), format)
Brian E. Granger
Massive work on the notebook document format....
r4484 except:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(400, u'Invalid JSON data')
Brian E. Granger
File upload/import working from notebook browser.
r4491
if name is None:
try:
Brian E. Granger
Implemented metadata for notebook format.
r4637 name = nb.metadata.name
Brian E. Granger
File upload/import working from notebook browser.
r4491 except AttributeError:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(400, u'Missing notebook name')
Brian E. Granger
Implemented metadata for notebook format.
r4637 nb.metadata.name = name
Brian E. Granger
File upload/import working from notebook browser.
r4491
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:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(415, u'Invalid notebook format: %s' % format)
Brian E. Granger
File upload/import working from notebook browser.
r4491
Brian E. Granger
Massive work on the notebook document format....
r4484 try:
Thomas Kluyver
Decode data for saving notebook, allowing saving in Python 3.
r4869 nb = current.reads(data.decode('utf-8'), format)
Brian E. Granger
Massive work on the notebook document format....
r4484 except:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(400, u'Invalid JSON data')
Brian E. Granger
File upload/import working from notebook browser.
r4491
if name is not None:
Brian E. Granger
Implemented metadata for notebook format.
r4637 nb.metadata.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:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
Brian E. Granger
Massive work on the notebook document format....
r4484 old_name = self.mapping[notebook_id]
try:
Brian E. Granger
Implemented metadata for notebook format.
r4637 new_name = nb.metadata.name
Brian E. Granger
Massive work on the notebook document format....
r4484 except AttributeError:
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(400, u'Missing notebook name')
Brian E. Granger
Massive work on the notebook document format....
r4484 path = self.get_path_by_name(new_name)
try:
with open(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:
raise web.HTTPError(400, u'Unexpected error while saving notebook: %s' % e)
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653 # save .py script as well
if self.save_script:
pypath = os.path.splitext(path)[0] + '.py'
try:
with open(pypath,'w') as f:
current.write(nb, f, u'py')
MinRK
include error in 'Unexpected error' message.
r5709 except Exception as e:
raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s' % e)
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653
Brian E. Granger
Massive work on the notebook document format....
r4484 if old_name != new_name:
old_path = self.get_path_by_name(old_name)
if os.path.isfile(old_path):
os.unlink(old_path)
MinRK
allow saving notebook.py next to notebook.ipynb...
r5653 if self.save_script:
old_pypath = os.path.splitext(old_path)[0] + '.py'
if os.path.isfile(old_pypath):
os.unlink(old_pypath)
Brian E. Granger
Massive work on the notebook document format....
r4484 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):
Brian E. Granger
Adding messages to HTTPError raising....
r4676 raise web.HTTPError(404, u'Notebook does not exist: %s' % notebook_id)
Brian E. Granger
Massive work on the notebook document format....
r4484 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
Fixing bug in new metadata implementation.
r4641 metadata = current.new_metadata(name=name)
nb = current.new_notebook(metadata=metadata)
Brian E. Granger
Massive work on the notebook document format....
r4484 with open(path,'w') as f:
Brian E. Granger
Making JSON the default .ipynb format.
r4633 current.write(nb, f, u'json')
Brian E. Granger
Massive work on the notebook document format....
r4484 return notebook_id