labapp.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. # coding: utf-8
  2. """A tornado based Jupyter lab server."""
  3. # Copyright (c) Jupyter Development Team.
  4. # Distributed under the terms of the Modified BSD License.
  5. import os
  6. from tornado import web
  7. from notebook.notebookapp import NotebookApp
  8. from traitlets import Unicode
  9. from notebook.base.handlers import IPythonHandler, FileFindHandler
  10. from jinja2 import FileSystemLoader
  11. from notebook.utils import url_path_join as ujoin
  12. from jupyter_core.paths import jupyter_path
  13. from ._version import __version__
  14. from .labextensions import (
  15. find_labextension, validate_labextension_folder,
  16. get_labextension_manifest_data_by_name,
  17. get_labextension_manifest_data_by_folder,
  18. get_labextension_config_python, CONFIG_SECTION
  19. )
  20. #-----------------------------------------------------------------------------
  21. # Module globals
  22. #-----------------------------------------------------------------------------
  23. DEV_NOTE_NPM = """It looks like you're running JupyterLab from source.
  24. If you're working on the TypeScript sources of JupyterLab, try running
  25. npm run watch
  26. from the JupyterLab repo directory in another terminal window to have the
  27. system incrementally watch and build JupyterLab's TypeScript for you, as you
  28. make changes.
  29. """
  30. HERE = os.path.dirname(__file__)
  31. FILE_LOADER = FileSystemLoader(HERE)
  32. BUILT_FILES = os.path.join(HERE, 'build')
  33. PREFIX = '/lab'
  34. EXTENSION_PREFIX = '/labextension'
  35. class LabHandler(IPythonHandler):
  36. """Render the Jupyter Lab View."""
  37. def initialize(self, labextensions, extension_prefix):
  38. self.labextensions = labextensions
  39. self.extension_prefix = extension_prefix
  40. @web.authenticated
  41. def get(self):
  42. manifest = get_labextension_manifest_data_by_folder(BUILT_FILES)
  43. if 'main' not in manifest:
  44. msg = ('JupyterLab build artifacts not detected, please see ' +
  45. 'CONTRIBUTING.md for build instructions.')
  46. self.log.error(msg)
  47. self.write(self.render_template('error.html',
  48. status_code=500,
  49. status_message='JupyterLab Error',
  50. page_title='JupyterLab Error',
  51. message=msg))
  52. return
  53. config = self._get_lab_config(manifest)
  54. self.write(self.render_template('lab.html', **config))
  55. def _get_lab_config(self, manifest):
  56. """Get the config data for the page template."""
  57. static_prefix = ujoin(self.base_url, PREFIX)
  58. labextensions = self.labextensions
  59. main = manifest['main']['entry']
  60. bundles = [ujoin(static_prefix, name + '.bundle.js') for name in
  61. ['loader', 'main']]
  62. entries = []
  63. # Only load CSS files if they exist.
  64. css_files = []
  65. for css_file in ['main.css']:
  66. if os.path.isfile(os.path.join(BUILT_FILES, css_file)):
  67. css_files.append(ujoin(static_prefix, css_file))
  68. configData = dict(
  69. terminalsAvailable=self.settings.get('terminals_available', False),
  70. )
  71. # Gather the lab extension files and entry points.
  72. for (name, data) in sorted(labextensions.items()):
  73. for value in data.values():
  74. if not isinstance(value, dict):
  75. continue
  76. if value.get('entry', None):
  77. entries.append(value['entry'])
  78. bundles.append('%s/%s/%s' % (
  79. self.extension_prefix, name, value['files'][0]
  80. ))
  81. for fname in value['files']:
  82. if os.path.splitext(fname)[1] == '.css':
  83. css_files.append('%s/%s/%s' % (
  84. self.extension_prefix, name, fname
  85. ))
  86. python_module = data.get('python_module', None)
  87. if python_module:
  88. try:
  89. value = get_labextension_config_python(python_module)
  90. configData.update(value)
  91. except Exception as e:
  92. self.log.error(e)
  93. mathjax_config = self.settings.get('mathjax_config',
  94. 'TeX-AMS_HTML-full,Safe')
  95. config = dict(
  96. static_prefix=static_prefix,
  97. page_title='JupyterLab Alpha Preview',
  98. mathjax_url=self.mathjax_url,
  99. mathjax_config=mathjax_config,
  100. jupyterlab_main=main,
  101. jupyterlab_css=css_files,
  102. jupyterlab_bundles=bundles,
  103. plugin_entries=entries,
  104. )
  105. config['jupyterlab_config'] = configData
  106. return config
  107. def get_template(self, name):
  108. return FILE_LOADER.load(self.settings['jinja2_env'], name)
  109. def get_extensions(lab_config):
  110. """Get the valid extensions from lab config."""
  111. extensions = dict()
  112. labextensions = lab_config.get('labextensions', {})
  113. for (name, ext_config) in labextensions.items():
  114. if not ext_config['enabled']:
  115. continue
  116. folder = find_labextension(name)
  117. if folder is None:
  118. continue
  119. warnings = validate_labextension_folder(name, folder)
  120. if warnings:
  121. continue
  122. data = get_labextension_manifest_data_by_name(name)
  123. if data is None:
  124. continue
  125. data['python_module'] = ext_config.get('python_module', None)
  126. extensions[name] = data
  127. return extensions
  128. def add_handlers(web_app, labextensions):
  129. """Add the appropriate handlers to the web app.
  130. """
  131. base_url = web_app.settings['base_url']
  132. prefix = ujoin(base_url, PREFIX)
  133. extension_prefix = ujoin(base_url, EXTENSION_PREFIX)
  134. handlers = [
  135. (prefix + r'/?', LabHandler, {
  136. 'labextensions': labextensions,
  137. 'extension_prefix': extension_prefix
  138. }),
  139. (prefix + r"/(.*)", FileFindHandler, {
  140. 'path': BUILT_FILES
  141. }),
  142. (extension_prefix + r"/(.*)", FileFindHandler, {
  143. 'path': jupyter_path('labextensions'),
  144. 'no_cache_paths': ['/'], # don't cache anything in labextensions
  145. })
  146. ]
  147. web_app.add_handlers(".*$", handlers)
  148. def load_jupyter_server_extension(nbapp):
  149. """Load the JupyterLab server extension.
  150. """
  151. # Print messages.
  152. nbapp.log.info('JupyterLab alpha preview extension loaded from %s' % HERE)
  153. base_dir = os.path.realpath(os.path.join(HERE, '..'))
  154. dev_mode = os.path.exists(os.path.join(base_dir, '.git'))
  155. if dev_mode:
  156. nbapp.log.info(DEV_NOTE_NPM)
  157. lab_config = nbapp.config.get(CONFIG_SECTION, {})
  158. extensions = get_extensions(lab_config)
  159. add_handlers(nbapp.web_app, extensions)
  160. class LabApp(NotebookApp):
  161. version = __version__
  162. description = """
  163. JupyterLab - An extensible computational environment for Jupyter.
  164. This launches a Tornado based HTML Server that serves up an
  165. HTML5/Javascript JupyterLab client.
  166. """
  167. examples = """
  168. jupyter lab # start JupyterLab
  169. jupyter lab --certfile=mycert.pem # use SSL/TLS certificate
  170. """
  171. subcommands = dict()
  172. default_url = Unicode('/lab', config=True,
  173. help="The default URL to redirect to from `/`")
  174. #-----------------------------------------------------------------------------
  175. # Main entry point
  176. #-----------------------------------------------------------------------------
  177. main = launch_new_instance = LabApp.launch_instance