diff --git a/IPython/html/base/handlers.py b/IPython/html/base/handlers.py index b52d5de..8796e55 100644 --- a/IPython/html/base/handlers.py +++ b/IPython/html/base/handlers.py @@ -1,4 +1,4 @@ -"""Base Tornado handlers for the notebook.""" +"""Base Tornado handlers for the notebook server.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. @@ -141,8 +141,8 @@ class IPythonHandler(AuthenticatedHandler): return self.settings['kernel_manager'] @property - def notebook_manager(self): - return self.settings['notebook_manager'] + def contents_manager(self): + return self.settings['contents_manager'] @property def cluster_manager(self): @@ -158,7 +158,7 @@ class IPythonHandler(AuthenticatedHandler): @property def project_dir(self): - return self.notebook_manager.notebook_dir + return getattr(self.contents_manager, 'root_dir', '/') #--------------------------------------------------------------- # CORS diff --git a/IPython/html/nbconvert/handlers.py b/IPython/html/nbconvert/handlers.py index fb97f5f..180e6c6 100644 --- a/IPython/html/nbconvert/handlers.py +++ b/IPython/html/nbconvert/handlers.py @@ -73,7 +73,7 @@ class NbconvertFileHandler(IPythonHandler): exporter = get_exporter(format, config=self.config, log=self.log) path = path.strip('/') - model = self.notebook_manager.get_notebook(name=name, path=path) + model = self.contents_manager.get(name=name, path=path) self.set_header('Last-Modified', model['last_modified']) diff --git a/IPython/html/nbconvert/tests/test_nbconvert_handlers.py b/IPython/html/nbconvert/tests/test_nbconvert_handlers.py index 6916f1f..ea44217 100644 --- a/IPython/html/nbconvert/tests/test_nbconvert_handlers.py +++ b/IPython/html/nbconvert/tests/test_nbconvert_handlers.py @@ -106,7 +106,7 @@ class APITest(NotebookTestBase): @onlyif_cmds_exist('pandoc') def test_from_post(self): - nbmodel_url = url_path_join(self.base_url(), 'api/notebooks/foo/testnb.ipynb') + nbmodel_url = url_path_join(self.base_url(), 'api/contents/foo/testnb.ipynb') nbmodel = requests.get(nbmodel_url).json() r = self.nbconvert_api.from_post(format='html', nbmodel=nbmodel) @@ -121,7 +121,7 @@ class APITest(NotebookTestBase): @onlyif_cmds_exist('pandoc') def test_from_post_zip(self): - nbmodel_url = url_path_join(self.base_url(), 'api/notebooks/foo/testnb.ipynb') + nbmodel_url = url_path_join(self.base_url(), 'api/contents/foo/testnb.ipynb') nbmodel = requests.get(nbmodel_url).json() r = self.nbconvert_api.from_post(format='latex', nbmodel=nbmodel) diff --git a/IPython/html/notebook/handlers.py b/IPython/html/notebook/handlers.py index 5db20cc..0fc8891 100644 --- a/IPython/html/notebook/handlers.py +++ b/IPython/html/notebook/handlers.py @@ -35,12 +35,12 @@ class NotebookHandler(IPythonHandler): """get renders the notebook template if a name is given, or redirects to the '/files/' handler if the name is not given.""" path = path.strip('/') - nbm = self.notebook_manager + cm = self.contents_manager if name is None: raise web.HTTPError(500, "This shouldn't be accessible: %s" % self.request.uri) # a .ipynb filename was given - if not nbm.notebook_exists(name, path): + if not cm.file_exists(name, path): raise web.HTTPError(404, u'Notebook does not exist: %s/%s' % (path, name)) name = url_escape(name) path = url_escape(path) @@ -55,8 +55,8 @@ class NotebookHandler(IPythonHandler): class NotebookRedirectHandler(IPythonHandler): def get(self, path=''): - nbm = self.notebook_manager - if nbm.path_exists(path): + cm = self.contents_manager + if cm.path_exists(path): # it's a *directory*, redirect to /tree url = url_path_join(self.base_url, 'tree', path) else: @@ -68,7 +68,7 @@ class NotebookRedirectHandler(IPythonHandler): # but so is the files handler itself, # so it should work until both are cleaned up. parts = path.split('/') - files_path = os.path.join(nbm.notebook_dir, *parts) + files_path = os.path.join(cm.root_dir, *parts) if not os.path.exists(files_path): self.log.warn("Deprecated files/ URL: %s", path) path = path.replace('/files/', '/', 1) diff --git a/IPython/html/notebookapp.py b/IPython/html/notebookapp.py index 013f14e..31d7d0f 100644 --- a/IPython/html/notebookapp.py +++ b/IPython/html/notebookapp.py @@ -55,8 +55,8 @@ from IPython.html import DEFAULT_STATIC_FILES_PATH from .base.handlers import Template404 from .log import log_request from .services.kernels.kernelmanager import MappingKernelManager -from .services.notebooks.nbmanager import NotebookManager -from .services.notebooks.filenbmanager import FileNotebookManager +from .services.contents.manager import ContentsManager +from .services.contents.filemanager import FileContentsManager from .services.clusters.clustermanager import ClusterManager from .services.sessions.sessionmanager import SessionManager @@ -121,19 +121,19 @@ def load_handlers(name): class NotebookWebApplication(web.Application): - def __init__(self, ipython_app, kernel_manager, notebook_manager, + def __init__(self, ipython_app, kernel_manager, contents_manager, cluster_manager, session_manager, kernel_spec_manager, log, base_url, settings_overrides, jinja_env_options): settings = self.init_settings( - ipython_app, kernel_manager, notebook_manager, cluster_manager, + ipython_app, kernel_manager, contents_manager, cluster_manager, session_manager, kernel_spec_manager, log, base_url, settings_overrides, jinja_env_options) handlers = self.init_handlers(settings) super(NotebookWebApplication, self).__init__(handlers, **settings) - def init_settings(self, ipython_app, kernel_manager, notebook_manager, + def init_settings(self, ipython_app, kernel_manager, contents_manager, cluster_manager, session_manager, kernel_spec_manager, log, base_url, settings_overrides, jinja_env_options=None): @@ -165,7 +165,7 @@ class NotebookWebApplication(web.Application): # managers kernel_manager=kernel_manager, - notebook_manager=notebook_manager, + contents_manager=contents_manager, cluster_manager=cluster_manager, session_manager=session_manager, kernel_spec_manager=kernel_spec_manager, @@ -193,18 +193,20 @@ class NotebookWebApplication(web.Application): handlers.extend(load_handlers('nbconvert.handlers')) handlers.extend(load_handlers('kernelspecs.handlers')) handlers.extend(load_handlers('services.kernels.handlers')) - handlers.extend(load_handlers('services.notebooks.handlers')) + handlers.extend(load_handlers('services.contents.handlers')) handlers.extend(load_handlers('services.clusters.handlers')) handlers.extend(load_handlers('services.sessions.handlers')) handlers.extend(load_handlers('services.nbconvert.handlers')) handlers.extend(load_handlers('services.kernelspecs.handlers')) # FIXME: /files/ should be handled by the Contents service when it exists - nbm = settings['notebook_manager'] - if hasattr(nbm, 'notebook_dir'): - handlers.extend([ - (r"/files/(.*)", AuthenticatedFileHandler, {'path' : nbm.notebook_dir}), + cm = settings['contents_manager'] + if hasattr(cm, 'root_dir'): + handlers.append( + (r"/files/(.*)", AuthenticatedFileHandler, {'path' : cm.root_dir}), + ) + handlers.append( (r"/nbextensions/(.*)", FileFindHandler, {'path' : settings['nbextensions_path']}), - ]) + ) # prepend base_url onto the patterns that we match new_handlers = [] for handler in handlers: @@ -263,11 +265,6 @@ flags['no-mathjax']=( """ ) -# Add notebook manager flags -flags.update(boolean_flag('script', 'FileNotebookManager.save_script', - 'Auto-save a .py script everytime the .ipynb notebook is saved', - 'Do not auto-save .py scripts for every notebook')) - aliases = dict(base_aliases) aliases.update({ @@ -302,7 +299,7 @@ class NotebookApp(BaseIPythonApplication): classes = [ KernelManager, ProfileDir, Session, MappingKernelManager, - NotebookManager, FileNotebookManager, NotebookNotary, + ContentsManager, FileContentsManager, NotebookNotary, ] flags = Dict(flags) aliases = Dict(aliases) @@ -557,7 +554,7 @@ class NotebookApp(BaseIPythonApplication): else: self.log.info("Using MathJax: %s", new) - notebook_manager_class = DottedObjectName('IPython.html.services.notebooks.filenbmanager.FileNotebookManager', + contents_manager_class = DottedObjectName('IPython.html.services.contents.filemanager.FileContentsManager', config=True, help='The notebook manager class to use.' ) @@ -621,7 +618,7 @@ class NotebookApp(BaseIPythonApplication): raise TraitError("No such notebook dir: %r" % new) # setting App.notebook_dir implies setting notebook and kernel dirs as well - self.config.FileNotebookManager.notebook_dir = new + self.config.FileContentsManager.root_dir = new self.config.MappingKernelManager.root_dir = new @@ -658,12 +655,12 @@ class NotebookApp(BaseIPythonApplication): parent=self, log=self.log, kernel_argv=self.kernel_argv, connection_dir = self.profile_dir.security_dir, ) - kls = import_item(self.notebook_manager_class) - self.notebook_manager = kls(parent=self, log=self.log) + kls = import_item(self.contents_manager_class) + self.contents_manager = kls(parent=self, log=self.log) kls = import_item(self.session_manager_class) self.session_manager = kls(parent=self, log=self.log, kernel_manager=self.kernel_manager, - notebook_manager=self.notebook_manager) + contents_manager=self.contents_manager) kls = import_item(self.cluster_manager_class) self.cluster_manager = kls(parent=self, log=self.log) self.cluster_manager.update_profiles() @@ -688,7 +685,7 @@ class NotebookApp(BaseIPythonApplication): self.webapp_settings['allow_credentials'] = self.allow_credentials self.web_app = NotebookWebApplication( - self, self.kernel_manager, self.notebook_manager, + self, self.kernel_manager, self.contents_manager, self.cluster_manager, self.session_manager, self.kernel_spec_manager, self.log, self.base_url, self.webapp_settings, self.jinja_environment_options @@ -838,7 +835,7 @@ class NotebookApp(BaseIPythonApplication): def notebook_info(self): "Return the current working directory and the server url information" - info = self.notebook_manager.info_string() + "\n" + info = self.contents_manager.info_string() + "\n" info += "%d active kernels \n" % len(self.kernel_manager._kernels) return info + "The IPython Notebook is running at: %s" % self.display_url diff --git a/IPython/html/services/contents/filenbmanager.py b/IPython/html/services/contents/filemanager.py similarity index 68% rename from IPython/html/services/contents/filenbmanager.py rename to IPython/html/services/contents/filemanager.py index 118f02d..2ddca80 100644 --- a/IPython/html/services/contents/filenbmanager.py +++ b/IPython/html/services/contents/filemanager.py @@ -1,4 +1,4 @@ -"""A notebook manager that uses the local file system for storage.""" +"""A contents manager that uses the local file system for storage.""" # Copyright (c) IPython Development Team. # Distributed under the terms of the Modified BSD License. @@ -10,7 +10,7 @@ import shutil from tornado import web -from .nbmanager import NotebookManager +from .manager import ContentsManager from IPython.nbformat import current from IPython.utils.path import ensure_dir_exists from IPython.utils.traitlets import Unicode, Bool, TraitError @@ -22,31 +22,19 @@ def sort_key(item): """Case-insensitive sorting.""" return item['name'].lower() -#----------------------------------------------------------------------------- -# Classes -#----------------------------------------------------------------------------- -class FileNotebookManager(NotebookManager): +class FileContentsManager(ContentsManager): - save_script = Bool(False, config=True, - help="""Automatically create a Python script when saving the notebook. + root_dir = Unicode(getcwd(), config=True) - For easier use of import, %run and %load across notebooks, a - .py script will be created next to any - .ipynb on each save. This can also be set with the - short `--script` flag. - """ - ) - notebook_dir = Unicode(getcwd(), config=True) - - def _notebook_dir_changed(self, name, old, new): - """Do a bit of validation of the notebook dir.""" + def _root_dir_changed(self, name, old, new): + """Do a bit of validation of the root_dir.""" if not os.path.isabs(new): # If we receive a non-absolute path, make it absolute. - self.notebook_dir = os.path.abspath(new) + self.root_dir = os.path.abspath(new) return if not os.path.exists(new) or not os.path.isdir(new): - raise TraitError("notebook dir %r is not a directory" % new) + raise TraitError("%r is not a directory" % new) checkpoint_dir = Unicode('.ipynb_checkpoints', config=True, help="""The directory name in which to keep notebook checkpoints @@ -68,14 +56,13 @@ class FileNotebookManager(NotebookManager): except OSError as e: self.log.debug("copystat on %s failed", dest, exc_info=True) - def get_notebook_names(self, path=''): - """List all notebook names in the notebook dir and path.""" + def get_names(self, path=''): + """List all filenames in the path (relative to root_dir).""" path = path.strip('/') if not os.path.isdir(self._get_os_path(path=path)): raise web.HTTPError(404, 'Directory not found: ' + path) - names = glob.glob(self._get_os_path('*'+self.filename_ext, path)) - names = [os.path.basename(name) - for name in names] + names = glob.glob(self._get_os_path('*', path)) + names = [ os.path.basename(name) for name in names if os.path.isfile(name)] return names def path_exists(self, path): @@ -85,7 +72,7 @@ class FileNotebookManager(NotebookManager): ---------- path : string The path to check. This is an API path (`/` separated, - relative to base notebook-dir). + relative to root_dir). Returns ------- @@ -103,7 +90,7 @@ class FileNotebookManager(NotebookManager): ---------- path : string The path to check. This is an API path (`/` separated, - relative to base notebook-dir). + relative to root_dir). Returns ------- @@ -113,40 +100,38 @@ class FileNotebookManager(NotebookManager): """ path = path.strip('/') os_path = self._get_os_path(path=path) - return is_hidden(os_path, self.notebook_dir) + return is_hidden(os_path, self.root_dir) def _get_os_path(self, name=None, path=''): - """Given a notebook name and a URL path, return its file system + """Given a filename and a URL path, return its file system path. Parameters ---------- name : string - The name of a notebook file with the .ipynb extension + A filename path : string The relative URL path (with '/' as separator) to the named - notebook. + file. Returns ------- path : string - A file system path that combines notebook_dir (location where - server started), the relative path, and the filename with the - current operating system's url. + API path to be evaluated relative to root_dir. """ if name is not None: path = path + '/' + name - return to_os_path(path, self.notebook_dir) + return to_os_path(path, self.root_dir) - def notebook_exists(self, name, path=''): - """Returns a True if the notebook exists. Else, returns False. + def file_exists(self, name, path=''): + """Returns a True if the file exists, else returns False. Parameters ---------- name : string - The name of the notebook you are checking. + The name of the file you are checking. path : string - The relative path to the notebook (with '/' as separator) + The relative path to the file's directory (with '/' as separator) Returns ------- @@ -164,14 +149,14 @@ class FileNotebookManager(NotebookManager): os_path = self._get_os_path('', path) if not os.path.isdir(os_path): raise web.HTTPError(404, u'directory does not exist: %r' % os_path) - elif is_hidden(os_path, self.notebook_dir): + elif is_hidden(os_path, self.root_dir): self.log.info("Refusing to serve hidden directory, via 404 Error") raise web.HTTPError(404, u'directory does not exist: %r' % os_path) dir_names = os.listdir(os_path) dirs = [] for name in dir_names: os_path = self._get_os_path(name, path) - if os.path.isdir(os_path) and not is_hidden(os_path, self.notebook_dir)\ + if os.path.isdir(os_path) and not is_hidden(os_path, self.root_dir)\ and self.should_list(name): try: model = self.get_dir_model(name, path) @@ -201,7 +186,7 @@ class FileNotebookManager(NotebookManager): model['type'] = 'directory' return model - def list_notebooks(self, path): + def list_files(self, path): """Returns a list of dictionaries that are the standard model for all notebooks in the relative 'path'. @@ -217,13 +202,13 @@ class FileNotebookManager(NotebookManager): a list of the notebook models without 'content' """ path = path.strip('/') - notebook_names = self.get_notebook_names(path) - notebooks = [self.get_notebook(name, path, content=False) - for name in notebook_names if self.should_list(name)] + names = self.get_names(path) + notebooks = [self.get(name, path, content=False) + for name in names if self.should_list(name)] notebooks = sorted(notebooks, key=sort_key) return notebooks - def get_notebook(self, name, path='', content=True): + def get(self, name, path='', content=True): """ Takes a path and name for a notebook and returns its model Parameters @@ -241,7 +226,7 @@ class FileNotebookManager(NotebookManager): dict in the model as well. """ path = path.strip('/') - if not self.notebook_exists(name=name, path=path): + if not self.file_exists(name=name, path=path): raise web.HTTPError(404, u'Notebook does not exist: %s' % name) os_path = self._get_os_path(name, path) info = os.stat(os_path) @@ -264,7 +249,7 @@ class FileNotebookManager(NotebookManager): model['content'] = nb return model - def save_notebook(self, model, name='', path=''): + def save(self, model, name='', path=''): """Save the notebook model and return the model with no content.""" path = path.strip('/') @@ -272,14 +257,14 @@ class FileNotebookManager(NotebookManager): raise web.HTTPError(400, u'No notebook JSON data provided') # One checkpoint should always exist - if self.notebook_exists(name, path) and not self.list_checkpoints(name, path): + if self.file_exists(name, path) and not self.list_checkpoints(name, path): self.create_checkpoint(name, path) new_path = model.get('path', path).strip('/') new_name = model.get('name', name) if path != new_path or name != new_name: - self.rename_notebook(name, path, new_name, new_path) + self.rename(name, path, new_name, new_path) # Save the notebook file os_path = self._get_os_path(new_name, new_path) @@ -296,35 +281,25 @@ class FileNotebookManager(NotebookManager): except Exception as e: raise web.HTTPError(400, u'Unexpected error while autosaving notebook: %s %s' % (os_path, e)) - # Save .py script as well - if self.save_script: - py_path = os.path.splitext(os_path)[0] + '.py' - self.log.debug("Writing script %s", py_path) - try: - with io.open(py_path, 'w', encoding='utf-8') as f: - current.write(nb, f, u'py') - except Exception as e: - raise web.HTTPError(400, u'Unexpected error while saving notebook as script: %s %s' % (py_path, e)) - - model = self.get_notebook(new_name, new_path, content=False) + model = self.get(new_name, new_path, content=False) return model - def update_notebook(self, model, name, path=''): - """Update the notebook's path and/or name""" + def update(self, model, name, path=''): + """Update the file's path and/or name""" path = path.strip('/') new_name = model.get('name', name) new_path = model.get('path', path).strip('/') if path != new_path or name != new_name: - self.rename_notebook(name, path, new_name, new_path) - model = self.get_notebook(new_name, new_path, content=False) + self.rename(name, path, new_name, new_path) + model = self.get(new_name, new_path, content=False) return model - def delete_notebook(self, name, path=''): - """Delete notebook by name and path.""" + def delete(self, name, path=''): + """Delete file by name and path.""" path = path.strip('/') 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) + raise web.HTTPError(404, u'File does not exist: %s' % os_path) # clear checkpoints for checkpoint in self.list_checkpoints(name, path): @@ -334,11 +309,11 @@ class FileNotebookManager(NotebookManager): self.log.debug("Unlinking checkpoint %s", cp_path) os.unlink(cp_path) - self.log.debug("Unlinking notebook %s", os_path) + self.log.debug("Unlinking file %s", os_path) os.unlink(os_path) - def rename_notebook(self, old_name, old_path, new_name, new_path): - """Rename a notebook.""" + def rename(self, old_name, old_path, new_name, new_path): + """Rename a file.""" old_path = old_path.strip('/') new_path = new_path.strip('/') if new_name == old_name and new_path == old_path: @@ -350,17 +325,12 @@ class FileNotebookManager(NotebookManager): # Should we proceed with the move? if os.path.isfile(new_os_path): raise web.HTTPError(409, u'Notebook with name already exists: %s' % new_os_path) - if self.save_script: - 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) - # Move the notebook file + # Move the file try: shutil.move(old_os_path, new_os_path) except Exception as e: - raise web.HTTPError(500, u'Unknown error renaming notebook: %s %s' % (old_os_path, e)) + raise web.HTTPError(500, u'Unknown error renaming file: %s %s' % (old_os_path, e)) # Move the checkpoints old_checkpoints = self.list_checkpoints(old_name, old_path) @@ -372,20 +342,16 @@ class FileNotebookManager(NotebookManager): self.log.debug("Renaming checkpoint %s -> %s", old_cp_path, new_cp_path) shutil.move(old_cp_path, new_cp_path) - # Move the .py script - if self.save_script: - shutil.move(old_py_path, new_py_path) - # Checkpoint-related utilities def get_checkpoint_path(self, checkpoint_id, name, path=''): """find the path to a checkpoint""" path = path.strip('/') - basename, _ = os.path.splitext(name) + basename, ext = os.path.splitext(name) filename = u"{name}-{checkpoint_id}{ext}".format( name=basename, checkpoint_id=checkpoint_id, - ext=self.filename_ext, + ext=ext, ) os_path = self._get_os_path(path=path) cp_dir = os.path.join(os_path, self.checkpoint_dir) @@ -408,22 +374,22 @@ class FileNotebookManager(NotebookManager): # public checkpoint API def create_checkpoint(self, name, path=''): - """Create a checkpoint from the current state of a notebook""" + """Create a checkpoint from the current state of a file""" path = path.strip('/') - nb_path = self._get_os_path(name, path) + src_path = self._get_os_path(name, path) # only the one checkpoint ID: checkpoint_id = u"checkpoint" cp_path = self.get_checkpoint_path(checkpoint_id, name, path) self.log.debug("creating checkpoint for notebook %s", name) - self._copy(nb_path, cp_path) + self._copy(src_path, cp_path) # return the checkpoint info return self.get_checkpoint_model(checkpoint_id, name, path) def list_checkpoints(self, name, path=''): - """list the checkpoints for a given notebook + """list the checkpoints for a given file - This notebook manager currently only supports one checkpoint per notebook. + This contents manager currently only supports one checkpoint per file. """ path = path.strip('/') checkpoint_id = "checkpoint" @@ -435,36 +401,37 @@ class FileNotebookManager(NotebookManager): def restore_checkpoint(self, checkpoint_id, name, path=''): - """restore a notebook to a checkpointed state""" + """restore a file to a checkpointed state""" path = path.strip('/') - self.log.info("restoring Notebook %s from checkpoint %s", name, checkpoint_id) + self.log.info("restoring %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) if not os.path.isfile(cp_path): self.log.debug("checkpoint file does not exist: %s", cp_path) raise web.HTTPError(404, - u'Notebook checkpoint does not exist: %s-%s' % (name, checkpoint_id) + u'checkpoint does not exist: %s-%s' % (name, checkpoint_id) ) # ensure notebook is readable (never restore from an unreadable notebook) - with io.open(cp_path, 'r', encoding='utf-8') as f: - current.read(f, u'json') + if cp_path.endswith('.ipynb'): + with io.open(cp_path, 'r', encoding='utf-8') as f: + current.read(f, u'json') self._copy(cp_path, nb_path) self.log.debug("copying %s -> %s", cp_path, nb_path) def delete_checkpoint(self, checkpoint_id, name, path=''): - """delete a notebook's checkpoint""" + """delete a file's checkpoint""" path = path.strip('/') cp_path = self.get_checkpoint_path(checkpoint_id, name, path) if not os.path.isfile(cp_path): raise web.HTTPError(404, - u'Notebook checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id) + u'Checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id) ) self.log.debug("unlinking %s", cp_path) os.unlink(cp_path) def info_string(self): - return "Serving notebooks from local directory: %s" % self.notebook_dir + return "Serving notebooks from local directory: %s" % self.root_dir def get_kernel_path(self, name, path='', model=None): - """ Return the path to start kernel in """ - return os.path.join(self.notebook_dir, path) + """Return the initial working dir a kernel associated with a given notebook""" + return os.path.join(self.root_dir, path) diff --git a/IPython/html/services/contents/handlers.py b/IPython/html/services/contents/handlers.py index 5647ce7..878b8e7 100644 --- a/IPython/html/services/contents/handlers.py +++ b/IPython/html/services/contents/handlers.py @@ -1,20 +1,7 @@ -"""Tornado handlers for the notebooks web service. +"""Tornado handlers for the contents web service.""" -Authors: - -* Brian Granger -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. import json @@ -27,33 +14,29 @@ from IPython.html.base.handlers import (IPythonHandler, json_errors, notebook_path_regex, path_regex, notebook_name_regex) -#----------------------------------------------------------------------------- -# Notebook web service handlers -#----------------------------------------------------------------------------- - -class NotebookHandler(IPythonHandler): +class ContentsHandler(IPythonHandler): SUPPORTED_METHODS = (u'GET', u'PUT', u'PATCH', u'POST', u'DELETE') - def notebook_location(self, name, path=''): - """Return the full URL location of a notebook based. + def location_url(self, name, path=''): + """Return the full URL location of a file. Parameters ---------- name : unicode - The base name of the notebook, such as "foo.ipynb". + The base name of the file, such as "foo.ipynb". path : unicode - The URL path of the notebook. + The API path of the file, such as "foo/bar". """ return url_escape(url_path_join( - self.base_url, 'api', 'notebooks', path, name + self.base_url, 'api', 'contents', path, name )) def _finish_model(self, model, location=True): """Finish a JSON request with a model, setting relevant headers, etc.""" if location: - location = self.notebook_location(model['name'], model['path']) + location = self.location_url(model['name'], model['path']) self.set_header('Location', location) self.set_header('Last-Modified', model['last_modified']) self.finish(json.dumps(model, default=date_default)) @@ -61,68 +44,68 @@ class NotebookHandler(IPythonHandler): @web.authenticated @json_errors def get(self, path='', name=None): - """Return a Notebook or list of notebooks. + """Return a file or list of files. - * GET with path and no notebook name lists notebooks in a directory - * GET with path and notebook name returns notebook JSON + * GET with path and no filename lists files in a directory + * GET with path and filename returns file contents model """ - nbm = self.notebook_manager - # Check to see if a notebook name was given + cm = self.contents_manager + # Check to see if a filename was given if name is None: # TODO: Remove this after we create the contents web service and directories are # no longer listed by the notebook web service. This should only handle notebooks # and not directories. - dirs = nbm.list_dirs(path) - notebooks = [] + dirs = cm.list_dirs(path) + files = [] index = [] - for nb in nbm.list_notebooks(path): + for nb in cm.list_files(path): if nb['name'].lower() == 'index.ipynb': index.append(nb) else: - notebooks.append(nb) - notebooks = index + dirs + notebooks - self.finish(json.dumps(notebooks, default=date_default)) + files.append(nb) + files = index + dirs + files + self.finish(json.dumps(files, default=date_default)) return # get and return notebook representation - model = nbm.get_notebook(name, path) + model = cm.get(name, path) self._finish_model(model, location=False) @web.authenticated @json_errors def patch(self, path='', name=None): """PATCH renames a notebook without re-uploading content.""" - nbm = self.notebook_manager + cm = self.contents_manager if name is None: - raise web.HTTPError(400, u'Notebook name missing') + raise web.HTTPError(400, u'Filename missing') model = self.get_json_body() if model is None: raise web.HTTPError(400, u'JSON body missing') - model = nbm.update_notebook(model, name, path) + model = cm.update(model, name, path) self._finish_model(model) - def _copy_notebook(self, copy_from, path, copy_to=None): - """Copy a notebook in path, optionally specifying the new name. + def _copy(self, copy_from, path, copy_to=None): + """Copy a file in path, optionally specifying the new name. Only support copying within the same directory. """ - self.log.info(u"Copying notebook from %s/%s to %s/%s", + self.log.info(u"Copying from %s/%s to %s/%s", path, copy_from, path, copy_to or '', ) - model = self.notebook_manager.copy_notebook(copy_from, copy_to, path) + model = self.contents_manager.copy(copy_from, copy_to, path) self.set_status(201) self._finish_model(model) - def _upload_notebook(self, model, path, name=None): - """Upload a notebook + def _upload(self, model, path, name=None): + """Upload a file If name specified, create it in path/name. """ - self.log.info(u"Uploading notebook to %s/%s", path, name or '') + self.log.info(u"Uploading file to %s/%s", path, name or '') if name: model['name'] = name - model = self.notebook_manager.create_notebook(model, path) + model = self.contents_manager.create_notebook(model, path) self.set_status(201) self._finish_model(model) @@ -135,14 +118,14 @@ class NotebookHandler(IPythonHandler): model = {} if name: model['name'] = name - model = self.notebook_manager.create_notebook(model, path=path) + model = self.contents_manager.create_notebook(model, path=path) self.set_status(201) self._finish_model(model) - def _save_notebook(self, model, path, name): - """Save an existing notebook.""" - self.log.info(u"Saving notebook at %s/%s", path, name) - model = self.notebook_manager.save_notebook(model, name, path) + def _save(self, model, path, name): + """Save an existing file.""" + self.log.info(u"Saving file at %s/%s", path, name) + model = self.contents_manager.save(model, name, path) if model['path'] != path.strip('/') or model['name'] != name: # a rename happened, set Location header location = True @@ -157,10 +140,10 @@ class NotebookHandler(IPythonHandler): POST creates new notebooks. The server always decides on the notebook name. - POST /api/notebooks/path + POST /api/contents/path New untitled notebook in path. If content specified, upload a notebook, otherwise start empty. - POST /api/notebooks/path?copy=OtherNotebook.ipynb + POST /api/contents/path?copy=OtherNotebook.ipynb New copy of OtherNotebook in path """ @@ -174,25 +157,25 @@ class NotebookHandler(IPythonHandler): if copy_from: if model.get('content'): raise web.HTTPError(400, "Can't upload and copy at the same time.") - self._copy_notebook(copy_from, path) + self._copy(copy_from, path) else: - self._upload_notebook(model, path) + self._upload(model, path) else: self._create_empty_notebook(path) @web.authenticated @json_errors def put(self, path='', name=None): - """Saves the notebook in the location specified by name and path. + """Saves the file in the location specified by name and path. PUT is very similar to POST, but the requester specifies the name, whereas with POST, the server picks the name. - PUT /api/notebooks/path/Name.ipynb + PUT /api/contents/path/Name.ipynb 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. - PUT /api/notebooks/path/Name.ipynb?copy=OtherNotebook.ipynb + PUT /api/contents/path/Name.ipynb?copy=OtherNotebook.ipynb Copy OtherNotebook to Name """ if name is None: @@ -204,34 +187,34 @@ class NotebookHandler(IPythonHandler): if copy_from: if model.get('content'): raise web.HTTPError(400, "Can't upload and copy at the same time.") - self._copy_notebook(copy_from, path, name) - elif self.notebook_manager.notebook_exists(name, path): - self._save_notebook(model, path, name) + self._copy(copy_from, path, name) + elif self.contents_manager.file_exists(name, path): + self._save(model, path, name) else: - self._upload_notebook(model, path, name) + self._upload(model, path, name) else: self._create_empty_notebook(path, name) @web.authenticated @json_errors def delete(self, path='', name=None): - """delete the notebook in the given notebook path""" - nbm = self.notebook_manager - nbm.delete_notebook(name, path) + """delete a file in the given path""" + cm = self.contents_manager + cm.delete(name, path) self.set_status(204) self.finish() -class NotebookCheckpointsHandler(IPythonHandler): +class CheckpointsHandler(IPythonHandler): SUPPORTED_METHODS = ('GET', 'POST') @web.authenticated @json_errors def get(self, path='', name=None): - """get lists checkpoints for a notebook""" - nbm = self.notebook_manager - checkpoints = nbm.list_checkpoints(name, path) + """get lists checkpoints for a file""" + cm = self.contents_manager + checkpoints = cm.list_checkpoints(name, path) data = json.dumps(checkpoints, default=date_default) self.finish(data) @@ -239,35 +222,35 @@ class NotebookCheckpointsHandler(IPythonHandler): @json_errors def post(self, path='', name=None): """post creates a new checkpoint""" - nbm = self.notebook_manager - checkpoint = nbm.create_checkpoint(name, path) + cm = self.contents_manager + checkpoint = cm.create_checkpoint(name, path) data = json.dumps(checkpoint, default=date_default) - location = url_path_join(self.base_url, 'api/notebooks', + location = url_path_join(self.base_url, 'api/contents', path, name, 'checkpoints', checkpoint['id']) self.set_header('Location', url_escape(location)) self.set_status(201) self.finish(data) -class ModifyNotebookCheckpointsHandler(IPythonHandler): +class ModifyCheckpointsHandler(IPythonHandler): SUPPORTED_METHODS = ('POST', 'DELETE') @web.authenticated @json_errors def post(self, path, name, checkpoint_id): - """post restores a notebook from a checkpoint""" - nbm = self.notebook_manager - nbm.restore_checkpoint(checkpoint_id, name, path) + """post restores a file from a checkpoint""" + cm = self.contents_manager + cm.restore_checkpoint(checkpoint_id, name, path) self.set_status(204) self.finish() @web.authenticated @json_errors def delete(self, path, name, checkpoint_id): - """delete clears a checkpoint for a given notebook""" - nbm = self.notebook_manager - nbm.delete_checkpoint(checkpoint_id, name, path) + """delete clears a checkpoint for a given file""" + cm = self.contents_manager + cm.delete_checkpoint(checkpoint_id, name, path) self.set_status(204) self.finish() @@ -279,9 +262,9 @@ class ModifyNotebookCheckpointsHandler(IPythonHandler): _checkpoint_id_regex = r"(?P[\w-]+)" default_handlers = [ - (r"/api/notebooks%s/checkpoints" % notebook_path_regex, NotebookCheckpointsHandler), - (r"/api/notebooks%s/checkpoints/%s" % (notebook_path_regex, _checkpoint_id_regex), - ModifyNotebookCheckpointsHandler), - (r"/api/notebooks%s" % notebook_path_regex, NotebookHandler), - (r"/api/notebooks%s" % path_regex, NotebookHandler), + (r"/api/contents%s/checkpoints" % notebook_path_regex, CheckpointsHandler), + (r"/api/contents%s/checkpoints/%s" % (notebook_path_regex, _checkpoint_id_regex), + ModifyCheckpointsHandler), + (r"/api/contents%s" % notebook_path_regex, ContentsHandler), + (r"/api/contents%s" % path_regex, ContentsHandler), ] diff --git a/IPython/html/services/contents/nbmanager.py b/IPython/html/services/contents/manager.py similarity index 70% rename from IPython/html/services/contents/nbmanager.py rename to IPython/html/services/contents/manager.py index 5f8bd97..32f3677 100644 --- a/IPython/html/services/contents/nbmanager.py +++ b/IPython/html/services/contents/manager.py @@ -1,21 +1,7 @@ -"""A base class notebook manager. +"""A base class for contents managers.""" -Authors: - -* Brian Granger -* Zach Sailer -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2011 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. from fnmatch import fnmatch import itertools @@ -25,13 +11,8 @@ from IPython.config.configurable import LoggingConfigurable from IPython.nbformat import current, sign from IPython.utils.traitlets import Instance, Unicode, List -#----------------------------------------------------------------------------- -# Classes -#----------------------------------------------------------------------------- -class NotebookManager(LoggingConfigurable): - - filename_ext = Unicode(u'.ipynb') +class ContentsManager(LoggingConfigurable): notary = Instance(sign.NotebookNotary) def _notary_default(self): @@ -41,7 +22,7 @@ class NotebookManager(LoggingConfigurable): Glob patterns to hide in file and directory listings. """) - # NotebookManager API part 1: methods that must be + # ContentsManager API part 1: methods that must be # implemented in subclasses. def path_exists(self, path): @@ -68,7 +49,7 @@ class NotebookManager(LoggingConfigurable): ---------- path : string The path to check. This is an API path (`/` separated, - relative to base notebook-dir). + relative to root dir). Returns ------- @@ -78,7 +59,7 @@ class NotebookManager(LoggingConfigurable): """ raise NotImplementedError - def notebook_exists(self, name, path=''): + def file_exists(self, name, path=''): """Returns a True if the notebook exists. Else, returns False. Parameters @@ -114,12 +95,10 @@ class NotebookManager(LoggingConfigurable): """ raise NotImplementedError('must be implemented in a subclass') - def list_notebooks(self, path=''): - """Return a list of notebook dicts without content. - - This returns a list of dicts, each of the form:: + def list_files(self, path=''): + """Return a list of contents dicts without content. - dict(notebook_id=notebook,name=name) + This returns a list of dicts This list of dicts should be sorted by name:: @@ -127,19 +106,19 @@ class NotebookManager(LoggingConfigurable): """ raise NotImplementedError('must be implemented in a subclass') - def get_notebook(self, name, path='', content=True): + def get_model(self, name, path='', content=True): """Get the notebook model with or without content.""" raise NotImplementedError('must be implemented in a subclass') - def save_notebook(self, model, name, path=''): + def save(self, model, name, path=''): """Save the notebook and return the model with no content.""" raise NotImplementedError('must be implemented in a subclass') - def update_notebook(self, model, name, path=''): + def update(self, model, name, path=''): """Update the notebook and return the model with no content.""" raise NotImplementedError('must be implemented in a subclass') - def delete_notebook(self, name, path=''): + def delete(self, name, path=''): """Delete notebook by name and path.""" raise NotImplementedError('must be implemented in a subclass') @@ -165,34 +144,34 @@ class NotebookManager(LoggingConfigurable): def info_string(self): return "Serving notebooks" - # NotebookManager API part 2: methods that have useable default + # ContentsManager API part 2: methods that have useable default # implementations, but can be overridden in subclasses. def get_kernel_path(self, name, path='', model=None): """ Return the path to start kernel in """ return path - def increment_filename(self, basename, path=''): - """Increment a notebook filename without the .ipynb to make it unique. + def increment_filename(self, filename, path=''): + """Increment a filename until it is unique. Parameters ---------- - basename : unicode - The name of a notebook without the ``.ipynb`` file extension. + filename : unicode + The name of a file, including extension path : unicode The URL path of the notebooks directory Returns ------- name : unicode - A notebook name (with the .ipynb extension) that starts - with basename and does not refer to any existing notebook. + A filename that is unique, based on the input filename. """ path = path.strip('/') + basename, ext = os.path.splitext(filename) for i in itertools.count(): name = u'{basename}{i}{ext}'.format(basename=basename, i=i, - ext=self.filename_ext) - if not self.notebook_exists(name, path): + ext=ext) + if not self.file_exists(name, path): break return name @@ -205,24 +184,25 @@ class NotebookManager(LoggingConfigurable): metadata = current.new_metadata(name=u'') model['content'] = current.new_notebook(metadata=metadata) if 'name' not in model: - model['name'] = self.increment_filename('Untitled', path) + model['name'] = self.increment_filename('Untitled.ipynb', path) model['path'] = path - model = self.save_notebook(model, model['name'], model['path']) + model = self.save(model, model['name'], model['path']) return model - def copy_notebook(self, from_name, to_name=None, path=''): - """Copy an existing notebook and return its new model. + def copy(self, from_name, to_name=None, path=''): + """Copy an existing file and return its new model. If to_name not specified, increment `from_name-Copy#.ipynb`. """ path = path.strip('/') - model = self.get_notebook(from_name, path) + model = self.get(from_name, path) if not to_name: - base = os.path.splitext(from_name)[0] + '-Copy' - to_name = self.increment_filename(base, path) + base, ext = os.path.splitext(from_name) + copy_name = u'{0}-Copy{1}'.format(base, ext) + to_name = self.increment_filename(copy_name, path) model['name'] = to_name - model = self.save_notebook(model, to_name, path) + model = self.save(model, to_name, path) return model def log_info(self): @@ -238,11 +218,11 @@ class NotebookManager(LoggingConfigurable): path : string The notebook's directory """ - model = self.get_notebook(name, path) + model = self.get(name, path) nb = model['content'] self.log.warn("Trusting notebook %s/%s", path, name) self.notary.mark_cells(nb, True) - self.save_notebook(model, name, path) + self.save(model, name, path) def check_and_sign(self, nb, name, path=''): """Check for trusted cells, and sign the notebook. diff --git a/IPython/html/services/contents/tests/test_notebooks_api.py b/IPython/html/services/contents/tests/test_contents_api.py similarity index 83% rename from IPython/html/services/contents/tests/test_notebooks_api.py rename to IPython/html/services/contents/tests/test_contents_api.py index 74c9a25..256b234 100644 --- a/IPython/html/services/contents/tests/test_notebooks_api.py +++ b/IPython/html/services/contents/tests/test_contents_api.py @@ -1,5 +1,5 @@ # coding: utf-8 -"""Test the notebooks webservice API.""" +"""Test the contents webservice API.""" import io import json @@ -30,14 +30,14 @@ def dirs_only(nb_list): return [x for x in nb_list if x['type']=='directory'] -class NBAPI(object): - """Wrapper for notebook API calls.""" +class API(object): + """Wrapper for contents API calls.""" def __init__(self, base_url): self.base_url = base_url def _req(self, verb, path, body=None): response = requests.request(verb, - url_path_join(self.base_url, 'api/notebooks', path), + url_path_join(self.base_url, 'api/contents', path), data=body, ) response.raise_for_status() @@ -127,7 +127,7 @@ class APITest(NotebookTestBase): nb = new_notebook(name=name) write(nb, f, format='ipynb') - self.nb_api = NBAPI(self.base_url()) + self.api = API(self.base_url()) def tearDown(self): nbdir = self.notebook_dir.name @@ -139,48 +139,48 @@ class APITest(NotebookTestBase): os.unlink(pjoin(nbdir, 'inroot.ipynb')) def test_list_notebooks(self): - nbs = notebooks_only(self.nb_api.list().json()) + nbs = notebooks_only(self.api.list().json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'inroot.ipynb') - nbs = notebooks_only(self.nb_api.list('/Directory with spaces in/').json()) + nbs = notebooks_only(self.api.list('/Directory with spaces in/').json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'inspace.ipynb') - nbs = notebooks_only(self.nb_api.list(u'/unicodé/').json()) + nbs = notebooks_only(self.api.list(u'/unicodé/').json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'innonascii.ipynb') self.assertEqual(nbs[0]['path'], u'unicodé') - nbs = notebooks_only(self.nb_api.list('/foo/bar/').json()) + nbs = notebooks_only(self.api.list('/foo/bar/').json()) self.assertEqual(len(nbs), 1) self.assertEqual(nbs[0]['name'], 'baz.ipynb') self.assertEqual(nbs[0]['path'], 'foo/bar') - nbs = notebooks_only(self.nb_api.list('foo').json()) + nbs = notebooks_only(self.api.list('foo').json()) self.assertEqual(len(nbs), 4) nbnames = { normalize('NFC', n['name']) for n in nbs } expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodé.ipynb'] expected = { normalize('NFC', name) for name in expected } self.assertEqual(nbnames, expected) - nbs = notebooks_only(self.nb_api.list('ordering').json()) + nbs = notebooks_only(self.api.list('ordering').json()) nbnames = [n['name'] for n in nbs] expected = ['A.ipynb', 'b.ipynb', 'C.ipynb'] self.assertEqual(nbnames, expected) def test_list_dirs(self): - dirs = dirs_only(self.nb_api.list().json()) + dirs = dirs_only(self.api.list().json()) dir_names = {normalize('NFC', d['name']) for d in dirs} self.assertEqual(dir_names, self.top_level_dirs) # Excluding hidden dirs def test_list_nonexistant_dir(self): with assert_http_error(404): - self.nb_api.list('nonexistant') + self.api.list('nonexistant') def test_get_contents(self): for d, name in self.dirs_nbs: - nb = self.nb_api.read('%s.ipynb' % name, d+'/').json() + nb = self.api.read('%s.ipynb' % name, d+'/').json() self.assertEqual(nb['name'], u'%s.ipynb' % name) self.assertIn('content', nb) self.assertIn('metadata', nb['content']) @@ -188,12 +188,12 @@ class APITest(NotebookTestBase): # Name that doesn't exist - should be a 404 with assert_http_error(404): - self.nb_api.read('q.ipynb', 'foo') + self.api.read('q.ipynb', 'foo') def _check_nb_created(self, resp, name, path): self.assertEqual(resp.status_code, 201) location_header = py3compat.str_to_unicode(resp.headers['Location']) - self.assertEqual(location_header, url_escape(url_path_join(u'/api/notebooks', path, name))) + self.assertEqual(location_header, url_escape(url_path_join(u'/api/contents', path, name))) self.assertEqual(resp.json()['name'], name) assert os.path.isfile(pjoin( self.notebook_dir.name, @@ -202,28 +202,28 @@ class APITest(NotebookTestBase): )) def test_create_untitled(self): - resp = self.nb_api.create_untitled(path=u'å b') + resp = self.api.create_untitled(path=u'å b') self._check_nb_created(resp, 'Untitled0.ipynb', u'å b') # Second time - resp = self.nb_api.create_untitled(path=u'å b') + resp = self.api.create_untitled(path=u'å b') self._check_nb_created(resp, 'Untitled1.ipynb', u'å b') # And two directories down - resp = self.nb_api.create_untitled(path='foo/bar') + resp = self.api.create_untitled(path='foo/bar') self._check_nb_created(resp, 'Untitled0.ipynb', 'foo/bar') def test_upload_untitled(self): nb = new_notebook(name='Upload test') nbmodel = {'content': nb} - resp = self.nb_api.upload_untitled(path=u'å b', + resp = self.api.upload_untitled(path=u'å b', body=json.dumps(nbmodel)) self._check_nb_created(resp, 'Untitled0.ipynb', u'å b') def test_upload(self): nb = new_notebook(name=u'ignored') nbmodel = {'content': nb} - resp = self.nb_api.upload(u'Upload tést.ipynb', path=u'å b', + resp = self.api.upload(u'Upload tést.ipynb', path=u'å b', body=json.dumps(nbmodel)) self._check_nb_created(resp, u'Upload tést.ipynb', u'å b') @@ -233,48 +233,48 @@ class APITest(NotebookTestBase): nb.worksheets.append(ws) ws.cells.append(v2.new_code_cell(input='print("hi")')) nbmodel = {'content': nb} - resp = self.nb_api.upload(u'Upload tést.ipynb', path=u'å b', + resp = self.api.upload(u'Upload tést.ipynb', path=u'å b', body=json.dumps(nbmodel)) self._check_nb_created(resp, u'Upload tést.ipynb', u'å b') - resp = self.nb_api.read(u'Upload tést.ipynb', u'å b') + resp = self.api.read(u'Upload tést.ipynb', u'å b') data = resp.json() self.assertEqual(data['content']['nbformat'], current.nbformat) self.assertEqual(data['content']['orig_nbformat'], 2) def test_copy_untitled(self): - resp = self.nb_api.copy_untitled(u'ç d.ipynb', path=u'å b') + resp = self.api.copy_untitled(u'ç d.ipynb', path=u'å b') self._check_nb_created(resp, u'ç d-Copy0.ipynb', u'å b') def test_copy(self): - resp = self.nb_api.copy(u'ç d.ipynb', u'cøpy.ipynb', path=u'å b') + resp = self.api.copy(u'ç d.ipynb', u'cøpy.ipynb', path=u'å b') self._check_nb_created(resp, u'cøpy.ipynb', u'å b') def test_delete(self): for d, name in self.dirs_nbs: - resp = self.nb_api.delete('%s.ipynb' % name, d) + resp = self.api.delete('%s.ipynb' % name, d) self.assertEqual(resp.status_code, 204) for d in self.dirs + ['/']: - nbs = notebooks_only(self.nb_api.list(d).json()) + nbs = notebooks_only(self.api.list(d).json()) self.assertEqual(len(nbs), 0) def test_rename(self): - resp = self.nb_api.rename('a.ipynb', 'foo', 'z.ipynb') + resp = self.api.rename('a.ipynb', 'foo', 'z.ipynb') self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb') self.assertEqual(resp.json()['name'], 'z.ipynb') assert os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'z.ipynb')) - nbs = notebooks_only(self.nb_api.list('foo').json()) + nbs = notebooks_only(self.api.list('foo').json()) nbnames = set(n['name'] for n in nbs) self.assertIn('z.ipynb', nbnames) self.assertNotIn('a.ipynb', nbnames) def test_rename_existing(self): with assert_http_error(409): - self.nb_api.rename('a.ipynb', 'foo', 'b.ipynb') + self.api.rename('a.ipynb', 'foo', 'b.ipynb') def test_save(self): - resp = self.nb_api.read('a.ipynb', 'foo') + resp = self.api.read('a.ipynb', 'foo') nbcontent = json.loads(resp.text)['content'] nb = to_notebook_json(nbcontent) ws = new_worksheet() @@ -282,32 +282,32 @@ class APITest(NotebookTestBase): ws.cells.append(new_heading_cell(u'Created by test ³')) nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb} - resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) + resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb') with io.open(nbfile, 'r', encoding='utf-8') as f: newnb = read(f, format='ipynb') self.assertEqual(newnb.worksheets[0].cells[0].source, u'Created by test ³') - nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content'] + nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] newnb = to_notebook_json(nbcontent) self.assertEqual(newnb.worksheets[0].cells[0].source, u'Created by test ³') # Save and rename nbmodel= {'name': 'a2.ipynb', 'path':'foo/bar', 'content': nb} - resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) + resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) saved = resp.json() self.assertEqual(saved['name'], 'a2.ipynb') self.assertEqual(saved['path'], 'foo/bar') assert os.path.isfile(pjoin(self.notebook_dir.name,'foo','bar','a2.ipynb')) assert not os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'a.ipynb')) with assert_http_error(404): - self.nb_api.read('a.ipynb', 'foo') + self.api.read('a.ipynb', 'foo') def test_checkpoints(self): - resp = self.nb_api.read('a.ipynb', 'foo') - r = self.nb_api.new_checkpoint('a.ipynb', 'foo') + resp = self.api.read('a.ipynb', 'foo') + r = self.api.new_checkpoint('a.ipynb', 'foo') self.assertEqual(r.status_code, 201) cp1 = r.json() self.assertEqual(set(cp1), {'id', 'last_modified'}) @@ -322,25 +322,25 @@ class APITest(NotebookTestBase): ws.cells.append(hcell) # Save nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb} - resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) + resp = self.api.save('a.ipynb', path='foo', body=json.dumps(nbmodel)) # List checkpoints - cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json() + cps = self.api.get_checkpoints('a.ipynb', 'foo').json() self.assertEqual(cps, [cp1]) - nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content'] + nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] nb = to_notebook_json(nbcontent) self.assertEqual(nb.worksheets[0].cells[0].source, 'Created by test') # Restore cp1 - r = self.nb_api.restore_checkpoint('a.ipynb', 'foo', cp1['id']) + r = self.api.restore_checkpoint('a.ipynb', 'foo', cp1['id']) self.assertEqual(r.status_code, 204) - nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content'] + nbcontent = self.api.read('a.ipynb', 'foo').json()['content'] nb = to_notebook_json(nbcontent) self.assertEqual(nb.worksheets, []) # Delete cp1 - r = self.nb_api.delete_checkpoint('a.ipynb', 'foo', cp1['id']) + r = self.api.delete_checkpoint('a.ipynb', 'foo', cp1['id']) self.assertEqual(r.status_code, 204) - cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json() + cps = self.api.get_checkpoints('a.ipynb', 'foo').json() self.assertEqual(cps, []) diff --git a/IPython/html/services/contents/tests/test_nbmanager.py b/IPython/html/services/contents/tests/test_manager.py similarity index 59% rename from IPython/html/services/contents/tests/test_nbmanager.py rename to IPython/html/services/contents/tests/test_manager.py index c4b85b9..8ad9efa 100644 --- a/IPython/html/services/contents/tests/test_nbmanager.py +++ b/IPython/html/services/contents/tests/test_manager.py @@ -15,59 +15,59 @@ from IPython.utils.tempdir import TemporaryDirectory from IPython.utils.traitlets import TraitError from IPython.html.utils import url_path_join -from ..filenbmanager import FileNotebookManager -from ..nbmanager import NotebookManager +from ..filemanager import FileContentsManager +from ..manager import ContentsManager -class TestFileNotebookManager(TestCase): +class TestFileContentsManager(TestCase): - def test_nb_dir(self): + def test_root_dir(self): with TemporaryDirectory() as td: - fm = FileNotebookManager(notebook_dir=td) - self.assertEqual(fm.notebook_dir, td) + fm = FileContentsManager(root_dir=td) + self.assertEqual(fm.root_dir, td) - def test_missing_nb_dir(self): + def test_missing_root_dir(self): with TemporaryDirectory() as td: - nbdir = os.path.join(td, 'notebook', 'dir', 'is', 'missing') - self.assertRaises(TraitError, FileNotebookManager, notebook_dir=nbdir) + root = os.path.join(td, 'notebook', 'dir', 'is', 'missing') + self.assertRaises(TraitError, FileContentsManager, root_dir=root) - def test_invalid_nb_dir(self): + def test_invalid_root_dir(self): with NamedTemporaryFile() as tf: - self.assertRaises(TraitError, FileNotebookManager, notebook_dir=tf.name) + self.assertRaises(TraitError, FileContentsManager, root_dir=tf.name) def test_get_os_path(self): # full filesystem path should be returned with correct operating system # separators. with TemporaryDirectory() as td: - nbdir = td - fm = FileNotebookManager(notebook_dir=nbdir) + root = td + fm = FileContentsManager(root_dir=root) path = fm._get_os_path('test.ipynb', '/path/to/notebook/') rel_path_list = '/path/to/notebook/test.ipynb'.split('/') - fs_path = os.path.join(fm.notebook_dir, *rel_path_list) + fs_path = os.path.join(fm.root_dir, *rel_path_list) self.assertEqual(path, fs_path) - fm = FileNotebookManager(notebook_dir=nbdir) + fm = FileContentsManager(root_dir=root) path = fm._get_os_path('test.ipynb') - fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') + fs_path = os.path.join(fm.root_dir, 'test.ipynb') self.assertEqual(path, fs_path) - fm = FileNotebookManager(notebook_dir=nbdir) + fm = FileContentsManager(root_dir=root) path = fm._get_os_path('test.ipynb', '////') - fs_path = os.path.join(fm.notebook_dir, 'test.ipynb') + fs_path = os.path.join(fm.root_dir, 'test.ipynb') self.assertEqual(path, fs_path) def test_checkpoint_subdir(self): subd = u'sub ∂ir' cp_name = 'test-cp.ipynb' with TemporaryDirectory() as td: - nbdir = td + root = td os.mkdir(os.path.join(td, subd)) - fm = FileNotebookManager(notebook_dir=nbdir) + fm = FileContentsManager(root_dir=root) cp_dir = fm.get_checkpoint_path('cp', 'test.ipynb', '/') cp_subdir = fm.get_checkpoint_path('cp', 'test.ipynb', '/%s/' % subd) self.assertNotEqual(cp_dir, cp_subdir) - self.assertEqual(cp_dir, os.path.join(nbdir, fm.checkpoint_dir, cp_name)) - self.assertEqual(cp_subdir, os.path.join(nbdir, subd, fm.checkpoint_dir, cp_name)) + self.assertEqual(cp_dir, os.path.join(root, fm.checkpoint_dir, cp_name)) + self.assertEqual(cp_subdir, os.path.join(root, subd, fm.checkpoint_dir, cp_name)) class TestNotebookManager(TestCase): @@ -75,8 +75,8 @@ class TestNotebookManager(TestCase): def setUp(self): self._temp_dir = TemporaryDirectory() self.td = self._temp_dir.name - self.notebook_manager = FileNotebookManager( - notebook_dir=self.td, + self.contents_manager = FileContentsManager( + root_dir=self.td, log=logging.getLogger() ) @@ -100,22 +100,22 @@ class TestNotebookManager(TestCase): nb.worksheets[0].cells.append(cell) def new_notebook(self): - nbm = self.notebook_manager - model = nbm.create_notebook() + cm = self.contents_manager + model = cm.create_notebook() name = model['name'] path = model['path'] - full_model = nbm.get_notebook(name, path) + full_model = cm.get(name, path) nb = full_model['content'] self.add_code_cell(nb) - nbm.save_notebook(full_model, name, path) + cm.save(full_model, name, path) return nb, name, path def test_create_notebook(self): - nm = self.notebook_manager + cm = self.contents_manager # Test in root directory - model = nm.create_notebook() + model = cm.create_notebook() assert isinstance(model, dict) self.assertIn('name', model) self.assertIn('path', model) @@ -124,23 +124,23 @@ class TestNotebookManager(TestCase): # Test in sub-directory sub_dir = '/foo/' - self.make_dir(nm.notebook_dir, 'foo') - model = nm.create_notebook(None, sub_dir) + self.make_dir(cm.root_dir, 'foo') + model = cm.create_notebook(None, sub_dir) assert isinstance(model, dict) self.assertIn('name', model) self.assertIn('path', model) self.assertEqual(model['name'], 'Untitled0.ipynb') self.assertEqual(model['path'], sub_dir.strip('/')) - def test_get_notebook(self): - nm = self.notebook_manager + def test_get(self): + cm = self.contents_manager # Create a notebook - model = nm.create_notebook() + model = cm.create_notebook() name = model['name'] path = model['path'] # Check that we 'get' on the notebook we just created - model2 = nm.get_notebook(name, path) + model2 = cm.get(name, path) assert isinstance(model2, dict) self.assertIn('name', model2) self.assertIn('path', model2) @@ -149,9 +149,9 @@ class TestNotebookManager(TestCase): # Test in sub-directory sub_dir = '/foo/' - self.make_dir(nm.notebook_dir, 'foo') - model = nm.create_notebook(None, sub_dir) - model2 = nm.get_notebook(name, sub_dir) + self.make_dir(cm.root_dir, 'foo') + model = cm.create_notebook(None, sub_dir) + model2 = cm.get(name, sub_dir) assert isinstance(model2, dict) self.assertIn('name', model2) self.assertIn('path', model2) @@ -159,35 +159,35 @@ class TestNotebookManager(TestCase): self.assertEqual(model2['name'], 'Untitled0.ipynb') self.assertEqual(model2['path'], sub_dir.strip('/')) - def test_update_notebook(self): - nm = self.notebook_manager + def test_update(self): + cm = self.contents_manager # Create a notebook - model = nm.create_notebook() + model = cm.create_notebook() name = model['name'] path = model['path'] # Change the name in the model for rename model['name'] = 'test.ipynb' - model = nm.update_notebook(model, name, path) + model = cm.update(model, name, path) assert isinstance(model, dict) self.assertIn('name', model) self.assertIn('path', model) self.assertEqual(model['name'], 'test.ipynb') # Make sure the old name is gone - self.assertRaises(HTTPError, nm.get_notebook, name, path) + self.assertRaises(HTTPError, cm.get, name, path) # Test in sub-directory # Create a directory and notebook in that directory sub_dir = '/foo/' - self.make_dir(nm.notebook_dir, 'foo') - model = nm.create_notebook(None, sub_dir) + self.make_dir(cm.root_dir, 'foo') + model = cm.create_notebook(None, sub_dir) name = model['name'] path = model['path'] # Change the name in the model for rename model['name'] = 'test_in_sub.ipynb' - model = nm.update_notebook(model, name, path) + model = cm.update(model, name, path) assert isinstance(model, dict) self.assertIn('name', model) self.assertIn('path', model) @@ -195,20 +195,20 @@ class TestNotebookManager(TestCase): self.assertEqual(model['path'], sub_dir.strip('/')) # Make sure the old name is gone - self.assertRaises(HTTPError, nm.get_notebook, name, path) + self.assertRaises(HTTPError, cm.get, name, path) - def test_save_notebook(self): - nm = self.notebook_manager + def test_save(self): + cm = self.contents_manager # Create a notebook - model = nm.create_notebook() + model = cm.create_notebook() name = model['name'] path = model['path'] # Get the model with 'content' - full_model = nm.get_notebook(name, path) + full_model = cm.get(name, path) # Save the notebook - model = nm.save_notebook(full_model, name, path) + model = cm.save(full_model, name, path) assert isinstance(model, dict) self.assertIn('name', model) self.assertIn('path', model) @@ -218,103 +218,84 @@ class TestNotebookManager(TestCase): # Test in sub-directory # Create a directory and notebook in that directory sub_dir = '/foo/' - self.make_dir(nm.notebook_dir, 'foo') - model = nm.create_notebook(None, sub_dir) + self.make_dir(cm.root_dir, 'foo') + model = cm.create_notebook(None, sub_dir) name = model['name'] path = model['path'] - model = nm.get_notebook(name, path) + model = cm.get(name, path) # Change the name in the model for rename - model = nm.save_notebook(model, name, path) + model = cm.save(model, name, path) assert isinstance(model, dict) self.assertIn('name', model) self.assertIn('path', model) self.assertEqual(model['name'], 'Untitled0.ipynb') self.assertEqual(model['path'], sub_dir.strip('/')) - def test_save_notebook_with_script(self): - nm = self.notebook_manager - # Create a notebook - model = nm.create_notebook() - nm.save_script = True - model = nm.create_notebook() - name = model['name'] - path = model['path'] - - # Get the model with 'content' - full_model = nm.get_notebook(name, path) - - # Save the notebook - model = nm.save_notebook(full_model, name, path) - - # Check that the script was created - py_path = os.path.join(nm.notebook_dir, os.path.splitext(name)[0]+'.py') - assert os.path.exists(py_path), py_path - - def test_delete_notebook(self): - nm = self.notebook_manager + def test_delete(self): + cm = self.contents_manager # Create a notebook nb, name, path = self.new_notebook() # Delete the notebook - nm.delete_notebook(name, path) + cm.delete(name, path) # Check that a 'get' on the deleted notebook raises and error - self.assertRaises(HTTPError, nm.get_notebook, name, path) + self.assertRaises(HTTPError, cm.get, name, path) - def test_copy_notebook(self): - nm = self.notebook_manager + def test_copy(self): + cm = self.contents_manager path = u'å b' name = u'nb √.ipynb' - os.mkdir(os.path.join(nm.notebook_dir, path)) - orig = nm.create_notebook({'name' : name}, path=path) + os.mkdir(os.path.join(cm.root_dir, path)) + orig = cm.create_notebook({'name' : name}, path=path) # copy with unspecified name - copy = nm.copy_notebook(name, path=path) + copy = cm.copy(name, path=path) self.assertEqual(copy['name'], orig['name'].replace('.ipynb', '-Copy0.ipynb')) # copy with specified name - copy2 = nm.copy_notebook(name, u'copy 2.ipynb', path=path) + copy2 = cm.copy(name, u'copy 2.ipynb', path=path) self.assertEqual(copy2['name'], u'copy 2.ipynb') def test_trust_notebook(self): - nbm = self.notebook_manager + cm = self.contents_manager nb, name, path = self.new_notebook() - untrusted = nbm.get_notebook(name, path)['content'] - assert not nbm.notary.check_cells(untrusted) + untrusted = cm.get(name, path)['content'] + assert not cm.notary.check_cells(untrusted) # print(untrusted) - nbm.trust_notebook(name, path) - trusted = nbm.get_notebook(name, path)['content'] + cm.trust_notebook(name, path) + trusted = cm.get(name, path)['content'] # print(trusted) - assert nbm.notary.check_cells(trusted) + assert cm.notary.check_cells(trusted) def test_mark_trusted_cells(self): - nbm = self.notebook_manager + cm = self.contents_manager nb, name, path = self.new_notebook() - nbm.mark_trusted_cells(nb, name, path) + cm.mark_trusted_cells(nb, name, path) for cell in nb.worksheets[0].cells: if cell.cell_type == 'code': assert not cell.trusted - nbm.trust_notebook(name, path) - nb = nbm.get_notebook(name, path)['content'] + cm.trust_notebook(name, path) + nb = cm.get(name, path)['content'] for cell in nb.worksheets[0].cells: if cell.cell_type == 'code': assert cell.trusted def test_check_and_sign(self): - nbm = self.notebook_manager + cm = self.contents_manager nb, name, path = self.new_notebook() - nbm.mark_trusted_cells(nb, name, path) - nbm.check_and_sign(nb, name, path) - assert not nbm.notary.check_signature(nb) + cm.mark_trusted_cells(nb, name, path) + cm.check_and_sign(nb, name, path) + assert not cm.notary.check_signature(nb) - nbm.trust_notebook(name, path) - nb = nbm.get_notebook(name, path)['content'] - nbm.mark_trusted_cells(nb, name, path) - nbm.check_and_sign(nb, name, path) - assert nbm.notary.check_signature(nb) + cm.trust_notebook(name, path) + nb = cm.get(name, path)['content'] + cm.mark_trusted_cells(nb, name, path) + cm.check_and_sign(nb, name, path) + assert cm.notary.check_signature(nb) diff --git a/IPython/html/services/sessions/handlers.py b/IPython/html/services/sessions/handlers.py index cd84dc4..691339f 100644 --- a/IPython/html/services/sessions/handlers.py +++ b/IPython/html/services/sessions/handlers.py @@ -1,20 +1,7 @@ -"""Tornado handlers for the sessions web service. +"""Tornado handlers for the sessions web service.""" -Authors: - -* Zach Sailer -""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2013 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- +# Copyright (c) IPython Development Team. +# Distributed under the terms of the Modified BSD License. import json @@ -24,10 +11,6 @@ from ...base.handlers import IPythonHandler, json_errors from IPython.utils.jsonutil import date_default from IPython.html.utils import url_path_join, url_escape -#----------------------------------------------------------------------------- -# Session web service handlers -#----------------------------------------------------------------------------- - class SessionRootHandler(IPythonHandler): @@ -45,6 +28,8 @@ class SessionRootHandler(IPythonHandler): # Creates a new session #(unless a session already exists for the named nb) sm = self.session_manager + cm = self.contents_manager + km = self.kernel_manager model = self.get_json_body() if model is None: diff --git a/IPython/html/services/sessions/sessionmanager.py b/IPython/html/services/sessions/sessionmanager.py index cc68028..67adbb7 100644 --- a/IPython/html/services/sessions/sessionmanager.py +++ b/IPython/html/services/sessions/sessionmanager.py @@ -32,7 +32,7 @@ from IPython.utils.traitlets import Instance class SessionManager(LoggingConfigurable): kernel_manager = Instance('IPython.html.services.kernels.kernelmanager.MappingKernelManager') - notebook_manager = Instance('IPython.html.services.notebooks.nbmanager.NotebookManager', args=()) + contents_manager = Instance('IPython.html.services.contents.manager.ContentsManager', args=()) # Session database initialized below _cursor = None @@ -77,7 +77,7 @@ class SessionManager(LoggingConfigurable): """Creates a session and returns its model""" session_id = self.new_session_id() # allow nbm to specify kernels cwd - kernel_path = self.notebook_manager.get_kernel_path(name=name, path=path) + kernel_path = self.contents_manager.get_kernel_path(name=name, path=path) kernel_id = self.kernel_manager.start_kernel(path=kernel_path, kernel_name=kernel_name) return self.save_session(session_id, name=name, path=path, diff --git a/IPython/html/static/notebook/js/notebook.js b/IPython/html/static/notebook/js/notebook.js index e01e7ff..67e5e8b 100644 --- a/IPython/html/static/notebook/js/notebook.js +++ b/IPython/html/static/notebook/js/notebook.js @@ -1908,7 +1908,7 @@ define([ this.events.trigger('notebook_saving.Notebook'); var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name ); @@ -2041,7 +2041,7 @@ define([ }; var url = utils.url_join_encode( base_url, - 'api/notebooks', + 'api/contents', path ); $.ajax(url,settings); @@ -2070,7 +2070,7 @@ define([ }; var url = utils.url_join_encode( base_url, - 'api/notebooks', + 'api/contents', path ); $.ajax(url,settings); @@ -2095,7 +2095,7 @@ define([ this.events.trigger('rename_notebook.Notebook', data); var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name ); @@ -2113,7 +2113,7 @@ define([ }; var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name ); @@ -2182,7 +2182,7 @@ define([ this.events.trigger('notebook_loading.Notebook'); var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name ); @@ -2345,7 +2345,7 @@ define([ Notebook.prototype.list_checkpoints = function () { var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name, 'checkpoints' @@ -2396,7 +2396,7 @@ define([ Notebook.prototype.create_checkpoint = function () { var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name, 'checkpoints' @@ -2485,7 +2485,7 @@ define([ this.events.trigger('notebook_restoring.Notebook', checkpoint); var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name, 'checkpoints', @@ -2533,7 +2533,7 @@ define([ this.events.trigger('notebook_restoring.Notebook', checkpoint); var url = utils.url_join_encode( this.base_url, - 'api/notebooks', + 'api/contents', this.notebook_path, this.notebook_name, 'checkpoints', diff --git a/IPython/html/static/tree/js/notebooklist.js b/IPython/html/static/tree/js/notebooklist.js index 4228e39..7827241 100644 --- a/IPython/html/static/tree/js/notebooklist.js +++ b/IPython/html/static/tree/js/notebooklist.js @@ -148,7 +148,7 @@ define([ var url = utils.url_join_encode( this.base_url, 'api', - 'notebooks', + 'contents', this.notebook_path ); $.ajax(url, settings); @@ -328,7 +328,7 @@ define([ }; var url = utils.url_join_encode( notebooklist.base_url, - 'api/notebooks', + 'api/contents', notebooklist.notebook_path, nbname ); @@ -375,7 +375,7 @@ define([ var url = utils.url_join_encode( that.base_url, - 'api/notebooks', + 'api/contents', that.notebook_path, nbname ); @@ -419,7 +419,7 @@ define([ }; var url = utils.url_join_encode( base_url, - 'api/notebooks', + 'api/contents', path ); $.ajax(url, settings); diff --git a/IPython/html/tests/launchnotebook.py b/IPython/html/tests/launchnotebook.py index 67a1955..42ae768 100644 --- a/IPython/html/tests/launchnotebook.py +++ b/IPython/html/tests/launchnotebook.py @@ -33,7 +33,7 @@ class NotebookTestBase(TestCase): @classmethod def wait_until_alive(cls): """Wait for the server to be alive""" - url = 'http://localhost:%i/api/notebooks' % cls.port + url = 'http://localhost:%i/api/contents' % cls.port for _ in range(int(MAX_WAITTIME/POLL_INTERVAL)): try: requests.get(url) diff --git a/IPython/html/tree/handlers.py b/IPython/html/tree/handlers.py index 4a4320f..d1ae0c2 100644 --- a/IPython/html/tree/handlers.py +++ b/IPython/html/tree/handlers.py @@ -51,7 +51,7 @@ class TreeHandler(IPythonHandler): @web.authenticated def get(self, path='', name=None): path = path.strip('/') - nbm = self.notebook_manager + cm = self.contents_manager if name is not None: # is a notebook, redirect to notebook handler url = url_escape(url_path_join( @@ -60,10 +60,10 @@ class TreeHandler(IPythonHandler): self.log.debug("Redirecting %s to %s", self.request.path, url) self.redirect(url) else: - if not nbm.path_exists(path=path): + if not cm.path_exists(path=path): # Directory is hidden or does not exist. raise web.HTTPError(404) - elif nbm.is_hidden(path): + elif cm.is_hidden(path): self.log.info("Refusing to serve hidden directory, via 404 Error") raise web.HTTPError(404) breadcrumbs = self.generate_breadcrumbs(path)