Browse Source

Add JupyterLab python package infrastructure to build the Jupyter extension.

Jason Grout 9 years ago
parent
commit
76f69bc7ff
9 changed files with 480 additions and 11 deletions
  1. 91 0
      .gitignore
  2. 3 0
      MANIFEST.in
  3. 30 11
      README.md
  4. 52 0
      jupyterlab/__init__.py
  5. 40 0
      jupyterlab/index.js
  6. 33 0
      jupyterlab/lab.html
  7. 32 0
      jupyterlab/package.json
  8. 41 0
      jupyterlab/webpack.conf.js
  9. 158 0
      setup.py

+ 91 - 0
.gitignore

@@ -37,3 +37,94 @@ docs
 .ipynb_checkpoints
 Unititled*
 untitled*
+
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# IPython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject

+ 3 - 0
MANIFEST.in

@@ -0,0 +1,3 @@
+recursive-include jupyterlab/build *
+include jupyterlab/lab.html
+include package.json

+ 30 - 11
README.md

@@ -1,5 +1,5 @@
 JupyterLab
-==================
+==========
 
 An extensible computational environment for Jupyter.
 
@@ -7,21 +7,40 @@ An extensible computational environment for Jupyter.
 
 <img src="jupyter-plugins-demo.gif" alt="JupyterLab Demo" style="width: 100%;"/>
 
