-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
add contents webservice #5938
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add contents webservice #5938
Changes from all commits
e918eb5
48c2b3d
dbd4c50
98ccb25
f8d34e1
2bee5e5
fb8cd39
35c8d20
ec353c7
b334379
8f08d4e
8c324cd
920c0ba
459d507
96cae31
5318299
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. | ||
|
|
@@ -27,7 +27,7 @@ | |
| from IPython.config import Application | ||
| from IPython.utils.path import filefind | ||
| from IPython.utils.py3compat import string_types | ||
| from IPython.html.utils import is_hidden | ||
| from IPython.html.utils import is_hidden, url_path_join, url_escape | ||
|
|
||
| #----------------------------------------------------------------------------- | ||
| # Top-level handlers | ||
|
|
@@ -141,8 +141,8 @@ def kernel_manager(self): | |
| 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): | ||
|
|
@@ -156,10 +156,6 @@ def session_manager(self): | |
| def kernel_spec_manager(self): | ||
| return self.settings['kernel_spec_manager'] | ||
|
|
||
| @property | ||
| def project_dir(self): | ||
| return self.notebook_manager.notebook_dir | ||
|
|
||
| #--------------------------------------------------------------- | ||
| # CORS | ||
| #--------------------------------------------------------------- | ||
|
|
@@ -409,13 +405,46 @@ class TrailingSlashHandler(web.RequestHandler): | |
| def get(self): | ||
| self.redirect(self.request.uri.rstrip('/')) | ||
|
|
||
|
|
||
| class FilesRedirectHandler(IPythonHandler): | ||
| """Handler for redirecting relative URLs to the /files/ handler""" | ||
| def get(self, 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: | ||
| orig_path = path | ||
| # otherwise, redirect to /files | ||
| parts = path.split('/') | ||
| path = '/'.join(parts[:-1]) | ||
| name = parts[-1] | ||
|
|
||
| if not cm.file_exists(name=name, path=path) and 'files' in parts: | ||
| # redirect without files/ iff it would 404 | ||
| # this preserves pre-2.0-style 'files/' links | ||
| self.log.warn("Deprecated files/ URL: %s", orig_path) | ||
| parts.remove('files') | ||
| path = '/'.join(parts[:-1]) | ||
|
|
||
| if not cm.file_exists(name=name, path=path): | ||
| raise web.HTTPError(404) | ||
|
|
||
| url = url_path_join(self.base_url, 'files', path, name) | ||
| url = url_escape(url) | ||
| self.log.debug("Redirecting %s to %s", self.request.path, url) | ||
| self.redirect(url) | ||
|
|
||
|
|
||
| #----------------------------------------------------------------------------- | ||
| # URL pattern fragments for re-use | ||
| #----------------------------------------------------------------------------- | ||
|
|
||
| path_regex = r"(?P<path>(?:/.*)*)" | ||
| notebook_name_regex = r"(?P<name>[^/]+\.ipynb)" | ||
| notebook_path_regex = "%s/%s" % (path_regex, notebook_name_regex) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these two stlil needed?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are still used in notebook and nbconvert URLs. |
||
| file_name_regex = r"(?P<name>[^/]+)" | ||
| file_path_regex = "%s/%s" % (path_regex, file_name_regex) | ||
|
|
||
| #----------------------------------------------------------------------------- | ||
| # URL to handler mappings | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,17 @@ | ||
| """Tornado handlers for the live notebook view. | ||
| """Tornado handlers for the live notebook view.""" | ||
|
|
||
| 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 os | ||
| from tornado import web | ||
| HTTPError = web.HTTPError | ||
|
|
||
| from ..base.handlers import IPythonHandler, notebook_path_regex, path_regex | ||
| from ..utils import url_path_join, url_escape | ||
|
|
||
| #----------------------------------------------------------------------------- | ||
| # Handlers | ||
| #----------------------------------------------------------------------------- | ||
| from ..base.handlers import ( | ||
| IPythonHandler, FilesRedirectHandler, | ||
| notebook_path_regex, path_regex, | ||
| ) | ||
| from ..utils import url_escape | ||
|
|
||
|
|
||
| class NotebookHandler(IPythonHandler): | ||
|
|
@@ -35,48 +21,23 @@ def get(self, path='', name=None): | |
| """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): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the contents manager have methods/parameters for things like "does this exist and is it a notebook", or "load this, throwing an error if it is not a notebook"?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think it's necessary. The caller can always check the |
||
| raise web.HTTPError(404, u'Notebook does not exist: %s/%s' % (path, name)) | ||
| name = url_escape(name) | ||
| path = url_escape(path) | ||
| self.write(self.render_template('notebook.html', | ||
| project=self.project_dir, | ||
| notebook_path=path, | ||
| notebook_name=name, | ||
| kill_kernel=False, | ||
| mathjax_url=self.mathjax_url, | ||
| ) | ||
| ) | ||
|
|
||
| class NotebookRedirectHandler(IPythonHandler): | ||
| def get(self, path=''): | ||
| nbm = self.notebook_manager | ||
| if nbm.path_exists(path): | ||
| # it's a *directory*, redirect to /tree | ||
| url = url_path_join(self.base_url, 'tree', path) | ||
| else: | ||
| # otherwise, redirect to /files | ||
| if '/files/' in path: | ||
| # redirect without files/ iff it would 404 | ||
| # this preserves pre-2.0-style 'files/' links | ||
| # FIXME: this is hardcoded based on notebook_path, | ||
| # 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) | ||
| if not os.path.exists(files_path): | ||
| self.log.warn("Deprecated files/ URL: %s", path) | ||
| path = path.replace('/files/', '/', 1) | ||
|
|
||
| url = url_path_join(self.base_url, 'files', path) | ||
| url = url_escape(url) | ||
| self.log.debug("Redirecting %s to %s", self.request.path, url) | ||
| self.redirect(url) | ||
|
|
||
| #----------------------------------------------------------------------------- | ||
| # URL to handler mappings | ||
|
|
@@ -85,6 +46,6 @@ def get(self, path=''): | |
|
|
||
| default_handlers = [ | ||
| (r"/notebooks%s" % notebook_path_regex, NotebookHandler), | ||
| (r"/notebooks%s" % path_regex, NotebookRedirectHandler), | ||
| (r"/notebooks%s" % path_regex, FilesRedirectHandler), | ||
| ] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,8 +55,8 @@ | |
| 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 @@ def init_settings(self, ipython_app, kernel_manager, notebook_manager, | |
|
|
||
| # 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 @@ def init_handlers(self, settings): | |
| 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: | ||
|
|
@@ -264,9 +266,9 @@ def start(self): | |
| ) | ||
|
|
||
| # 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')) | ||
| flags.update(boolean_flag('script', 'FileContentsManager.save_script', | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to note: we are taking this option away without apparently having a deprecation cycle (the sort where we mark things as deprecated, not the sort where we grumble about them).
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've put the flag back, so the notebook will start with a warning instead of refusing to start. |
||
| 'DEPRECATED, IGNORED', | ||
| 'DEPRECATED, IGNORED')) | ||
|
|
||
| aliases = dict(base_aliases) | ||
|
|
||
|
|
@@ -302,7 +304,7 @@ class NotebookApp(BaseIPythonApplication): | |
|
|
||
| classes = [ | ||
| KernelManager, ProfileDir, Session, MappingKernelManager, | ||
| NotebookManager, FileNotebookManager, NotebookNotary, | ||
| ContentsManager, FileContentsManager, NotebookNotary, | ||
| ] | ||
| flags = Dict(flags) | ||
| aliases = Dict(aliases) | ||
|
|
@@ -557,7 +559,7 @@ def _mathjax_url_changed(self, name, old, new): | |
| 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 +623,7 @@ def _notebook_dir_changed(self, name, old, new): | |
| 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 +660,12 @@ def init_configurables(self): | |
| 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 +690,7 @@ def init_webapp(self): | |
| 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 +840,7 @@ def cleanup_kernels(self): | |
|
|
||
| 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 | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it be only
parts[0] == 'files'instead of'files' in parts?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because
filesshould be a relative URL, and thus can occur at any point in the URL.