|
@@ -3,17 +3,133 @@
|
|
|
|
|
|
# Copyright (c) Jupyter Development Team.
|
|
|
# Distributed under the terms of the Modified BSD License.
|
|
|
-
|
|
|
-# TODO: import base server app
|
|
|
import os
|
|
|
-from jupyter_core.paths import jupyter_config_path, jupyter_path
|
|
|
+from tornado import web
|
|
|
+
|
|
|
from notebook.notebookapp import NotebookApp
|
|
|
-from traitlets import List, Dict, Unicode, default
|
|
|
-from traitlets.config.manager import BaseJSONConfigManager
|
|
|
+from traitlets import Dict, Unicode
|
|
|
+from notebook.base.handlers import IPythonHandler, FileFindHandler
|
|
|
+from jinja2 import FileSystemLoader
|
|
|
+from notebook.utils import url_path_join as ujoin
|
|
|
+from jupyter_core.paths import jupyter_path
|
|
|
+
|
|
|
+from .labextensions import (
|
|
|
+ find_labextension, validate_labextension_folder,
|
|
|
+ get_labextension_manifest_data_by_name,
|
|
|
+ get_labextension_manifest_data_by_folder,
|
|
|
+ get_labextension_config_python
|
|
|
+)
|
|
|
+
|
|
|
+try:
|
|
|
+ from ._version import __version__
|
|
|
+except ImportError as e:
|
|
|
+ # when we are python 3 only, add 'from e' at the end to chain the exception.
|
|
|
+ raise ImportError("No module named 'jupyter._version'. Build the jupyterlab package to generate this module, for example, with `pip install -e /path/to/jupyterlab/repo`.")
|
|
|
+
|
|
|
+#-----------------------------------------------------------------------------
|
|
|
+# Module globals
|
|
|
+#-----------------------------------------------------------------------------
|
|
|
+
|
|
|
+DEV_NOTE_NPM = """It looks like you're running JupyterLab from source.
|
|
|
+If you're working on the TypeScript sources of JupyterLab, try running
|
|
|
+
|
|
|
+ npm run watch
|
|
|
+
|
|
|
+from the JupyterLab repo directory in another terminal window to have the
|
|
|
+system incrementally watch and build JupyterLab's TypeScript for you, as you
|
|
|
+make changes.
|
|
|
+"""
|
|
|
+
|
|
|
+HERE = os.path.dirname(__file__)
|
|
|
+FILE_LOADER = FileSystemLoader(HERE)
|
|
|
+BUILT_FILES = os.path.join(HERE, 'build')
|
|
|
+PREFIX = '/lab'
|
|
|
+EXTENSION_PREFIX = '/labextension'
|
|
|
+
|
|
|
+
|
|
|
+class LabHandler(IPythonHandler):
|
|
|
+ """Render the Jupyter Lab View."""
|
|
|
+
|
|
|
+ @web.authenticated
|
|
|
+ def get(self):
|
|
|
+ static_prefix = ujoin(self.base_url, PREFIX)
|
|
|
+ labextensions = self.application.labextensions
|
|
|
+ data = get_labextension_manifest_data_by_folder(BUILT_FILES)
|
|
|
+ if 'main' not in data or 'extensions' not in data:
|
|
|
+ msg = ('JupyterLab build artifacts not detected, please see ' +
|
|
|
+ 'CONTRIBUTING.md for build instructions.')
|
|
|
+ self.log.error(msg)
|
|
|
+ self.write(self.render_template('error.html',
|
|
|
+ status_code=500,
|
|
|
+ status_message='JupyterLab Error',
|
|
|
+ page_title='JupyterLab Error',
|
|
|
+ message=msg))
|
|
|
+ return
|
|
|
+
|
|
|
+ main = data['main']['entry']
|
|
|
+ bundles = [ujoin(static_prefix, name + '.bundle.js') for name in
|
|
|
+ ['loader', 'main', 'extensions']]
|
|
|
+ entries = [data['extensions']['entry']]
|
|
|
+
|
|
|
+ # Only load CSS files if they exist.
|
|
|
+ css_files = []
|
|
|
+ for css_file in ['main.css', 'extensions.css']:
|
|
|
+ if os.path.isfile(os.path.join(BUILT_FILES, css_file)):
|
|
|
+ css_files.append(ujoin(static_prefix, css_file))
|
|
|
|
|
|
-from .labextensions import find_labextension, validate_labextension_folder
|
|
|
+ config = dict(
|
|
|
+ static_prefix=static_prefix,
|
|
|
+ page_title='JupyterLab Alpha Preview',
|
|
|
+ mathjax_url=self.mathjax_url,
|
|
|
+ jupyterlab_main=main,
|
|
|
+ jupyterlab_css=css_files,
|
|
|
+ jupyterlab_bundles=bundles,
|
|
|
+ plugin_entries=entries,
|
|
|
+ mathjax_config='TeX-AMS_HTML-full,Safe',
|
|
|
+ #mathjax_config=self.mathjax_config # for the next release of the notebook
|
|
|
+ )
|
|
|
|
|
|
-from ._version import __version__
|
|
|
+ configData = dict(
|
|
|
+ terminalsAvailable=self.settings.get('terminals_available', False),
|
|
|
+ )
|
|
|
+
|
|
|
+ # Gather the lab extension files and entry points.
|
|
|
+ for (name, data) in sorted(labextensions.items()):
|
|
|
+ for value in data.values():
|
|
|
+ if value.get('entry', None):
|
|
|
+ entries.append(value['entry'])
|
|
|
+ bundles.append('%s/%s/%s' % (
|
|
|
+ EXTENSION_PREFIX, name, value['files'][0]
|
|
|
+ ))
|
|
|
+ for fname in value['files']:
|
|
|
+ if os.path.splitext(fname)[1] == '.css':
|
|
|
+ css_files.append('%s/%s/%s' % (
|
|
|
+ EXTENSION_PREFIX, name, fname
|
|
|
+ ))
|
|
|
+ python_module = data.get('python_module', None)
|
|
|
+ if python_module:
|
|
|
+ try:
|
|
|
+ value = get_labextension_config_python(python_module)
|
|
|
+ configData.update(value)
|
|
|
+ except Exception as e:
|
|
|
+ self.log.error(e)
|
|
|
+
|
|
|
+ config['jupyterlab_config'] = configData
|
|
|
+ self.write(self.render_template('lab.html', **config))
|
|
|
+
|
|
|
+ def get_template(self, name):
|
|
|
+ return FILE_LOADER.load(self.settings['jinja2_env'], name)
|
|
|
+
|
|
|
+
|
|
|
+#-----------------------------------------------------------------------------
|
|
|
+# URL to handler mappings
|
|
|
+#-----------------------------------------------------------------------------
|
|
|
+
|
|
|
+default_handlers = [
|
|
|
+ (PREFIX + r'/?', LabHandler),
|
|
|
+ (PREFIX + r"/(.*)", FileFindHandler,
|
|
|
+ {'path': BUILT_FILES}),
|
|
|
+]
|
|
|
|
|
|
|
|
|
class LabApp(NotebookApp):
|
|
@@ -45,6 +161,60 @@ class LabApp(NotebookApp):
|
|
|
'Extensions are loaded in alphabetical order')
|
|
|
)
|
|
|
|
|
|
+ def add_labextensions(self, webapp):
|
|
|
+ """Get the enabledd and valid lab extensions.
|
|
|
+
|
|
|
+ Returns
|
|
|
+ -------
|
|
|
+ out - dict
|
|
|
+ The manifest data for each enabled and active extension,
|
|
|
+ and optionally its associated python_module.
|
|
|
+ """
|
|
|
+ out = dict()
|
|
|
+ for (name, config) in self.labextensions.items():
|
|
|
+ if not config['enabled']:
|
|
|
+ continue
|
|
|
+ warnings = validate_labextension_folder(name, find_labextension(name))
|
|
|
+ if warnings:
|
|
|
+ continue
|
|
|
+ data = get_labextension_manifest_data_by_name(name)
|
|
|
+ if data is None:
|
|
|
+ continue
|
|
|
+ data['python_module'] = config.get('python_module', None)
|
|
|
+ out[name] = data
|
|
|
+ webapp.labextensions = out
|
|
|
+
|
|
|
+ def init_webapp(self):
|
|
|
+ super(LabApp, self).init_webapp()
|
|
|
+ self.add_lab_handlers(self.web_app)
|
|
|
+ self.add_labextensions(self.web_app)
|
|
|
+
|
|
|
+ def add_lab_handlers(self, webapp):
|
|
|
+ base_url = webapp.settings['base_url']
|
|
|
+ webapp.add_handlers(".*$",
|
|
|
+ [(ujoin(base_url, h[0]),) + h[1:] for h in default_handlers])
|
|
|
+ labextension_handler = (
|
|
|
+ r"%s/(.*)" % EXTENSION_PREFIX, FileFindHandler, {
|
|
|
+ 'path': jupyter_path('labextensions'),
|
|
|
+ 'no_cache_paths': ['/'], # don't cache anything in labbextensions
|
|
|
+ }
|
|
|
+ )
|
|
|
+ webapp.add_handlers(".*$", [labextension_handler])
|
|
|
+ base_dir = os.path.realpath(os.path.join(HERE, '..'))
|
|
|
+ dev_mode = os.path.exists(os.path.join(base_dir, '.git'))
|
|
|
+ if dev_mode:
|
|
|
+ self.log.info(DEV_NOTE_NPM)
|
|
|
+
|
|
|
+
|
|
|
+def bootstrap_from_nbapp(nbapp):
|
|
|
+ """Bootstrap the lab app on to off a noteobok app.
|
|
|
+ """
|
|
|
+ labapp = LabApp()
|
|
|
+ labapp.load_config_file()
|
|
|
+ webapp = nbapp.web_app
|
|
|
+ labapp.add_lab_handlers(webapp)
|
|
|
+ labapp.add_labextensions(webapp)
|
|
|
+
|
|
|
#-----------------------------------------------------------------------------
|
|
|
# Main entry point
|
|
|
#-----------------------------------------------------------------------------
|