-Package Install
----------------
+Jupyter Notebook Extension
+--------------------------
+
+### Prerequisites
+- Jupyter notebook 4.2+
+
+### Developer Installation
+
+```
+git clone https://github.com/jupyter/jupyterlab.git
+pip install -e jupyterlab
+jupyter serverextension enable --py jupyterlab
+```
+
+### Use
+
+Start up the Jupyter notebook, and then open a browser to the server's URL with path `/lab` (e.g., `http://localhost:8888/lab`).
+
+
+NPM Package Install
+-------------------
 
 **Prerequisites**
 - [node](http://nodejs.org/)
 - [python](https://www.continuum.io/downloads)
 
 ```bash
-npm install --save jupyter-js-plugins
+npm install --save jupyterlab
 conda install notebook  # notebook 4.2+ required
 ```
 
 
-Source Build
-------------
+NPM Source Build
+----------------
 
 **Prerequisites**
 - [git](http://git-scm.com/)
@@ -29,8 +48,8 @@ Source Build
 - [python](https://www.continuum.io/downloads)
 
 ```bash
-git clone https://github.com/jupyter/jupyter-js-plugins.git
-cd jupyter-js-plugins
+git clone https://github.com/jupyter/jupyterlab.git
+cd jupyterlab
 npm install
 npm run build
 conda install notebook  # notebook 4.2+ required
@@ -57,13 +76,13 @@ Build Example
 -------------
 
 Follow the source build instructions first.
-Requires a Python install with the Jupyter notebook.
+Requires a Python install with the Jupyter notebook (version 4.2 or later).
 
 ```bash
-npm run build:example
+npm run build:examples
 ```
 
-Change to `example` directory and run `python main.py`.
+Change to the appropriate example in the `examples` directory and run `python main.py`.
 
 
 Build Docs

+ 52 - 0
jupyterlab/__init__.py

@@ -0,0 +1,52 @@
+"""Tornado handlers for the Lab view."""
+
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+import os
+from tornado import web
+from notebook.base.handlers import IPythonHandler, FileFindHandler
+from jinja2 import FileSystemLoader
+
+
+FILE_LOADER = FileSystemLoader(os.path.dirname(__file__))
+PREFIX = '/lab'
+
+class LabHandler(IPythonHandler):
+    """Render the Jupyter Lab View."""   
+
+    @web.authenticated
+    def get(self):
+        self.write(self.render_template('lab.html',
+            static_prefix=PREFIX,
+            page_title='Pre-Alpha Jupyter Lab Demo',
+            terminals_available=self.settings['terminals_available'],
+            mathjax_url=self.mathjax_url,
+            mathjax_config='TeX-AMS_HTML-full,Safe',
+            #mathjax_config=self.mathjax_config # for the next release of the notebook
+        ))
+
+    def get_template(self, name):
+        return FILE_LOADER.load(self.settings['jinja2_env'], name)
+
+#-----------------------------------------------------------------------------
+# URL to handler mappings
+#-----------------------------------------------------------------------------
+
+default_handlers = [
+    (PREFIX, LabHandler),
+    (PREFIX+r"/(.*)", FileFindHandler,
+        {'path': os.path.join(os.path.dirname(__file__), 'build')}),
+    ]
+
+def _jupyter_server_extension_paths():
+    return [{
+        "module": "jupyterlab"
+    }]
+    
+def load_jupyter_server_extension(nbapp):
+    nbapp.log.info('Pre-alpha version of Lab extension loaded')
+
+    webapp = nbapp.web_app
+    #base_url = webapp.settings['base_url']
+    webapp.add_handlers(".*$", default_handlers)

+ 40 - 0
jupyterlab/index.js

@@ -0,0 +1,40 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+'use strict';
+
+var phosphide = require('phosphide/lib/core/application');
+
+// ES6 Promise polyfill
+require('es6-promise').polyfill();
+
+require('font-awesome/css/font-awesome.min.css');
+require('jupyterlab/lib/default-theme/index.css');
+
+var app = new phosphide.Application({
+  extensions: [
+    require('jupyterlab/lib/about/plugin').aboutExtension,
+    require('jupyterlab/lib/console/plugin').consoleExtension,
+    require('jupyterlab/lib/editorhandler/plugin').editorHandlerExtension,
+    require('jupyterlab/lib/filebrowser/plugin').fileBrowserExtension,
+    require('jupyterlab/lib/help/plugin').helpHandlerExtension,
+    require('jupyterlab/lib/imagehandler/plugin').imageHandlerExtension,
+    require('jupyterlab/lib/landing/plugin').landingExtension,
+    require('jupyterlab/lib/main/plugin').mainExtension,
+    require('jupyterlab/lib/notebook/plugin').notebookHandlerExtension,
+    require('jupyterlab/lib/shortcuts/plugin').shortcutsExtension,
+    require('jupyterlab/lib/terminal/plugin').terminalExtension,
+    require('jupyterlab/lib/widgets/plugin').widgetManagerExtension,
+    require('phosphide/lib/extensions/commandpalette').commandPaletteExtension,
+  ],
+  providers: [
+    require('jupyterlab/lib/clipboard/plugin').clipboardProvider,
+    require('jupyterlab/lib/docregistry/plugin').docRegistryProvider,
+    require('jupyterlab/lib/notebook/plugin').activeNotebookProvider,
+    require('jupyterlab/lib/rendermime/plugin').renderMimeProvider,
+    require('jupyterlab/lib/services/plugin').servicesProvider,
+  ]
+});
+
+window.onload = function() {
+    app.run();
+}

+ 33 - 0
jupyterlab/lab.html

@@ -0,0 +1,33 @@
+<!--
+Copyright (c) Jupyter Development Team.
+Distributed under the terms of the Modified BSD License.
+-->
+
+<!DOCTYPE HTML>
+<html>
+
+<head>
+    <meta charset="utf-8">
+
+    <title>{{page_title}}</title>
+{#
+    {% if mathjax_url %}
+    <script type="text/javascript" src="{{mathjax_url}}?config={{mathjax_config}}&amp;delayStartupUntil=configured" charset="utf-8"></script>
+    {% endif %}
+#}
+</head>
+
+
+
+<body>
+
+<script id='jupyter-config-data' type="application/json">{
+  "baseUrl": "{{base_url | urlencode}}",
+  "wsUrl": "{{ws_url| urlencode}}",
+  "notebookPath": "{{notebook_path | urlencode}}"
+}</script>
+<script src="{{static_prefix}}/bundle.js" type="text/javascript" charset="utf-8"></script>
+
+</body>
+
+</html>

+ 32 - 0
jupyterlab/package.json

@@ -0,0 +1,32 @@
+{
+  "private": true,
+  "name": "jupyterlab-extension",
+  "version": "0.0.1",
+  "description": "JupyterLab extension",
+  "main": "lib/index.js",
+  "typings": "lib/index.d.ts",
+  "dependencies": {
+    "es6-promise": "^3.1.2",
+    "font-awesome": "^4.6.1",
+    "jupyterlab": "../",
+    "phosphide": "^0.9.4"
+  },
+  "devDependencies": {
+    "css-loader": "^0.23.1",
+    "file-loader": "^0.8.5",
+    "json-loader": "^0.5.4",
+    "rimraf": "^2.5.0",
+    "style-loader": "^0.13.0",
+    "typescript": "^1.7.5",
+    "url-loader": "^0.5.7",
+    "webpack": "^1.12.11"
+  },
+  "scripts": {
+    "clean": "rimraf build",
+    "build": "npm update jupyterlab && webpack --config webpack.conf.js",
+    "postinstall": "npm dedupe",
+    "test": "echo 'no tests specified'"
+  },
+  "author": "Project Jupyter",
+  "license": "BSD-3-Clause"
+}

+ 41 - 0
jupyterlab/webpack.conf.js

@@ -0,0 +1,41 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+// Support for Node 0.10
+// See https://github.com/webpack/css-loader/issues/144
+require('es6-promise').polyfill();
+
+module.exports = {
+  entry: './index.js',
+  output: {
+    path: __dirname + "/build",
+    filename: "bundle.js",
+    publicPath: "lab/"
+  },
+  node: {
+    fs: "empty"
+  },
+  debug: true,
+  bail: true,
+  devtool: 'source-map',
+  module: {
+    loaders: [
+      { test: /\.css$/, loader: 'style-loader!css-loader' },
+      { test: /\.json$/, loader: 'json-loader' },
+      { test: /\.html$/, loader: 'file'},
+      // jquery-ui loads some images
+      { test: /\.(jpg|png|gif)$/, loader: "file" },
+      // required to load font-awesome
+      { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&minetype=application/font-woff" },
+      { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&minetype=application/font-woff" },
+      { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&minetype=application/octet-stream" },
+      { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
+      { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url?limit=10000&minetype=image/svg+xml" }
+    ]
+  },
+  externals: {
+      "base/js/namespace": "base/js/namespace",
+      "notebook/js/outputarea": "notebook/js/outputarea",
+      "services/kernels/comm": "services/kernels/comm"
+  }
+}

+ 158 - 0
setup.py

@@ -0,0 +1,158 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) Jupyter Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import print_function
+from setuptools import setup, find_packages, Command
+from setuptools.command.sdist import sdist
+from setuptools.command.build_py import build_py
+from setuptools.command.egg_info import egg_info
+from subprocess import check_call
+import os
+import sys
+import platform
+
+here = os.path.dirname(os.path.abspath(__file__))
+node_root = os.path.join(here, 'jupyterlab')
+is_repo = os.path.exists(os.path.join(here, '.git'))
+
+npm_path = os.pathsep.join([
+    os.path.join(node_root, 'node_modules', '.bin'),
+    os.environ.get('PATH', os.defpath),
+])
+
+from distutils import log
+log.set_verbosity(log.DEBUG)
+log.info('setup.py entered')
+log.info('$PATH=%s' % os.environ['PATH'])
+
+LONG_DESCRIPTION = 'A pre-alpha JupyterLab demo.'
+
+def js_prerelease(command, strict=False):
+    """decorator for building minified js/css prior to another command"""
+    class DecoratedCommand(command):
+        def run(self):
+            jsdeps = self.distribution.get_command_obj('jsdeps')
+            if not is_repo and all(os.path.exists(t) for t in jsdeps.targets):
+                # sdist, nothing to do
+                command.run(self)
+                return
+
+            try:
+                self.distribution.run_command('jsdeps')
+            except Exception as e:
+                missing = [t for t in jsdeps.targets if not os.path.exists(t)]
+                if strict or missing:
+                    log.warn('rebuilding js and css failed')
+                    if missing:
+                        log.error('missing files: %s' % missing)
+                    raise e
+                else:
+                    log.warn('rebuilding js and css failed (not a problem)')
+                    log.warn(str(e))
+            command.run(self)
+            update_package_data(self.distribution)
+    return DecoratedCommand
+
+def update_package_data(distribution):
+    """update package_data to catch changes during setup"""
+    build_py = distribution.get_command_obj('build_py')
+    # distribution.package_data = find_package_data()
+    # re-init build_py options which load package_data
+    build_py.finalize_options()
+
+
+class NPM(Command):
+    description = 'install package.json dependencies using npm'
+
+    user_options = []
+
+    node_modules = os.path.join(node_root, 'node_modules')
+
+    targets = [
+        os.path.join(here, 'jupyterlab', 'build', 'bundle.js'),
+    ]
+
+    def initialize_options(self):
+        pass
+
+    def finalize_options(self):
+        pass
+
+    def has_npm(self):
+        try:
+            check_call(['npm', '--version'])
+            return True
+        except:
+            return False
+
+    def should_run_npm_install(self):
+        package_json = os.path.join(node_root, 'package.json')
+        node_modules_exists = os.path.exists(self.node_modules)
+        return self.has_npm()
+
+    def run(self):
+        has_npm = self.has_npm()
+        if not has_npm:
+            log.error("`npm` unavailable.  If you're running this command using sudo, make sure `npm` is available to sudo")
+
+        env = os.environ.copy()
+        env['PATH'] = npm_path
+
+        if self.should_run_npm_install():
+            log.info("Installing build dependencies with npm.  This may take a while...")
+            check_call(['npm', 'install'], cwd=node_root, stdout=sys.stdout, stderr=sys.stderr)
+            check_call(['npm', 'run', 'build'], cwd=node_root, stdout=sys.stdout, stderr=sys.stderr)
+            os.utime(self.node_modules, None)
+
+        for t in self.targets:
+            if not os.path.exists(t):
+                msg = 'Missing file: %s' % t
+                if not has_npm:
+                    msg += '\nnpm is required to build a development version of widgetsnbextension'
+                raise ValueError(msg)
+
+        # update package data in case this created new files
+        update_package_data(self.distribution)
+
+import json
+with open(os.path.join(here, 'package.json')) as f:
+    packagejson = json.load(f)
+
+setup_args = {
+    'name': 'jupyterlab',
+    'version': packagejson['version'],
+    'description': 'A pre-alpha Jupyter lab environment notebook server extension.',
+    'long_description': LONG_DESCRIPTION,
+    'License': 'BSD',
+    'include_package_data': True,
+    'install_requires': ['notebook>=4.2.0'],
+    'packages': find_packages(),
+    'zip_safe': False,
+    'package_data': {'jupyterlab': [
+        'build/*',
+        'lab.html'
+    ]},
+    'cmdclass': {
+        'build_py': js_prerelease(build_py),
+        'egg_info': js_prerelease(egg_info),
+        'sdist': js_prerelease(sdist, strict=True),
+        'jsdeps': NPM,
+    },
+    'author': 'Jupyter Development Team',
+    'author_email': 'jupyter@googlegroups.com',
+    'url': 'http://jupyter.org',
+    'keywords': ['ipython', 'jupyter', 'Web'],
+    'classifiers': [
+        'Development Status :: 1 - Alpha',
+        'Intended Audience :: Developers',
+        'Intended Audience :: Science/Research',
+        'License :: OSI Approved :: BSD License',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+    ],
+}
+
+setup(**setup_args)