diff --git a/IPython/html/base/handlers.py b/IPython/html/base/handlers.py index 14e5487..6c3dcdd 100644 --- a/IPython/html/base/handlers.py +++ b/IPython/html/base/handlers.py @@ -19,10 +19,9 @@ except ImportError: from jinja2 import TemplateNotFound from tornado import web -try: - from tornado.log import app_log -except ImportError: - app_log = logging.getLogger() +from tornado import gen +from tornado.log import app_log + import IPython from IPython.utils.sysinfo import get_sys_info @@ -355,9 +354,10 @@ def json_errors(method): the error in a human readable form. """ @functools.wraps(method) + @gen.coroutine def wrapper(self, *args, **kwargs): try: - result = method(self, *args, **kwargs) + result = yield gen.maybe_future(method(self, *args, **kwargs)) except web.HTTPError as e: status = e.status_code message = e.log_message @@ -375,7 +375,8 @@ def json_errors(method): reply = dict(message=message, reason=None, traceback=tb_text) self.finish(json.dumps(reply)) else: - return result + # FIXME: can use regular return in generators in py3 + raise gen.Return(result) return wrapper diff --git a/IPython/html/services/contents/handlers.py b/IPython/html/services/contents/handlers.py index 07c700c..ea629c9 100644 --- a/IPython/html/services/contents/handlers.py +++ b/IPython/html/services/contents/handlers.py @@ -5,7 +5,7 @@ import json -from tornado import web +from tornado import gen, web from IPython.html.utils import url_path_join, url_escape from IPython.utils.jsonutil import date_default @@ -102,6 +102,7 @@ class ContentsHandler(IPythonHandler): @web.authenticated @json_errors + @gen.coroutine def get(self, path=''): """Return a model for a file or directory. @@ -117,7 +118,7 @@ class ContentsHandler(IPythonHandler): if format not in {None, 'text', 'base64'}: raise web.HTTPError(400, u'Format %r is invalid' % format) - model = self.contents_manager.get(path=path, type=type, format=format) + model = yield gen.maybe_future(self.contents_manager.get(path=path, type=type, format=format)) if model['type'] == 'directory': # group listing by type, then by name (case-insensitive) # FIXME: sorting should be done in the frontends @@ -127,52 +128,58 @@ class ContentsHandler(IPythonHandler): @web.authenticated @json_errors + @gen.coroutine def patch(self, path=''): """PATCH renames a file or directory without re-uploading content.""" cm = self.contents_manager model = self.get_json_body() if model is None: raise web.HTTPError(400, u'JSON body missing') - model = cm.update(model, path) + model = yield gen.maybe_future(cm.update(model, path)) validate_model(model, expect_content=False) self._finish_model(model) - + + @gen.coroutine def _copy(self, copy_from, copy_to=None): """Copy a file, optionally specifying a target directory.""" self.log.info(u"Copying {copy_from} to {copy_to}".format( copy_from=copy_from, copy_to=copy_to or '', )) - model = self.contents_manager.copy(copy_from, copy_to) + model = yield gen.maybe_future(self.contents_manager.copy(copy_from, copy_to)) self.set_status(201) validate_model(model, expect_content=False) self._finish_model(model) + @gen.coroutine def _upload(self, model, path): """Handle upload of a new file to path""" self.log.info(u"Uploading file to %s", path) - model = self.contents_manager.new(model, path) + model = yield gen.maybe_future(self.contents_manager.new(model, path)) self.set_status(201) validate_model(model, expect_content=False) self._finish_model(model) - + + @gen.coroutine def _new_untitled(self, path, type='', ext=''): """Create a new, empty untitled entity""" self.log.info(u"Creating new %s in %s", type or 'file', path) - model = self.contents_manager.new_untitled(path=path, type=type, ext=ext) + model = yield gen.maybe_future(self.contents_manager.new_untitled(path=path, type=type, ext=ext)) self.set_status(201) validate_model(model, expect_content=False) self._finish_model(model) - + + @gen.coroutine def _save(self, model, path): """Save an existing file.""" self.log.info(u"Saving file at %s", path) - model = self.contents_manager.save(model, path) + model = yield gen.maybe_future(self.contents_manager.save(model, path)) validate_model(model, expect_content=False) self._finish_model(model) @web.authenticated @json_errors + @gen.coroutine def post(self, path=''): """Create a new file in the specified path. @@ -200,14 +207,15 @@ class ContentsHandler(IPythonHandler): ext = model.get('ext', '') type = model.get('type', '') if copy_from: - self._copy(copy_from, path) + yield self._copy(copy_from, path) else: - self._new_untitled(path, type=type, ext=ext) + yield self._new_untitled(path, type=type, ext=ext) else: - self._new_untitled(path) + yield self._new_untitled(path) @web.authenticated @json_errors + @gen.coroutine def put(self, path=''): """Saves the file in the location specified by name and path. @@ -223,20 +231,22 @@ class ContentsHandler(IPythonHandler): if model: if model.get('copy_from'): raise web.HTTPError(400, "Cannot copy with PUT, only POST") - if self.contents_manager.file_exists(path): - self._save(model, path) + exists = yield gen.maybe_future(self.contents_manager.file_exists(path)) + if exists: + yield gen.maybe_future(self._save(model, path)) else: - self._upload(model, path) + yield gen.maybe_future(self._upload(model, path)) else: - self._new_untitled(path) + yield gen.maybe_future(self._new_untitled(path)) @web.authenticated @json_errors + @gen.coroutine def delete(self, path=''): """delete a file in the given path""" cm = self.contents_manager self.log.warn('delete %s', path) - cm.delete(path) + yield gen.maybe_future(cm.delete(path)) self.set_status(204) self.finish() @@ -247,19 +257,21 @@ class CheckpointsHandler(IPythonHandler): @web.authenticated @json_errors + @gen.coroutine def get(self, path=''): """get lists checkpoints for a file""" cm = self.contents_manager - checkpoints = cm.list_checkpoints(path) + checkpoints = yield gen.maybe_future(cm.list_checkpoints(path)) data = json.dumps(checkpoints, default=date_default) self.finish(data) @web.authenticated @json_errors + @gen.coroutine def post(self, path=''): """post creates a new checkpoint""" cm = self.contents_manager - checkpoint = cm.create_checkpoint(path) + checkpoint = yield gen.maybe_future(cm.create_checkpoint(path)) data = json.dumps(checkpoint, default=date_default) location = url_path_join(self.base_url, 'api/contents', path, 'checkpoints', checkpoint['id']) @@ -274,19 +286,21 @@ class ModifyCheckpointsHandler(IPythonHandler): @web.authenticated @json_errors + @gen.coroutine def post(self, path, checkpoint_id): """post restores a file from a checkpoint""" cm = self.contents_manager - cm.restore_checkpoint(checkpoint_id, path) + yield gen.maybe_future(cm.restore_checkpoint(checkpoint_id, path)) self.set_status(204) self.finish() @web.authenticated @json_errors + @gen.coroutine def delete(self, path, checkpoint_id): """delete clears a checkpoint for a given file""" cm = self.contents_manager - cm.delete_checkpoint(checkpoint_id, path) + yield gen.maybe_future(cm.delete_checkpoint(checkpoint_id, path)) self.set_status(204) self.finish()