##// END OF EJS Templates
move utils.io.atomic_writing to contents.fileio
move utils.io.atomic_writing to contents.fileio

File last commit:

r21073:ff373955
r21113:348b2930
Show More
handlers.py
342 lines | 11.2 KiB | text/x-python | PythonLexer
MinRK
rename notebooks service to contents service...
r17524 """Tornado handlers for the contents web service."""
Brian E. Granger
Adding new files.
r10641
MinRK
rename notebooks service to contents service...
r17524 # Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
Brian E. Granger
Adding new files.
r10641
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 import json
Brian E. Granger
Adding new files.
r10641
Min RK
allow ContentsManager methods to return Futures...
r19595 from tornado import gen, web
Brian E. Granger
Adding new files.
r10641
MinRK
escape URLs in Location headers
r13132 from IPython.html.utils import url_path_join, url_escape
Min RK
import jsonutil from jupyter_client
r21073 from jupyter_client.jsonutil import date_default
Brian E. Granger
Adding new files.
r10641
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 from IPython.html.base.handlers import (
IPythonHandler, json_errors, path_regex,
)
Brian E. Granger
Adding new files.
r10641
Zachary Sailer
manual rebase notebooks web services
r12984
MinRK
contents service review...
r17529 def sort_key(model):
"""key function for case-insensitive sort by name and type"""
iname = model['name'].lower()
type_key = {
'directory' : '0',
'notebook' : '1',
'file' : '2',
}.get(model['type'], '9')
return u'%s%s' % (type_key, iname)
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395
def validate_model(model, expect_content):
Scott Sanderson
DOC: Add docstring for validate_model.
r19396 """
Validate a model returned by a ContentsManager method.
If expect_content is True, then we expect non-null entries for 'content'
and 'format'.
"""
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 required_keys = {
Scott Sanderson
DEV: Tweaks from PR feedback....
r19399 "name",
"path",
"type",
"writable",
"created",
"last_modified",
"mimetype",
"content",
"format",
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 }
missing = required_keys - set(model.keys())
if missing:
raise web.HTTPError(
500,
u"Missing Model Keys: {missing}".format(missing=missing),
)
maybe_none_keys = ['content', 'format']
Scott Sanderson
DEV: Tweaks from PR feedback....
r19399 if model['type'] == 'file':
# mimetype should be populated only for file models
maybe_none_keys.append('mimetype')
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 if expect_content:
errors = [key for key in maybe_none_keys if model[key] is None]
if errors:
raise web.HTTPError(
500,
u"Keys unexpectedly None: {keys}".format(keys=errors),
)
else:
Scott Sanderson
DEV: Tweaks from PR feedback....
r19399 errors = {
key: model[key]
for key in maybe_none_keys
if model[key] is not None
}
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 if errors:
raise web.HTTPError(
500,
u"Keys unexpectedly not None: {keys}".format(keys=errors),
)
MinRK
rename notebooks service to contents service...
r17524 class ContentsHandler(IPythonHandler):
Paul Ivanov
removing contents changes from this PR
r13042
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE')
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def location_url(self, path):
MinRK
rename notebooks service to contents service...
r17524 """Return the full URL location of a file.
MinRK
mv services/notebooks services/contents
r17523
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 Parameters
----------
path : unicode
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 The API path of the file, such as "foo/bar.txt".
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 """
MinRK
escape URLs in Location headers
r13132 return url_escape(url_path_join(
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 self.base_url, 'api', 'contents', path
MinRK
escape URLs in Location headers
r13132 ))
Paul Ivanov
removing contents changes from this PR
r13042
MinRK
update notebook creation handlers...
r13129 def _finish_model(self, model, location=True):
"""Finish a JSON request with a model, setting relevant headers, etc."""
if location:
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 location = self.location_url(model['path'])
MinRK
update notebook creation handlers...
r13129 self.set_header('Location', location)
self.set_header('Last-Modified', model['last_modified'])
Min RK
set application/json on contents model replies
r19313 self.set_header('Content-Type', 'application/json')
MinRK
update notebook creation handlers...
r13129 self.finish(json.dumps(model, default=date_default))
MinRK
mv services/notebooks services/contents
r17523
Paul Ivanov
removing contents changes from this PR
r13042 @web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def get(self, path=''):
MinRK
updates per review...
r17535 """Return a model for a file or directory.
Brian E. Granger
Adding docstring to NotebookHandler.get.
r13113
MinRK
updates per review...
r17535 A directory model contains a list of models (without content)
of the files and directories it contains.
MinRK
adjust definition of 'path' in notebooks...
r13067 """
MinRK
teach contents service about non-notebook files
r17525 path = path or ''
Min RK
ContentsManager type kwarg to match model key...
r19391 type = self.get_query_argument('type', default=None)
if type not in {None, 'directory', 'file', 'notebook'}:
raise web.HTTPError(400, u'Type %r is invalid' % type)
Thomas Kluyver
Add type parameter for contents GET requests
r18781
Scott Sanderson
Minor cleanups in the contents API....
r19349 format = self.get_query_argument('format', default=None)
Thomas Kluyver
Allow specifying format when getting files from contents API
r18788 if format not in {None, 'text', 'base64'}:
raise web.HTTPError(400, u'Format %r is invalid' % format)
Min RK
allow requesting contents without body...
r20134 content = self.get_query_argument('content', default='1')
if content not in {'0', '1'}:
raise web.HTTPError(400, u'Content %r is invalid' % content)
content = int(content)
model = yield gen.maybe_future(self.contents_manager.get(
path=path, type=type, format=format, content=content,
))
if model['type'] == 'directory' and content:
MinRK
contents service review...
r17529 # group listing by type, then by name (case-insensitive)
MinRK
updates per review...
r17535 # FIXME: sorting should be done in the frontends
MinRK
contents service review...
r17529 model['content'].sort(key=sort_key)
Min RK
allow requesting contents without body...
r20134 validate_model(model, expect_content=content)
MinRK
update notebook creation handlers...
r13129 self._finish_model(model, location=False)
Brian E. Granger
Adding new files.
r10641
Paul Ivanov
removing contents changes from this PR
r13042 @web.authenticated
MinRK
adjust definition of 'path' in notebooks...
r13067 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def patch(self, path=''):
"""PATCH renames a file or directory without re-uploading content."""
MinRK
rename notebooks service to contents service...
r17524 cm = self.contents_manager
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 model = self.get_json_body()
if model is None:
raise web.HTTPError(400, u'JSON body missing')
Min RK
allow ContentsManager methods to return Futures...
r19595 model = yield gen.maybe_future(cm.update(model, path))
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 validate_model(model, expect_content=False)
MinRK
update notebook creation handlers...
r13129 self._finish_model(model)
Min RK
allow ContentsManager methods to return Futures...
r19595
@gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def _copy(self, copy_from, copy_to=None):
Min RK
address review in contents service...
r18758 """Copy a file, optionally specifying a target directory."""
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 self.log.info(u"Copying {copy_from} to {copy_to}".format(
MinRK
updates per review...
r17535 copy_from=copy_from,
copy_to=copy_to or '',
))
Min RK
allow ContentsManager methods to return Futures...
r19595 model = yield gen.maybe_future(self.contents_manager.copy(copy_from, copy_to))
MinRK
update notebook creation handlers...
r13129 self.set_status(201)
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 validate_model(model, expect_content=False)
MinRK
update notebook creation handlers...
r13129 self._finish_model(model)
MinRK
mv services/notebooks services/contents
r17523
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def _upload(self, model, path):
"""Handle upload of a new file to path"""
self.log.info(u"Uploading file to %s", path)
Min RK
allow ContentsManager methods to return Futures...
r19595 model = yield gen.maybe_future(self.contents_manager.new(model, path))
MinRK
update notebook creation handlers...
r13129 self.set_status(201)
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 validate_model(model, expect_content=False)
MinRK
update notebook creation handlers...
r13129 self._finish_model(model)
Min RK
allow ContentsManager methods to return Futures...
r19595
@gen.coroutine
Min RK
split ContentsManager.new, add ContentsManager.new_untitled
r18759 def _new_untitled(self, path, type='', ext=''):
"""Create a new, empty untitled entity"""
Min RK
address review in contents service...
r18758 self.log.info(u"Creating new %s in %s", type or 'file', path)
Min RK
allow ContentsManager methods to return Futures...
r19595 model = yield gen.maybe_future(self.contents_manager.new_untitled(path=path, type=type, ext=ext))
MinRK
update notebook creation handlers...
r13129 self.set_status(201)
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 validate_model(model, expect_content=False)
MinRK
update notebook creation handlers...
r13129 self._finish_model(model)
Min RK
allow ContentsManager methods to return Futures...
r19595
@gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def _save(self, model, path):
MinRK
rename notebooks service to contents service...
r17524 """Save an existing file."""
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 self.log.info(u"Saving file at %s", path)
Min RK
allow ContentsManager methods to return Futures...
r19595 model = yield gen.maybe_future(self.contents_manager.save(model, path))
Scott Sanderson
DEV: Validate models returned from ContentsManager methods.
r19395 validate_model(model, expect_content=False)
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 self._finish_model(model)
MinRK
mv services/notebooks services/contents
r17523
Paul Ivanov
removing contents changes from this PR
r13042 @web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def post(self, path=''):
Min RK
address review in contents service...
r18758 """Create a new file in the specified path.
MinRK
mv services/notebooks services/contents
r17523
Min RK
address review in contents service...
r18758 POST creates new files. The server always decides on the name.
MinRK
mv services/notebooks services/contents
r17523
MinRK
rename notebooks service to contents service...
r17524 POST /api/contents/path
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 New untitled, empty file or directory.
MinRK
add support and tests for uploading and saving regular files
r17527 POST /api/contents/path
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 with body {"copy_from" : "/path/to/OtherNotebook.ipynb"}
Thomas Kluyver
Miscellaneous docs fixes
r13597 New copy of OtherNotebook in path
MinRK
update upload and copy...
r13074 """
MinRK
mv services/notebooks services/contents
r17523
MinRK
teach contents service about non-notebook files
r17525 cm = self.contents_manager
if cm.file_exists(path):
Min RK
address review in contents service...
r18758 raise web.HTTPError(400, "Cannot POST to files, use PUT instead.")
MinRK
mv services/notebooks services/contents
r17523
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 if not cm.dir_exists(path):
MinRK
teach contents service about non-notebook files
r17525 raise web.HTTPError(404, "No such directory: %s" % path)
MinRK
copy_from in json, not in url param
r13137 model = self.get_json_body()
MinRK
mv services/notebooks services/contents
r17523
MinRK
copy_from in json, not in url param
r13137 if model is not None:
copy_from = model.get('copy_from')
Min RK
address review in contents service...
r18758 ext = model.get('ext', '')
Min RK
split ContentsManager.new, add ContentsManager.new_untitled
r18759 type = model.get('type', '')
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 if copy_from:
Min RK
allow ContentsManager methods to return Futures...
r19595 yield self._copy(copy_from, path)
MinRK
copy_from in json, not in url param
r13137 else:
Min RK
allow ContentsManager methods to return Futures...
r19595 yield self._new_untitled(path, type=type, ext=ext)
MinRK
update notebook creation handlers...
r13129 else:
Min RK
allow ContentsManager methods to return Futures...
r19595 yield self._new_untitled(path)
Zachary Sailer
Add 'patch' to session & notebook, rename working
r12997
@web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def put(self, path=''):
MinRK
rename notebooks service to contents service...
r17524 """Saves the file in the location specified by name and path.
MinRK
mv services/notebooks services/contents
r17523
Thomas Kluyver
Miscellaneous docs fixes
r13597 PUT is very similar to POST, but the requester specifies the name,
whereas with POST, the server picks the name.
MinRK
mv services/notebooks services/contents
r17523
MinRK
rename notebooks service to contents service...
r17524 PUT /api/contents/path/Name.ipynb
Thomas Kluyver
Miscellaneous docs fixes
r13597 Save notebook at ``path/Name.ipynb``. Notebook structure is specified
in `content` key of JSON request body. If content is not specified,
create a new empty notebook.
MinRK
update notebook creation handlers...
r13129 """
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 model = self.get_json_body()
MinRK
copy_from in json, not in url param
r13137 if model:
Min RK
remove copy via PUT...
r18750 if model.get('copy_from'):
raise web.HTTPError(400, "Cannot copy with PUT, only POST")
Min RK
allow ContentsManager methods to return Futures...
r19595 exists = yield gen.maybe_future(self.contents_manager.file_exists(path))
if exists:
yield gen.maybe_future(self._save(model, path))
MinRK
update notebook creation handlers...
r13129 else:
Min RK
allow ContentsManager methods to return Futures...
r19595 yield gen.maybe_future(self._upload(model, path))
MinRK
update notebook creation handlers...
r13129 else:
Min RK
allow ContentsManager methods to return Futures...
r19595 yield gen.maybe_future(self._new_untitled(path))
Brian E. Granger
Adding new files.
r10641
@web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def delete(self, path=''):
MinRK
rename notebooks service to contents service...
r17524 """delete a file in the given path"""
cm = self.contents_manager
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 self.log.warn('delete %s', path)
Min RK
allow ContentsManager methods to return Futures...
r19595 yield gen.maybe_future(cm.delete(path))
Brian E. Granger
Adding new files.
r10641 self.set_status(204)
self.finish()
Paul Ivanov
removing contents changes from this PR
r13042
MinRK
rename notebooks service to contents service...
r17524 class CheckpointsHandler(IPythonHandler):
MinRK
mv services/notebooks services/contents
r17523
Paul Ivanov
removing contents changes from this PR
r13042 SUPPORTED_METHODS = ('GET', 'POST')
MinRK
mv services/notebooks services/contents
r17523
Zachary Sailer
change standard money keys
r13015 @web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def get(self, path=''):
MinRK
rename notebooks service to contents service...
r17524 """get lists checkpoints for a file"""
cm = self.contents_manager
Min RK
allow ContentsManager methods to return Futures...
r19595 checkpoints = yield gen.maybe_future(cm.list_checkpoints(path))
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 data = json.dumps(checkpoints, default=date_default)
Paul Ivanov
removing contents changes from this PR
r13042 self.finish(data)
MinRK
mv services/notebooks services/contents
r17523
Zachary Sailer
change standard money keys
r13015 @web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def post(self, path=''):
Paul Ivanov
removing contents changes from this PR
r13042 """post creates a new checkpoint"""
MinRK
rename notebooks service to contents service...
r17524 cm = self.contents_manager
Min RK
allow ContentsManager methods to return Futures...
r19595 checkpoint = yield gen.maybe_future(cm.create_checkpoint(path))
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 data = json.dumps(checkpoint, default=date_default)
MinRK
rename notebooks service to contents service...
r17524 location = url_path_join(self.base_url, 'api/contents',
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 path, 'checkpoints', checkpoint['id'])
MinRK
escape URLs in Location headers
r13132 self.set_header('Location', url_escape(location))
Thomas Kluyver
Fixes for notebook checkpoint APIs
r13110 self.set_status(201)
Paul Ivanov
removing contents changes from this PR
r13042 self.finish(data)
MinRK
rename notebooks service to contents service...
r17524 class ModifyCheckpointsHandler(IPythonHandler):
MinRK
mv services/notebooks services/contents
r17523
Paul Ivanov
removing contents changes from this PR
r13042 SUPPORTED_METHODS = ('POST', 'DELETE')
MinRK
mv services/notebooks services/contents
r17523
Paul Ivanov
removing contents changes from this PR
r13042 @web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def post(self, path, checkpoint_id):
MinRK
rename notebooks service to contents service...
r17524 """post restores a file from a checkpoint"""
cm = self.contents_manager
Min RK
allow ContentsManager methods to return Futures...
r19595 yield gen.maybe_future(cm.restore_checkpoint(checkpoint_id, path))
Paul Ivanov
removing contents changes from this PR
r13042 self.set_status(204)
self.finish()
MinRK
mv services/notebooks services/contents
r17523
Paul Ivanov
removing contents changes from this PR
r13042 @web.authenticated
Zachary Sailer
review fixes on tests, add extra kernel api test
r13045 @json_errors
Min RK
allow ContentsManager methods to return Futures...
r19595 @gen.coroutine
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 def delete(self, path, checkpoint_id):
MinRK
rename notebooks service to contents service...
r17524 """delete clears a checkpoint for a given file"""
cm = self.contents_manager
Min RK
allow ContentsManager methods to return Futures...
r19595 yield gen.maybe_future(cm.delete_checkpoint(checkpoint_id, path))
Paul Ivanov
removing contents changes from this PR
r13042 self.set_status(204)
self.finish()
MinRK
mv services/notebooks services/contents
r17523
MinRK
redirect /api/notebooks to /api/contents...
r18262
class NotebooksRedirectHandler(IPythonHandler):
"""Redirect /api/notebooks to /api/contents"""
SUPPORTED_METHODS = ('GET', 'PUT', 'PATCH', 'POST', 'DELETE')
def get(self, path):
self.log.warn("/api/notebooks is deprecated, use /api/contents")
self.redirect(url_path_join(
self.base_url,
'api/contents',
path
))
put = patch = post = delete = get
Brian E. Granger
More work on the handlers
r10647 #-----------------------------------------------------------------------------
# URL to handler mappings
#-----------------------------------------------------------------------------
Paul Ivanov
removing contents changes from this PR
r13042
_checkpoint_id_regex = r"(?P<checkpoint_id>[\w-]+)"
Brian E. Granger
More work on the handlers
r10647
default_handlers = [
MinRK
Remove separate 'path', 'name' in Contents API...
r18749 (r"/api/contents%s/checkpoints" % path_regex, CheckpointsHandler),
(r"/api/contents%s/checkpoints/%s" % (path_regex, _checkpoint_id_regex),
MinRK
rename notebooks service to contents service...
r17524 ModifyCheckpointsHandler),
(r"/api/contents%s" % path_regex, ContentsHandler),
MinRK
redirect /api/notebooks to /api/contents...
r18262 (r"/api/notebooks/?(.*)", NotebooksRedirectHandler),
Brian E. Granger
More work on the handlers
r10647 ]