@ -0,0 +1,19 @@
|
||||
[bumpversion]
|
||||
current_version = 7, 0, 0, 'alpha', 0
|
||||
commit = False
|
||||
tag = False
|
||||
parse = (?P<major>\d+)\,\ (?P<minor>\d+)\,\ (?P<patch>\d+)\,\ \'(?P<release>\S+)\'\,\ (?P<build>\d+)
|
||||
serialize =
|
||||
{major}, {minor}, {patch}, '{release}', {build}
|
||||
|
||||
[bumpversion:part:release]
|
||||
optional_value = final
|
||||
values =
|
||||
alpha
|
||||
beta
|
||||
candidate
|
||||
final
|
||||
|
||||
[bumpversion:part:build]
|
||||
|
||||
[bumpversion:file:notebook/_version.py]
|
||||
@ -1,5 +1,22 @@
|
||||
*.min.js
|
||||
*components*
|
||||
*node_modules*
|
||||
*built*
|
||||
*build*
|
||||
lint-staged.config.js
|
||||
.eslintrc.js
|
||||
|
||||
node_modules
|
||||
**/build
|
||||
**/lib
|
||||
**/node_modules
|
||||
**/mock_packages
|
||||
**/static
|
||||
**/typings
|
||||
**/schemas
|
||||
**/themes
|
||||
coverage
|
||||
*.map.js
|
||||
*.bundle.js
|
||||
|
||||
# jetbrains IDE stuff
|
||||
.idea/
|
||||
|
||||
# ms IDE stuff
|
||||
.history/
|
||||
.vscode/
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es6: true,
|
||||
commonjs: true,
|
||||
node: true,
|
||||
'jest/globals': true
|
||||
},
|
||||
root: true,
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:jest/recommended'
|
||||
],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.eslint.json',
|
||||
sourceType: 'module'
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'jest'],
|
||||
rules: {
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'error',
|
||||
{
|
||||
selector: 'interface',
|
||||
format: ['PascalCase'],
|
||||
custom: {
|
||||
regex: '^I[A-Z]',
|
||||
match: true
|
||||
}
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': ['warn', { args: 'none' }],
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/no-empty-interface': 'off',
|
||||
'@typescript-eslint/quotes': [
|
||||
'error',
|
||||
'single',
|
||||
{ avoidEscape: true, allowTemplateLiterals: false }
|
||||
],
|
||||
'jest/no-done-callback': 'off',
|
||||
curly: ['error', 'all'],
|
||||
eqeqeq: 'error',
|
||||
'prefer-arrow-callback': 'error'
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"semi": 1,
|
||||
"no-cond-assign": 2,
|
||||
"no-debugger": 2,
|
||||
"comma-dangle": 0,
|
||||
"no-unreachable" : 2
|
||||
}
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"semi": 1,
|
||||
"no-cond-assign": 2,
|
||||
"no-debugger": 2,
|
||||
"comma-dangle": 0,
|
||||
"no-unreachable": 2
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,44 @@
|
||||
name: "Build Jupyter Notebook"
|
||||
description: "Build Jupyter Notebook from source"
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Base Setup
|
||||
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
python -m pip install --upgrade jupyter_packaging~=0.10 "jupyterlab>=4.0.0a20,<5" build
|
||||
|
||||
- name: Build pypi distributions
|
||||
shell: bash
|
||||
run: |
|
||||
python -m build
|
||||
|
||||
- name: Build npm distributions
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir pkgs
|
||||
jlpm lerna exec -- npm pack
|
||||
cp packages/*/*.tgz pkgs
|
||||
|
||||
- name: Build checksum file
|
||||
shell: bash
|
||||
run: |
|
||||
cd dist
|
||||
sha256sum * | tee SHA256SUMS
|
||||
cd ../pkgs
|
||||
sha256sum * | tee SHA256SUMS
|
||||
|
||||
- name: Upload distributions
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: notebook-dist-${{ github.run_number }}
|
||||
path: ./dist
|
||||
|
||||
- name: Upload distributions
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: notebook-pkgs-${{ github.run_number }}
|
||||
path: ./pkgs
|
||||
@ -0,0 +1,15 @@
|
||||
name: Binder Badge
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
binder:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: jupyterlab/maintainer-tools/.github/actions/binder-link@v1
|
||||
with:
|
||||
github_token: ${{ secrets.github_token }}
|
||||
url_path: tree
|
||||
@ -0,0 +1,110 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
permissions:
|
||||
contents:
|
||||
write
|
||||
|
||||
env:
|
||||
PIP_DISABLE_PIP_VERSION_CHECK: 1
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -l {0}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
uses: ./.github/actions/build-dist
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Base Setup
|
||||
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U jupyter_packaging~=0.10
|
||||
|
||||
- name: Install the package
|
||||
run: |
|
||||
python -m pip install .
|
||||
jupyter labextension list 2>&1 | grep -ie "@jupyter-notebook/lab-extension.*enabled.*ok" -
|
||||
jupyter server extension list 2>&1 | grep -ie "notebook.*enabled" -
|
||||
python -m jupyterlab.browser_check
|
||||
|
||||
- name: Lint
|
||||
run: |
|
||||
jlpm
|
||||
jlpm run eslint:check
|
||||
jlpm run prettier:check
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
jlpm run build:test
|
||||
jlpm run test
|
||||
|
||||
install:
|
||||
needs: [build]
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu, macos, windows]
|
||||
python: ['3.7', '3.10']
|
||||
include:
|
||||
- python: '3.7'
|
||||
dist: 'notebook*.tar.gz'
|
||||
- python: '3.10'
|
||||
dist: 'notebook*.whl'
|
||||
- os: windows
|
||||
py_cmd: python
|
||||
- os: macos
|
||||
py_cmd: python3
|
||||
- os: ubuntu
|
||||
py_cmd: python
|
||||
steps:
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
architecture: 'x64'
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: notebook-dist-${{ github.run_number }}
|
||||
path: ./dist
|
||||
- name: Install the prerequisites
|
||||
run: |
|
||||
${{ matrix.py_cmd }} -m pip install pip wheel
|
||||
- name: Install the package
|
||||
run: |
|
||||
cd dist
|
||||
${{ matrix.py_cmd }} -m pip install -vv ${{ matrix.dist }}
|
||||
- name: Validate environment
|
||||
run: |
|
||||
${{ matrix.py_cmd }} -m pip freeze
|
||||
${{ matrix.py_cmd }} -m pip check
|
||||
- name: Validate the install
|
||||
run: |
|
||||
jupyter labextension list
|
||||
jupyter labextension list 2>&1 | grep -ie "@jupyter-notebook/lab-extension.*enabled.*ok" -
|
||||
jupyter server extension list
|
||||
jupyter server extension list 2>&1 | grep -ie "notebook.*enabled" -
|
||||
jupyter notebook --version
|
||||
jupyter notebook --help
|
||||
@ -0,0 +1,80 @@
|
||||
name: Build Utilities
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
pull_request:
|
||||
branches: '*'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash -l {0}
|
||||
|
||||
jobs:
|
||||
versioning:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Base Setup
|
||||
uses: jupyterlab/maintainer-tools/.github/actions/base-setup@v1
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U "jupyterlab>=4.0.0a20,<5" jupyter_packaging~=0.10
|
||||
jlpm
|
||||
jlpm run build
|
||||
|
||||
- name: Configure git identity to commit
|
||||
run: |
|
||||
git config --global user.email "you@example.com"
|
||||
git config --global user.name "Your Name"
|
||||
|
||||
- name: Reset version
|
||||
run: |
|
||||
# TODO: improve this with a mock package?
|
||||
# This step is to ensure the workflow always starts with a final version
|
||||
sed -i -E "s/VersionInfo\(.*\)/VersionInfo\(9, 8, 7, 'final', 0\)/" notebook/_version.py
|
||||
sed -i -E "s/current_version = .*/current_version = 9, 8, 7, 'final', 0/" .bumpversion.cfg
|
||||
jlpm run lerna version 9.8.7 --no-push --force-publish --no-git-tag-version --yes
|
||||
git commit -am "Release 9.8.7"
|
||||
|
||||
- name: Patch Release
|
||||
run: |
|
||||
jlpm release:patch --force
|
||||
|
||||
- name: Minor Release
|
||||
run: |
|
||||
jlpm release:bump minor --force
|
||||
|
||||
- name: Release Cycle
|
||||
run: |
|
||||
# beta
|
||||
jlpm release:bump release --force
|
||||
# rc
|
||||
jlpm release:bump release --force
|
||||
# final
|
||||
jlpm release:bump release --force
|
||||
|
||||
- name: Major Release
|
||||
run: |
|
||||
jlpm release:bump major --force
|
||||
|
||||
npm:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
architecture: 'x64'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install -U "jupyterlab>=4.0.0a20,<5" jupyter_packaging~=0.10 pip
|
||||
jlpm
|
||||
jlpm run build
|
||||
@ -1,61 +0,0 @@
|
||||
name: Linux JS Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
pull_request:
|
||||
branches: '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu, macos]
|
||||
group: [notebook, base, services]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: 3.8
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.x'
|
||||
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
env:
|
||||
cache-name: cache-node-modules
|
||||
with:
|
||||
# npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-${{ env.cache-name }}-
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
|
||||
- name: Cache pip on Linux
|
||||
uses: actions/cache@v1
|
||||
if: startsWith(runner.os, 'Linux')
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ matrix.python }}-${{ hashFiles('**/requirements.txt', 'setup.py') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-${{ matrix.python }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install --upgrade setuptools wheel
|
||||
npm install
|
||||
npm install -g casperjs@1.1.3 phantomjs-prebuilt@2.1.7
|
||||
pip install .[test]
|
||||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
python -m notebook.jstest ${{ matrix.group }}
|
||||
@ -1,53 +0,0 @@
|
||||
# The NBConvert Service requires pandoc. Instead of testing
|
||||
# Pandoc on every operating system (which should already be
|
||||
# done in nbconvert directly), we'll only test these services
|
||||
# on ubuntu where we can easily load Pandoc from a Github
|
||||
# Actions docker image (this docker image is not on other
|
||||
# operating systems).
|
||||
name: NBConvert Service Tests
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
pull_request:
|
||||
branches: '*'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ '3.7', '3.8', '3.9', '3.10' ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- name: Install Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: 'x64'
|
||||
- name: Setup Pandoc
|
||||
uses: r-lib/actions/setup-pandoc@v1
|
||||
- name: Upgrade packaging dependencies
|
||||
run: |
|
||||
pip install --upgrade pip setuptools wheel
|
||||
- name: Get pip cache dir
|
||||
id: pip-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.pip-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-${{ matrix.python-version }}-
|
||||
${{ runner.os }}-pip-
|
||||
- name: Install the Python dependencies
|
||||
run: |
|
||||
pip install -e .[test]
|
||||
- name: Run NBConvert Tests
|
||||
run: |
|
||||
pytest notebook/nbconvert/tests/
|
||||
- name: Run NBConvert Service Tests
|
||||
run: |
|
||||
pytest notebook/services/nbconvert/tests/
|
||||
@ -1,53 +0,0 @@
|
||||
name: Python Tests
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
pull_request:
|
||||
branches: '*'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu, macos, windows]
|
||||
python-version: [ '3.7', '3.8', '3.9', '3.10' ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
- name: Install Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: 'x64'
|
||||
- name: Upgrade packaging dependencies
|
||||
run: |
|
||||
pip install --upgrade pip setuptools wheel --user
|
||||
- name: Get pip cache dir
|
||||
id: pip-cache
|
||||
run: |
|
||||
echo "::set-output name=dir::$(pip cache dir)"
|
||||
- name: Cache pip
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ${{ steps.pip-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-${{ matrix.python-version }}-
|
||||
${{ runner.os }}-pip-
|
||||
- name: Install the Python dependencies
|
||||
run: |
|
||||
pip install -e .[test] codecov
|
||||
- name: List installed packages
|
||||
run: |
|
||||
pip freeze
|
||||
pip check
|
||||
- name: Run Server-side tests
|
||||
run: |
|
||||
pytest -vv --cov notebook --cov-branch --cov-report term-missing:skip-covered --ignore-glob=notebook/tests/selenium/* --ignore-glob=notebook/nbconvert/tests/* --ignore-glob=notebook/services/nbconvert/tests/*
|
||||
- name: Run Integration Tests
|
||||
run: |
|
||||
pytest -v notebook/tests/test_notebookapp_integration.py --integration_tests
|
||||
- name: Coverage
|
||||
run: |
|
||||
codecov
|
||||
@ -1,46 +0,0 @@
|
||||
name: Selenium Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: '*'
|
||||
pull_request:
|
||||
branches: '*'
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu, macos]
|
||||
python-version: [ '3.7', '3.8', '3.9', '3.10' ]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: 'x64'
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '12.x'
|
||||
|
||||
- name: Install JS
|
||||
run: |
|
||||
npm install
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
python -m pip install -U pip setuptools wheel
|
||||
pip install --upgrade selenium
|
||||
pip install pytest
|
||||
pip install .[test]
|
||||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
export JUPYTER_TEST_BROWSER=firefox
|
||||
export MOZ_HEADLESS=1
|
||||
pytest -sv notebook/tests/selenium
|
||||
@ -0,0 +1,100 @@
|
||||
name: UI Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
uses: ./.github/actions/build-dist
|
||||
|
||||
ui-tests:
|
||||
needs: [build]
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
browser: [firefox, chromium]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
architecture: 'x64'
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: notebook-dist-${{ github.run_number }}
|
||||
path: ./dist
|
||||
|
||||
- name: Install the prerequisites
|
||||
run: |
|
||||
python -m pip install pip wheel
|
||||
|
||||
- name: Install the package
|
||||
run: |
|
||||
cd dist
|
||||
python -m pip install -vv notebook*.whl
|
||||
|
||||
- name: Install the test dependencies
|
||||
run: |
|
||||
cd ui-tests
|
||||
jlpm --frozen-lockfile
|
||||
jlpm playwright install
|
||||
|
||||
- name: Start Jupyter Notebook
|
||||
run: |
|
||||
cd ui-tests
|
||||
jlpm start:detached
|
||||
|
||||
- name: Wait for Jupyter Notebook
|
||||
uses: ifaxity/wait-on-action@v1
|
||||
with:
|
||||
resource: http-get://127.0.0.1:8888/
|
||||
timeout: 360000
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
cd ui-tests
|
||||
jlpm test --browser ${{ matrix.browser }}
|
||||
|
||||
- name: Upload Playwright Test assets
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: notebook-${{ matrix.browser }}-test-assets
|
||||
path: |
|
||||
ui-tests/test-results
|
||||
|
||||
- name: Upload Playwright Test report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: notebook-${{ matrix.browser }}-test-report
|
||||
path: |
|
||||
ui-tests/playwright-report
|
||||
|
||||
- name: Update snapshots
|
||||
if: failure()
|
||||
run: |
|
||||
cd ui-tests
|
||||
# remove previous snapshots from other browser
|
||||
jlpm rimraf "test/**/*-snapshots/*.png"
|
||||
# generate new snapshots
|
||||
jlpm run test:update --browser ${{ matrix.browser }}
|
||||
|
||||
- name: Upload updated snapshots
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: notebook-${{ matrix.browser }}-updated-snapshots
|
||||
path: ui-tests/test
|
||||
@ -1,58 +1,126 @@
|
||||
MANIFEST
|
||||
build
|
||||
dist
|
||||
_build
|
||||
docs/man/*.gz
|
||||
docs/source/api/generated
|
||||
docs/source/config.rst
|
||||
docs/gh-pages
|
||||
notebook/i18n/*/LC_MESSAGES/*.mo
|
||||
notebook/i18n/*/LC_MESSAGES/nbjs.json
|
||||
notebook/static/components
|
||||
notebook/static/style/*.min.css*
|
||||
notebook/static/*/js/built/
|
||||
notebook/static/*/built/
|
||||
notebook/static/built/
|
||||
notebook/static/*/js/main.min.js*
|
||||
notebook/static/lab/*bundle.js
|
||||
node_modules
|
||||
*.py[co]
|
||||
__pycache__
|
||||
*.egg-info
|
||||
*~
|
||||
*.bak
|
||||
*.bundle.*
|
||||
lib/
|
||||
node_modules/
|
||||
*.egg-info/
|
||||
.ipynb_checkpoints
|
||||
.tox
|
||||
.DS_Store
|
||||
\#*#
|
||||
.#*
|
||||
*.tsbuildinfo
|
||||
|
||||
# Created by https://www.gitignore.io/api/python
|
||||
# Edit at https://www.gitignore.io/?templates=python
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# 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/
|
||||
.nox/
|
||||
.coverage
|
||||
.pytest_cache
|
||||
src
|
||||
|
||||
*.swp
|
||||
*.map
|
||||
.idea/
|
||||
Read the Docs
|
||||
config.rst
|
||||
*.iml
|
||||
/.project
|
||||
/.pydevproject
|
||||
|
||||
package-lock.json
|
||||
geckodriver.log
|
||||
*.iml
|
||||
|
||||
# jetbrains IDE stuff
|
||||
*.iml
|
||||
.idea/
|
||||
|
||||
# ms IDE stuff
|
||||
*.code-workspace
|
||||
.history
|
||||
.vscode
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# copied changelog
|
||||
docs/source/changelog.md
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
.jupyter_releaser_checkout
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# OS X stuff
|
||||
*.DS_Store
|
||||
|
||||
# End of https://www.gitignore.io/api/python
|
||||
|
||||
_temp_extension
|
||||
junit.xml
|
||||
[uU]ntitled*
|
||||
notebook/static/*
|
||||
!notebook/static/favicons
|
||||
notebook/labextension
|
||||
notebook/schemas
|
||||
|
||||
# playwright
|
||||
ui-tests/test-results
|
||||
ui-tests/playwright-report
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
@ -1,149 +0,0 @@
|
||||
A. J. Holyoake <a.j.holyoake@gmail.com> ajholyoake <a.j.holyoake@gmail.com>
|
||||
Aaron Culich <aculich@gmail.com> Aaron Culich <aculich@eecs.berkeley.edu>
|
||||
Aron Ahmadia <aron@ahmadia.net> ahmadia <aron@ahmadia.net>
|
||||
Benjamin Ragan-Kelley <benjaminrk@gmail.com> <minrk@Mercury.local>
|
||||
Benjamin Ragan-Kelley <benjaminrk@gmail.com> Min RK
|
||||
Benjamin Ragan-Kelley <benjaminrk@gmail.com> MinRK <benjaminrk@gmail.com>
|
||||
Barry Wark <barrywark@gmail.com> Barry Wark <barrywarkatgmaildotcom>
|
||||
Ben Edwards <bedwards@cs.unm.edu> Ben Edwards <bedwards@sausage.(none)>
|
||||
Bradley M. Froehle <brad.froehle@gmail.com> Bradley M. Froehle <bfroehle@math.berkeley.edu>
|
||||
Bradley M. Froehle <brad.froehle@gmail.com> Bradley Froehle <brad.froehle@gmail.com>
|
||||
Brandon Parsons <brandon@parsonstx.com> Brandon Parsons <brandon.parsons@hp.com>
|
||||
Brian E. Granger <ellisonbg@gmail.com> Brian Granger
|
||||
Brian E. Granger <ellisonbg@gmail.com> Brian Granger <>
|
||||
Brian E. Granger <ellisonbg@gmail.com> bgranger <>
|
||||
Brian E. Granger <ellisonbg@gmail.com> bgranger <bgranger@red>
|
||||
Christoph Gohlke <cgohlke@uci.edu> cgohlke <cgohlke@uci.edu>
|
||||
Cyrille Rossant <cyrille.rossant@gmail.com> rossant <rossant@github>
|
||||
Damián Avila <damianavila82@yahoo.com.ar> damianavila <damianavila82@yahoo.com.ar>
|
||||
Damián Avila <damianavila82@yahoo.com.ar> damianavila <damianavila@gmail.com>
|
||||
Damon Allen <damontallen@gmail.com> damontallen <damontallen@gmail.com>
|
||||
Darren Dale <dsdale24@gmail.com> darren.dale <>
|
||||
Darren Dale <dsdale24@gmail.com> Darren Dale <>
|
||||
Dav Clark <davclark@berkeley.edu> Dav Clark <>
|
||||
Dav Clark <davclark@berkeley.edu> Dav Clark <davclark@gmail.com>
|
||||
David Hirschfeld <david.hirschfeld@gazprom-mt.com> dhirschfeld <david.hirschfeld@gazprom-mt.com>
|
||||
David P. Sanders <dpsanders@gmail.com> David P. Sanders <dpsanders@ciencias.unam.mx>
|
||||
David Warde-Farley <wardefar@iro.umontreal.ca> David Warde-Farley <>
|
||||
Doug Blank <dblank@cs.brynmawr.edu> Doug Blank <doug.blank@gmail.com>
|
||||
Eugene Van den Bulke <eugene.van-den-bulke@gmail.com> Eugene Van den Bulke <eugene.vandenbulke@gmail.com>
|
||||
Evan Patterson <epatters@enthought.com> <epatters@EPattersons-MacBook-Pro.local>
|
||||
Evan Patterson <epatters@enthought.com> <epatters@evan-laptop.localdomain>
|
||||
Evan Patterson <epatters@enthought.com> <epatters@caltech.edu>
|
||||
Evan Patterson <epatters@enthought.com> <ejpatters@gmail.com>
|
||||
Evan Patterson <epatters@enthought.com> epatters <ejpatters@gmail.com>
|
||||
Evan Patterson <epatters@enthought.com> epatters <epatters@enthought.com>
|
||||
Ernie French <ernestfrench@gmail.com> Ernie French <ernie@gqpbj.com>
|
||||
Ernie French <ernestfrench@gmail.com> ernie french <ernestfrench@gmail.com>
|
||||
Ernie French <ernestfrench@gmail.com> ernop <ernestfrench@gmail.com>
|
||||
Fernando Perez <Fernando.Perez@berkeley.edu> <fperez.net@gmail.com>
|
||||
Fernando Perez <Fernando.Perez@berkeley.edu> Fernando Perez <fernando.perez@berkeley.edu>
|
||||
Fernando Perez <Fernando.Perez@berkeley.edu> fperez <>
|
||||
Fernando Perez <Fernando.Perez@berkeley.edu> fptest <>
|
||||
Fernando Perez <Fernando.Perez@berkeley.edu> fptest1 <>
|
||||
Fernando Perez <Fernando.Perez@berkeley.edu> Fernando Perez <fernando.perez@berkeley.edu>
|
||||
Fernando Perez <fernando.perez@berkeley.edu> Fernando Perez <>
|
||||
Fernando Perez <fernando.perez@berkeley.edu> Fernando Perez <fperez@maqroll>
|
||||
Frank Murphy <fpmurphy@mtu.edu> Frank Murphy <fmurphy@arbor.net>
|
||||
Gabriel Becker <gmbecker@ucdavis.edu> gmbecker <gmbecker@ucdavis.edu>
|
||||
Gael Varoquaux <gael.varoquaux@normalesup.org> gael.varoquaux <>
|
||||
Gael Varoquaux <gael.varoquaux@normalesup.org> gvaroquaux <gvaroquaux@gvaroquaux-desktop>
|
||||
Gael Varoquaux <gael.varoquaux@normalesup.org> Gael Varoquaux <>
|
||||
Ingolf Becker <ingolf.becker@googlemail.com> watercrossing <ingolf.becker@googlemail.com>
|
||||
Jake Vanderplas <jakevdp@gmail.com> Jake Vanderplas <vanderplas@astro.washington.edu>
|
||||
Jakob Gager <jakob.gager@gmail.com> jakobgager <jakob.gager@gmail.com>
|
||||
Jakob Gager <jakob.gager@gmail.com> jakobgager <gager@ilsb.tuwien.ac.at>
|
||||
Jakob Gager <jakob.gager@gmail.com> jakobgager <jakobgager@hotmail.com>
|
||||
Jason Grout <jgrout6@bloomberg.net> <jason.grout@drake.edu>
|
||||
Jason Grout <jgrout6@bloomberg.net> <jason-github@creativetrax.com>
|
||||
Jason Gors <jason.gors.work@gmail.com> jason gors <jason.gors.work@gmail.com>
|
||||
Jason Gors <jason.gors.work@gmail.com> jgors <jason.gors.work@gmail.com>
|
||||
Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens Hedegaard Nielsen <jhn@jhn-Znote.(none)>
|
||||
Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens H Nielsen <jenshnielsen@gmail.com>
|
||||
Jens Hedegaard Nielsen <jenshnielsen@gmail.com> Jens H. Nielsen <jenshnielsen@gmail.com>
|
||||
Jez Ng <jezreel@gmail.com> Jez Ng <me@jezng.com>
|
||||
Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <jonathan@LifebookMint.(none)>
|
||||
Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <jon.freder@gmail.com>
|
||||
Jonathan Frederic <jdfreder@calpoly.edu> Jonathan Frederic <xh3xx.goose@gmail.com>
|
||||
Jonathan Frederic <jdfreder@calpoly.edu> jon <jon.freder@gmail.com>
|
||||
Jonathan Frederic <jdfreder@calpoly.edu> U-Jon-PC\Jon <Jon@Jon-PC.(none)>
|
||||
Jonathan March <jmarch@enthought.com> Jonathan March <JDM@MarchRay.net>
|
||||
Jonathan March <jmarch@enthought.com> jdmarch <JDM@marchRay.net>
|
||||
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jörgen Stenarson <jorgen.stenarson@bostream.nu>
|
||||
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jorgen Stenarson <jorgen.stenarson@bostream.nu>
|
||||
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jorgen Stenarson <>
|
||||
Jörgen Stenarson <jorgen.stenarson@kroywen.se> jstenar <jorgen.stenarson@bostream.nu>
|
||||
Jörgen Stenarson <jorgen.stenarson@kroywen.se> jstenar <>
|
||||
Jörgen Stenarson <jorgen.stenarson@kroywen.se> Jörgen Stenarson <jorgen.stenarson@kroywen.se>
|
||||
Juergen Hasch <python@elbonia.de> juhasch <python@elbonia.de>
|
||||
Juergen Hasch <python@elbonia.de> juhasch <hasch@VMBOX.fritz.box>
|
||||
Julia Evans <julia@jvns.ca> Julia Evans <julia@stripe.com>
|
||||
Kester Tong <kestert@google.com> KesterTong <kestert@google.com>
|
||||
Kyle Kelley <rgbkrk@gmail.com> Kyle Kelley <kyle.kelley@rackspace.com>
|
||||
Kyle Kelley <rgbkrk@gmail.com> rgbkrk <rgbkrk@gmail.com>
|
||||
Laurent Dufréchou <laurent.dufrechou@gmail.com> <laurent.dufrechou@gmail.com>
|
||||
Laurent Dufréchou <laurent.dufrechou@gmail.com> <laurent@Pep>
|
||||
Laurent Dufréchou <laurent.dufrechou@gmail.com> laurent dufrechou <>
|
||||
Laurent Dufréchou <laurent.dufrechou@gmail.com> laurent.dufrechou <>
|
||||
Laurent Dufréchou <laurent.dufrechou@gmail.com> Laurent Dufrechou <>
|
||||
Laurent Dufréchou <laurent.dufrechou@gmail.com> laurent.dufrechou@gmail.com <>
|
||||
Laurent Dufréchou <laurent.dufrechou@gmail.com> ldufrechou <ldufrechou@PEP>
|
||||
Lorena Pantano <lorena.pantano@gmail.com> Lorena <lorena.pantano@gmail.com>
|
||||
Luis Pedro Coelho <luis@luispedro.org> Luis Pedro Coelho <lpc@cmu.edu>
|
||||
Marc Molla <marcmolla@gmail.com> marcmolla <marcmolla@gmail.com>
|
||||
Martín Gaitán <gaitan@gmail.com> Martín Gaitán <gaitan@phasety.com>
|
||||
Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias BUSSONNIER <bussonniermatthias@gmail.com>
|
||||
Matthias Bussonnier <bussonniermatthias@gmail.com> Bussonnier Matthias <bussonniermatthias@gmail.com>
|
||||
Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias BUSSONNIER <bussonniermatthias@umr168-curn-1-24x-6561.curie.fr>
|
||||
Matthias Bussonnier <bussonniermatthias@gmail.com> Matthias Bussonnier <carreau@Aspire.(none)>
|
||||
Michael Droettboom <mdboom@gmail.com> Michael Droettboom <mdroe@stsci.edu>
|
||||
Nicholas Bollweg <nick.bollweg@gmail.com> Nicholas Bollweg (Nick) <nick.bollweg@gmail.com>
|
||||
Nicolas Rougier <Nicolas.Rougier@inria.fr> <Nicolas.rougier@inria.fr>
|
||||
Nikolay Koldunov <koldunovn@gmail.com> Nikolay Koldunov <nikolay.koldunov@zmaw.de>
|
||||
Omar Andrés Zapata Mesa <andresete.chaos@gmail.com> Omar Andres Zapata Mesa <andresete.chaos@gmail.com>
|
||||
Omar Andrés Zapata Mesa <andresete.chaos@gmail.com> Omar Andres Zapata Mesa <omazapa@tuxhome>
|
||||
Pankaj Pandey <pankaj86@gmail.com> Pankaj Pandey <pankaj@enthought.com>
|
||||
Pascal Schetelat <pascal.schetelat@gmail.com> pascal-schetelat <pascal.schetelat@gmail.com>
|
||||
Paul Ivanov <pi@berkeley.edu> Paul Ivanov <pivanov314@gmail.com>
|
||||
Pauli Virtanen <pauli.virtanen@iki.fi> Pauli Virtanen <>
|
||||
Pauli Virtanen <pauli.virtanen@iki.fi> Pauli Virtanen <pav@iki.fi>
|
||||
Pierre Gerold <pierre.gerold@laposte.net> Pierre Gerold <gerold@crans.org>
|
||||
Pietro Berkes <pberkes@enthought.com> Pietro Berkes <pietro.berkes@googlemail.com>
|
||||
Piti Ongmongkolkul <piti118@gmail.com> piti118 <piti118@gmail.com>
|
||||
Prabhu Ramachandran <prabhu@enthought.com> Prabhu Ramachandran <>
|
||||
Puneeth Chaganti <punchagan@gmail.com> Puneeth Chaganti <punchagan@muse-amuse.in>
|
||||
Robert Kern <robert.kern@gmail.com> rkern <>
|
||||
Robert Kern <robert.kern@gmail.com> Robert Kern <rkern@enthought.com>
|
||||
Robert Kern <robert.kern@gmail.com> Robert Kern <rkern@Sacrilege.local>
|
||||
Robert Kern <robert.kern@gmail.com> Robert Kern <>
|
||||
Robert Marchman <bo.marchman@gmail.com> Robert Marchman <robert.l.marchman@dartmouth.edu>
|
||||
Satrajit Ghosh <satra@mit.edu> Satrajit Ghosh <satra@ba5.mit.edu>
|
||||
Satrajit Ghosh <satra@mit.edu> Satrajit Ghosh <satrajit.ghosh@gmail.com>
|
||||
Scott Sanderson <scoutoss@gmail.com> Scott Sanderson <ssanderson@quantopian.com>
|
||||
smithj1 <smithj1@LMC-022896.local> smithj1 <smithj1@LMC-022896.swisscom.com>
|
||||
smithj1 <smithj1@LMC-022896.local> smithj1 <smithj1@lmc-022896.local>
|
||||
Steven Johnson <steven.johnson@drake.edu> stevenJohnson <steven.johnson@drake.edu>
|
||||
Steven Silvester <steven.silvester@ieee.org> blink1073 <steven.silvester@ieee.org>
|
||||
S. Weber <s8weber@c4.usr.sh> s8weber <s8weber@c5.usr.sh>
|
||||
Stefan van der Walt <stefan@sun.ac.za> Stefan van der Walt <bzr@mentat.za.net>
|
||||
Silvia Vinyes <silvia.vinyes@gmail.com> Silvia <silvia@silvia-U44SG.(none)>
|
||||
Silvia Vinyes <silvia.vinyes@gmail.com> silviav12 <silvia.vinyes@gmail.com>
|
||||
Sylvain Corlay <scorlay@bloomberg.net> <sylvain.corlay@gmail.com>
|
||||
Sylvain Corlay <scorlay@bloomberg.net> sylvain.corlay <sylvain.corlay@gmail.com>
|
||||
Ted Drain <ted.drain@gmail.com> TD22057 <ted.drain@gmail.com>
|
||||
Théophile Studer <theo.studer@gmail.com> Théophile Studer <studer@users.noreply.github.com>
|
||||
Thomas Kluyver <takowl@gmail.com> Thomas <takowl@gmail.com>
|
||||
Thomas Spura <tomspur@fedoraproject.org> Thomas Spura <thomas.spura@gmail.com>
|
||||
Timo Paulssen <timonator@perpetuum-immobile.de> timo <timonator@perpetuum-immobile.de>
|
||||
vds <vds@VIVIAN> vds2212 <vds2212@VIVIAN>
|
||||
vds <vds@VIVIAN> vds <vds@vivian>
|
||||
Ville M. Vainio <vivainio@gmail.com> <vivainio2@WN-W0941>
|
||||
Ville M. Vainio <vivainio@gmail.com> ville <ville@VILLE-PC>
|
||||
Ville M. Vainio <vivainio@gmail.com> ville <ville@ville-desktop>
|
||||
Ville M. Vainio <vivainio@gmail.com> vivainio <>
|
||||
Ville M. Vainio <vivainio@gmail.com> Ville M. Vainio <vivainio@villev>
|
||||
Ville M. Vainio <vivainio@gmail.com> Ville M. Vainio <vivainio@ville_vmw>
|
||||
Walter Doerwald <walter@livinglogic.de> walter.doerwald <>
|
||||
Walter Doerwald <walter@livinglogic.de> Walter Doerwald <>
|
||||
W. Trevor King <wking@tremily.us> W. Trevor King <wking@drexel.edu>
|
||||
Yoval P. <yoval@gmx.com> y-p <yoval@gmx.com>
|
||||
@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
**/node_modules
|
||||
**/lib
|
||||
**/package.json
|
||||
**/static
|
||||
**/labextension
|
||||
build
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
# Contributing to Jupyter Notebook
|
||||
|
||||
Thanks for contributing to Jupyter Notebook!
|
||||
|
||||
Make sure to follow [Project Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/master/conduct/code_of_conduct.md)
|
||||
for a friendly and welcoming collaborative environment.
|
||||
|
||||
## Setting up a development environment
|
||||
|
||||
Note: You will need NodeJS to build the extension package.
|
||||
|
||||
The `jlpm` command is JupyterLab's pinned version of [yarn](https://yarnpkg.com/) that is installed with JupyterLab. You may use
|
||||
`yarn` or `npm` in lieu of `jlpm` below.
|
||||
|
||||
**Note**: we recomment using `mamba` to speed the creating of the environment.
|
||||
|
||||
```bash
|
||||
# create a new environment
|
||||
mamba create -n notebook -c conda-forge python nodejs -y
|
||||
|
||||
# activate the environment
|
||||
mamba activate notebook
|
||||
|
||||
# Install package in development mode
|
||||
pip install -e .
|
||||
|
||||
# Link the notebook extension and @jupyter-notebook schemas
|
||||
jlpm develop
|
||||
|
||||
# Enable the server extension
|
||||
jupyter server extension enable notebook
|
||||
```
|
||||
|
||||
`notebook` follows a monorepo structure. To build all the packages at once:
|
||||
|
||||
```bash
|
||||
jlpm build
|
||||
```
|
||||
|
||||
There is also a `watch` script to watch for changes and rebuild the app automatically:
|
||||
|
||||
```bash
|
||||
jlpm watch
|
||||
```
|
||||
|
||||
To make sure the `notebook` server extension is installed:
|
||||
|
||||
```bash
|
||||
$ jupyter server extension list
|
||||
Config dir: /home/username/.jupyter
|
||||
|
||||
Config dir: /home/username/miniforge3/envs/notebook/etc/jupyter
|
||||
jupyterlab enabled
|
||||
- Validating jupyterlab...
|
||||
jupyterlab 3.0.0 OK
|
||||
notebook enabled
|
||||
- Validating notebook...
|
||||
notebook 7.0.0a0 OK
|
||||
|
||||
Config dir: /usr/local/etc/jupyter
|
||||
```
|
||||
|
||||
Then start Jupyter Notebook with:
|
||||
|
||||
```bash
|
||||
jupyter notebook
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
To run the tests:
|
||||
|
||||
```bash
|
||||
jlpm run build:test
|
||||
jlpm run test
|
||||
```
|
||||
|
||||
There are also end to end tests to cover higher level user interactions, located in the [`ui-tests`](./ui-tests) folder. To run these tests:
|
||||
|
||||
```bash
|
||||
cd ui-tests
|
||||
# start a new Jupyter server in a terminal
|
||||
jlpm start
|
||||
|
||||
# in a new terminal, run the tests
|
||||
jlpm test
|
||||
```
|
||||
|
||||
The `test` script calls the Playwright test runner. You can pass additional arguments to `playwright` by appending parameters to the command. For example to run the test in headed mode, `jlpm test --headed`.
|
||||
|
||||
Checkout the [Playwright Command Line Reference](https://playwright.dev/docs/test-cli/) for more information about the available command line options.
|
||||
|
||||
Running the end to end tests in headful mode will trigger something like the following:
|
||||
|
||||

|
||||
@ -1,197 +0,0 @@
|
||||
Contributing to the Jupyter Notebook
|
||||
====================================
|
||||
|
||||
If you're reading this section, you're probably interested in contributing to
|
||||
Jupyter. Welcome and thanks for your interest in contributing!
|
||||
|
||||
Please take a look at the Contributor documentation, familiarize yourself with
|
||||
using the Jupyter Notebook, and introduce yourself on the mailing list and
|
||||
share what area of the project you are interested in working on.
|
||||
|
||||
General Guidelines
|
||||
------------------
|
||||
|
||||
For general documentation about contributing to Jupyter projects, see the
|
||||
`Project Jupyter Contributor Documentation`__.
|
||||
|
||||
__ https://jupyter.readthedocs.io/en/latest/contributing/content-contributor.html
|
||||
|
||||
|
||||
Setting Up a Development Environment
|
||||
------------------------------------
|
||||
|
||||
Installing Node.js and npm
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Building the Notebook from its GitHub source code requires some tools to
|
||||
create and minify JavaScript components and the CSS,
|
||||
specifically Node.js and Node's package manager, ``npm``.
|
||||
It should be node version ≥ 6.0.
|
||||
|
||||
If you use ``conda``, you can get them with::
|
||||
|
||||
conda install -c conda-forge nodejs
|
||||
|
||||
If you use `Homebrew <https://brew.sh/>`_ on Mac OS X::
|
||||
|
||||
brew install node
|
||||
|
||||
Installation on Linux may vary, but be aware that the `nodejs` or `npm` packages
|
||||
included in the system package repository may be too old to work properly.
|
||||
|
||||
You can also use the installer from the `Node.js website <https://nodejs.org>`_.
|
||||
|
||||
|
||||
Installing the Jupyter Notebook
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you have installed the dependencies mentioned above, use the following
|
||||
steps::
|
||||
|
||||
pip install --upgrade setuptools pip
|
||||
git clone https://github.com/jupyter/notebook
|
||||
cd notebook
|
||||
pip install -e .
|
||||
|
||||
If you are using a system-wide Python installation and you only want to install the notebook for you,
|
||||
you can add ``--user`` to the install commands.
|
||||
|
||||
Once you have done this, you can launch the master branch of Jupyter notebook
|
||||
from any directory in your system with::
|
||||
|
||||
jupyter notebook
|
||||
|
||||
Verification
|
||||
^^^^^^^^^^^^
|
||||
|
||||
While running the notebook, select one of your notebook files (the file will have the extension ``.ipynb``).
|
||||
In the top tab you will click on "Help" and then click on "About". In the pop window you will see information about the version of Jupyter that you are running. You will see "The version of the notebook server is:".
|
||||
If you are working in development mode, you will see that your version of Jupyter notebook will include the word "dev". If it does not include the word "dev", you are currently not working in development mode and should follow the steps below to uninstall and reinstall Jupyter.
|
||||
|
||||
Troubleshooting the Installation
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
If you do not see that your Jupyter Notebook is not running on dev mode, it's possible that you are
|
||||
running other instances of Jupyter Notebook. You can try the following steps:
|
||||
|
||||
1. Uninstall all instances of the notebook package. These include any installations you made using
|
||||
pip or conda.
|
||||
2. Run ``python3 -m pip install -e .`` in the notebook repository to install the notebook from there.
|
||||
3. Run ``npm run build`` to make sure the Javascript and CSS are updated and compiled.
|
||||
4. Launch with ``python3 -m notebook --port 8989``, and check that the browser is pointing to ``localhost:8989``
|
||||
(rather than the default 8888). You don't necessarily have to launch with port 8989, as long as you use
|
||||
a port that is neither the default nor in use, then it should be fine.
|
||||
5. Verify the installation with the steps in the previous section.
|
||||
|
||||
|
||||
Rebuilding JavaScript and CSS
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
There is a build step for the JavaScript and CSS in the notebook.
|
||||
To make sure that you are working with up-to-date code, you will need to run
|
||||
this command whenever there are changes to JavaScript or LESS sources::
|
||||
|
||||
npm run build
|
||||
|
||||
**IMPORTANT:** Don't forget to run ``npm run build`` after switching branches.
|
||||
When switching between branches of different versions (e.g. ``4.x`` and
|
||||
``master``), run ``pip install -e .``. If you have tried the above and still
|
||||
find that the notebook is not reflecting the current source code, try cleaning
|
||||
the repo with ``git clean -xfd`` and reinstalling with ``pip install -e .``.
|
||||
|
||||
Development Tip
|
||||
"""""""""""""""
|
||||
|
||||
When doing development, you can use this command to automatically rebuild
|
||||
JavaScript and LESS sources as they are modified::
|
||||
|
||||
npm run build:watch
|
||||
|
||||
Git Hooks
|
||||
"""""""""
|
||||
|
||||
If you want to automatically update dependencies and recompile JavaScript and
|
||||
CSS after checking out a new commit, you can install post-checkout and
|
||||
post-merge hooks which will do it for you::
|
||||
|
||||
git-hooks/install-hooks.sh
|
||||
|
||||
See ``git-hooks/README.md`` for more details.
|
||||
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
Python Tests
|
||||
^^^^^^^^^^^^
|
||||
|
||||
Install dependencies::
|
||||
|
||||
pip install -e '.[test]'
|
||||
|
||||
To run the Python tests, use::
|
||||
|
||||
pytest
|
||||
|
||||
If you want coverage statistics as well, you can run::
|
||||
|
||||
py.test --cov notebook -v --pyargs notebook
|
||||
|
||||
JavaScript Tests
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
To run the JavaScript tests, you will need to have PhantomJS and CasperJS
|
||||
installed::
|
||||
|
||||
npm install -g casperjs phantomjs-prebuilt
|
||||
|
||||
Then, to run the JavaScript tests::
|
||||
|
||||
python -m notebook.jstest [group]
|
||||
|
||||
where ``[group]`` is an optional argument that is a path relative to
|
||||
``notebook/tests/``.
|
||||
For example, to run all tests in ``notebook/tests/notebook``::
|
||||
|
||||
python -m notebook.jstest notebook
|
||||
|
||||
or to run just ``notebook/tests/notebook/deletecell.js``::
|
||||
|
||||
python -m notebook.jstest notebook/deletecell.js
|
||||
|
||||
|
||||
Building the Documentation
|
||||
--------------------------
|
||||
|
||||
To build the documentation you'll need `Sphinx <http://www.sphinx-doc.org/>`_,
|
||||
`pandoc <http://pandoc.org/>`_ and a few other packages.
|
||||
|
||||
To install (and activate) a conda environment named ``notebook_docs``
|
||||
containing all the necessary packages (except pandoc), use::
|
||||
|
||||
conda create -n notebook_docs pip
|
||||
conda activate notebook_docs # Linux and OS X
|
||||
activate notebook_docs # Windows
|
||||
pip install .[docs]
|
||||
|
||||
If you want to install the necessary packages with ``pip``, use the following instead::
|
||||
|
||||
pip install .[docs]
|
||||
|
||||
Once you have installed the required packages, you can build the docs with::
|
||||
|
||||
cd docs
|
||||
make html
|
||||
|
||||
After that, the generated HTML files will be available at
|
||||
``build/html/index.html``. You may view the docs in your browser.
|
||||
|
||||
You can automatically check if all hyperlinks are still valid::
|
||||
|
||||
make linkcheck
|
||||
|
||||
Windows users can find ``make.bat`` in the ``docs`` folder.
|
||||
|
||||
You should also have a look at the `Project Jupyter Documentation Guide`__.
|
||||
|
||||
__ https://jupyter.readthedocs.io/en/latest/contributing/docs-contributions/index.html
|
||||
@ -1,80 +1,9 @@
|
||||
# Making a Release of Notebook
|
||||
# Releasing Jupyter Notebook
|
||||
|
||||
## Using `jupyter_releaser`
|
||||
## Automated releases
|
||||
|
||||
The recommended way to make a release is to use [`jupyter_releaser`](https://github.com/jupyter-server/jupyter_releaser#checklist-for-adoption).
|
||||
|
||||
## Manual Release Process
|
||||
We follow a similar bump strategy as in JupyterLab: https://github.com/jupyterlab/jupyterlab/blob/master/RELEASE.md#bump-version
|
||||
|
||||
### Start from a fresh git checkout and conda environment
|
||||
|
||||
#### Set the release branch
|
||||
|
||||
```bash
|
||||
export release_branch=master
|
||||
```
|
||||
|
||||
#### Create the git checkout
|
||||
|
||||
```bash
|
||||
git clone git@github.com:jupyter/notebook.git
|
||||
cd notebook
|
||||
git checkout ${release_banch}
|
||||
```
|
||||
|
||||
#### Create and activate the conda environment
|
||||
|
||||
```bash
|
||||
conda create -n notebook-release -c conda-forge jupyter
|
||||
conda activate notebook-release
|
||||
```
|
||||
|
||||
### Perform a local dev install
|
||||
|
||||
```bash
|
||||
pip install -ve .
|
||||
```
|
||||
|
||||
### Install release dependencies
|
||||
|
||||
```bash
|
||||
conda install -c conda-forge nodejs babel
|
||||
npm install -g po2json
|
||||
pip install jupyter_releaser # used for build dependencies (build, twine, tbump)
|
||||
```
|
||||
|
||||
### Update the version
|
||||
|
||||
```bash
|
||||
tbump --only-patch <new_version> # set the new version
|
||||
python setup.py jsversion
|
||||
git commit -am "Release $(python setup.py --version)"
|
||||
git tag $(python setup.py --version)
|
||||
```
|
||||
|
||||
### Create the artifacts
|
||||
|
||||
```bash
|
||||
rm -rf dist
|
||||
python -m build .
|
||||
```
|
||||
|
||||
### Upload the artifacts
|
||||
|
||||
```bash
|
||||
twine check dist/* && twine upload dist/*
|
||||
```
|
||||
|
||||
### Change back to dev version
|
||||
|
||||
```bash
|
||||
tbump --only-patch <dev_version> # Add the .dev suffix
|
||||
python setup.py jsversion
|
||||
git commit -am "Back to dev version"
|
||||
```
|
||||
|
||||
### Push the commits and tags
|
||||
|
||||
```bash
|
||||
git push origin ${release_branch} --tags
|
||||
```
|
||||
If you would still like to do the release manually instead, read below.
|
||||
|
||||
@ -0,0 +1,338 @@
|
||||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/dev_mode/index.js
|
||||
|
||||
import { PageConfig, URLExt } from '@jupyterlab/coreutils';
|
||||
|
||||
require('./style.js');
|
||||
require('./extraStyle.js');
|
||||
|
||||
function loadScript(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const newScript = document.createElement('script');
|
||||
newScript.onerror = reject;
|
||||
newScript.onload = resolve;
|
||||
newScript.async = true;
|
||||
document.head.appendChild(newScript);
|
||||
newScript.src = url;
|
||||
});
|
||||
}
|
||||
async function loadComponent(url, scope) {
|
||||
await loadScript(url);
|
||||
|
||||
// From MIT-licensed https://github.com/module-federation/module-federation-examples/blob/af043acd6be1718ee195b2511adf6011fba4233c/advanced-api/dynamic-remotes/app1/src/App.js#L6-L12
|
||||
// eslint-disable-next-line no-undef
|
||||
await __webpack_init_sharing__('default');
|
||||
const container = window._JUPYTERLAB[scope];
|
||||
// Initialize the container, it may provide shared modules and may need ours
|
||||
// eslint-disable-next-line no-undef
|
||||
await container.init(__webpack_share_scopes__.default);
|
||||
}
|
||||
|
||||
async function createModule(scope, module) {
|
||||
try {
|
||||
const factory = await window._JUPYTERLAB[scope].get(module);
|
||||
return factory();
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
`Failed to create module: package: ${scope}; module: ${module}`
|
||||
);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The main function
|
||||
*/
|
||||
async function main() {
|
||||
// load extra packages
|
||||
require('@jupyterlab/celltags');
|
||||
|
||||
const mimeExtensionsMods = [
|
||||
require('@jupyterlab/javascript-extension'),
|
||||
require('@jupyterlab/json-extension'),
|
||||
require('@jupyterlab/pdf-extension'),
|
||||
require('@jupyterlab/vega5-extension')
|
||||
];
|
||||
const mimeExtensions = await Promise.all(mimeExtensionsMods);
|
||||
|
||||
const disabled = [];
|
||||
// TODO: formalize the way the set of initial extensions and plugins are specified
|
||||
let baseMods = [
|
||||
// @jupyter-notebook plugins
|
||||
require('@jupyter-notebook/application-extension'),
|
||||
require('@jupyter-notebook/console-extension'),
|
||||
require('@jupyter-notebook/docmanager-extension'),
|
||||
require('@jupyter-notebook/documentsearch-extension'),
|
||||
require('@jupyter-notebook/help-extension'),
|
||||
require('@jupyter-notebook/notebook-extension'),
|
||||
// to handle opening new tabs after creating a new terminal
|
||||
require('@jupyter-notebook/terminal-extension'),
|
||||
|
||||
// @jupyterlab plugins
|
||||
require('@jupyterlab/application-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/application-extension:commands',
|
||||
'@jupyterlab/application-extension:context-menu',
|
||||
'@jupyterlab/application-extension:faviconbusy'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/apputils-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/apputils-extension:palette',
|
||||
'@jupyterlab/apputils-extension:settings',
|
||||
'@jupyterlab/apputils-extension:state',
|
||||
'@jupyterlab/apputils-extension:themes',
|
||||
'@jupyterlab/apputils-extension:themes-palette-menu',
|
||||
'@jupyterlab/apputils-extension:toolbar-registry'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/codemirror-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/codemirror-extension:services',
|
||||
'@jupyterlab/codemirror-extension:codemirror'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/completer-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/completer-extension:manager'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/console-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/console-extension:completer',
|
||||
'@jupyterlab/console-extension:cursor-position',
|
||||
'@jupyterlab/console-extension:factory',
|
||||
'@jupyterlab/console-extension:foreign',
|
||||
'@jupyterlab/console-extension:tracker'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/docmanager-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/docmanager-extension:plugin',
|
||||
'@jupyterlab/docmanager-extension:download'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/docprovider-extension'),
|
||||
require('@jupyterlab/documentsearch-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/documentsearch:plugin'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/filebrowser-extension:factory'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/fileeditor-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/fileeditor-extension:plugin'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/mainmenu-extension'),
|
||||
require('@jupyterlab/mathjax2-extension'),
|
||||
require('@jupyterlab/notebook-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/notebook-extension:code-console',
|
||||
'@jupyterlab/notebook-extension:export',
|
||||
'@jupyterlab/notebook-extension:factory',
|
||||
'@jupyterlab/notebook-extension:tracker',
|
||||
'@jupyterlab/notebook-extension:widget-factory'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyterlab/rendermime-extension'),
|
||||
require('@jupyterlab/shortcuts-extension'),
|
||||
// so new terminals can be create from the menu
|
||||
require('@jupyterlab/terminal-extension'),
|
||||
require('@jupyterlab/theme-light-extension'),
|
||||
require('@jupyterlab/theme-dark-extension'),
|
||||
require('@jupyterlab/translation-extension'),
|
||||
// Add the "Hub Control Panel" menu option when running in JupyterHub
|
||||
require('@jupyterlab/user-extension'),
|
||||
require('@jupyterlab/hub-extension')
|
||||
];
|
||||
|
||||
// The motivation here is to only load a specific set of plugins dependending on
|
||||
// the current page
|
||||
const page = PageConfig.getOption('notebookPage');
|
||||
switch (page) {
|
||||
case 'tree': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/filebrowser-extension:browser',
|
||||
'@jupyterlab/filebrowser-extension:download',
|
||||
'@jupyterlab/filebrowser-extension:file-upload-status',
|
||||
'@jupyterlab/filebrowser-extension:open-with',
|
||||
'@jupyterlab/filebrowser-extension:share-file'
|
||||
].includes(id)
|
||||
),
|
||||
require('@jupyter-notebook/tree-extension'),
|
||||
require('@jupyterlab/running-extension')
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'notebooks': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/completer-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/completer-extension:notebooks'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/tooltip-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/tooltip-extension:manager',
|
||||
'@jupyterlab/tooltip-extension:notebooks'
|
||||
].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'consoles': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/completer-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/completer-extension:consoles'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/tooltip-extension').default.filter(({ id }) =>
|
||||
[
|
||||
'@jupyterlab/tooltip-extension:manager',
|
||||
'@jupyterlab/tooltip-extension:consoles'
|
||||
].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
case 'edit': {
|
||||
baseMods = baseMods.concat([
|
||||
require('@jupyterlab/completer-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/completer-extension:files'].includes(id)
|
||||
),
|
||||
require('@jupyterlab/filebrowser-extension').default.filter(({ id }) =>
|
||||
['@jupyterlab/filebrowser-extension:browser'].includes(id)
|
||||
)
|
||||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over active plugins in an extension.
|
||||
*
|
||||
* #### Notes
|
||||
* This also populates the disabled
|
||||
*/
|
||||
function* activePlugins(extension) {
|
||||
// Handle commonjs or es2015 modules
|
||||
let exports;
|
||||
if (Object.prototype.hasOwnProperty.call(extension, '__esModule')) {
|
||||
exports = extension.default;
|
||||
} else {
|
||||
// CommonJS exports.
|
||||
exports = extension;
|
||||
}
|
||||
|
||||
let plugins = Array.isArray(exports) ? exports : [exports];
|
||||
for (let plugin of plugins) {
|
||||
if (PageConfig.Extension.isDisabled(plugin.id)) {
|
||||
disabled.push(plugin.id);
|
||||
continue;
|
||||
}
|
||||
yield plugin;
|
||||
}
|
||||
}
|
||||
|
||||
const extension_data = JSON.parse(
|
||||
PageConfig.getOption('federated_extensions')
|
||||
);
|
||||
|
||||
const mods = [];
|
||||
const federatedExtensionPromises = [];
|
||||
const federatedMimeExtensionPromises = [];
|
||||
const federatedStylePromises = [];
|
||||
|
||||
const extensions = await Promise.allSettled(
|
||||
extension_data.map(async data => {
|
||||
await loadComponent(
|
||||
`${URLExt.join(
|
||||
PageConfig.getOption('fullLabextensionsUrl'),
|
||||
data.name,
|
||||
data.load
|
||||
)}`,
|
||||
data.name
|
||||
);
|
||||
return data;
|
||||
})
|
||||
);
|
||||
|
||||
extensions.forEach(p => {
|
||||
if (p.status === 'rejected') {
|
||||
// There was an error loading the component
|
||||
console.error(p.reason);
|
||||
return;
|
||||
}
|
||||
|
||||
const data = p.value;
|
||||
if (data.extension) {
|
||||
federatedExtensionPromises.push(createModule(data.name, data.extension));
|
||||
}
|
||||
if (data.mimeExtension) {
|
||||
federatedMimeExtensionPromises.push(
|
||||
createModule(data.name, data.mimeExtension)
|
||||
);
|
||||
}
|
||||
if (data.style) {
|
||||
federatedStylePromises.push(createModule(data.name, data.style));
|
||||
}
|
||||
});
|
||||
|
||||
// Add the base frontend extensions
|
||||
const baseFrontendMods = await Promise.all(baseMods);
|
||||
baseFrontendMods.forEach(p => {
|
||||
for (let plugin of activePlugins(p)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the federated extensions.
|
||||
const federatedExtensions = await Promise.allSettled(
|
||||
federatedExtensionPromises
|
||||
);
|
||||
federatedExtensions.forEach(p => {
|
||||
if (p.status === 'fulfilled') {
|
||||
for (let plugin of activePlugins(p.value)) {
|
||||
mods.push(plugin);
|
||||
}
|
||||
} else {
|
||||
console.error(p.reason);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the federated mime extensions.
|
||||
const federatedMimeExtensions = await Promise.allSettled(
|
||||
federatedMimeExtensionPromises
|
||||
);
|
||||
federatedMimeExtensions.forEach(p => {
|
||||
if (p.status === 'fulfilled') {
|
||||
for (let plugin of activePlugins(p.value)) {
|
||||
mimeExtensions.push(plugin);
|
||||
}
|
||||
} else {
|
||||
console.error(p.reason);
|
||||
}
|
||||
});
|
||||
|
||||
// Load all federated component styles and log errors for any that do not
|
||||
(await Promise.allSettled(federatedStylePromises))
|
||||
.filter(({ status }) => status === 'rejected')
|
||||
.forEach(({ reason }) => {
|
||||
console.error(reason);
|
||||
});
|
||||
|
||||
const NotebookApp = require('@jupyter-notebook/application').NotebookApp;
|
||||
const app = new NotebookApp({ mimeExtensions });
|
||||
|
||||
app.registerPluginModules(mods);
|
||||
|
||||
// Expose global app instance when in dev mode or when toggled explicitly.
|
||||
const exposeAppInBrowser =
|
||||
(PageConfig.getOption('exposeAppInBrowser') || '').toLowerCase() === 'true';
|
||||
|
||||
if (exposeAppInBrowser) {
|
||||
window.jupyterapp = app;
|
||||
}
|
||||
|
||||
await app.start();
|
||||
}
|
||||
|
||||
window.addEventListener('load', main);
|
||||
@ -0,0 +1,240 @@
|
||||
{
|
||||
"name": "@jupyter-notebook/app",
|
||||
"version": "7.0.0-alpha.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "webpack",
|
||||
"build:prod": "webpack --mode=production",
|
||||
"clean": "rimraf build && jlpm run clean:static",
|
||||
"clean:static": "rimraf -g \"../notebook/static/!(favicons)\"",
|
||||
"prepublishOnly": "yarn run build",
|
||||
"watch": "webpack --config ./webpack.config.watch.js"
|
||||
},
|
||||
"resolutions": {
|
||||
"@jupyter-notebook/application": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/application-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/console-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/docmanager-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/documentsearch-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/help-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/notebook-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/terminal-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/tree-extension": "~7.0.0-alpha.0",
|
||||
"@jupyter-notebook/ui-components": "~7.0.0-alpha.0",
|
||||
"@jupyterlab/application": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/application-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/apputils": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/apputils-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/celltags": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/codeeditor": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/codemirror-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/completer": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/completer-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/console": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/console-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/coreutils": "~6.0.0-alpha.5",
|
||||
"@jupyterlab/docmanager": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/docmanager-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/docprovider": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/docprovider-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/documentsearch": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/documentsearch-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/filebrowser": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/filebrowser-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/fileeditor": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/fileeditor-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/hub-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/javascript-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/json-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/mainmenu": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/mainmenu-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/mathjax2-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/notebook": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/notebook-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/observables": "~5.0.0-alpha.5",
|
||||
"@jupyterlab/outputarea": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/pdf-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/rendermime": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/rendermime-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/rendermime-interfaces": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/running-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/services": "~7.0.0-alpha.5",
|
||||
"@jupyterlab/settingregistry": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/shared-models": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/shortcuts-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/statedb": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/statusbar": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/terminal": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/terminal-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/theme-dark-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/theme-light-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/tooltip": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/tooltip-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/translation": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/translation-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/ui-components": "~4.0.0-alpha.20",
|
||||
"@jupyterlab/user": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/user-extension": "~4.0.0-alpha.5",
|
||||
"@jupyterlab/vega5-extension": "~4.0.0-alpha.5",
|
||||
"@lumino/algorithm": "~1.9.1",
|
||||
"@lumino/application": "~1.28.1",
|
||||
"@lumino/commands": "~1.20.0",
|
||||
"@lumino/coreutils": "~1.12.0",
|
||||
"@lumino/disposable": "~1.10.1",
|
||||
"@lumino/domutils": "~1.8.1",
|
||||
"@lumino/dragdrop": "~1.14.0",
|
||||
"@lumino/messaging": "~1.10.1",
|
||||
"@lumino/properties": "~1.8.1",
|
||||
"@lumino/signaling": "~1.10.1",
|
||||
"@lumino/virtualdom": "~1.14.1",
|
||||
"@lumino/widgets": "~1.31.1",
|
||||
"react": "~17.0.2",
|
||||
"react-dom": "~17.0.2",
|
||||
"yjs": "~13.5.27"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jupyter-notebook/application": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/application-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/console-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/docmanager-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/documentsearch-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/help-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/notebook-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/terminal-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/tree-extension": "^7.0.0-alpha.0",
|
||||
"@jupyter-notebook/ui-components": "^7.0.0-alpha.0",
|
||||
"@jupyterlab/application-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/apputils-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/celltags": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/codemirror-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/completer-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/console-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/coreutils": "~6.0.0-alpha.5",
|
||||
"@jupyterlab/docmanager-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/docprovider-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/documentsearch-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/filebrowser-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/fileeditor-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/hub-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/javascript-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/json-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/mainmenu-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/mathjax2-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/notebook-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/pdf-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/rendermime-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/running-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/shortcuts-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/terminal-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/theme-dark-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/theme-light-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/tooltip-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/translation-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/user-extension": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/vega5-extension": "^4.0.0-alpha.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jupyterlab/builder": "^4.0.0-alpha.5",
|
||||
"@jupyterlab/buildutils": "^4.0.0-alpha.5",
|
||||
"@types/rimraf": "^3.0.0",
|
||||
"css-loader": "~5.0.1",
|
||||
"file-loader": "~5.0.2",
|
||||
"fs-extra": "^8.1.0",
|
||||
"glob": "~7.1.6",
|
||||
"mini-css-extract-plugin": "~0.9.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"raw-loader": "~4.0.0",
|
||||
"rimraf": "~3.0.2",
|
||||
"style-loader": "~1.0.1",
|
||||
"svg-url-loader": "~6.0.0",
|
||||
"url-loader": "~4.1.1",
|
||||
"watch": "~1.0.2",
|
||||
"webpack": "^5.7.0",
|
||||
"webpack-bundle-analyzer": "^4.1.0",
|
||||
"webpack-cli": "^4.2.0",
|
||||
"webpack-merge": "^5.1.2",
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
},
|
||||
"jupyterlab": {
|
||||
"name": "Jupyter Notebook",
|
||||
"extensions": [
|
||||
"@jupyter-notebook/application-extension",
|
||||
"@jupyter-notebook/console-extension",
|
||||
"@jupyter-notebook/docmanager-extension",
|
||||
"@jupyter-notebook/documentsearch-extension",
|
||||
"@jupyter-notebook/help-extension",
|
||||
"@jupyter-notebook/notebook-extension",
|
||||
"@jupyter-notebook/terminal-extension",
|
||||
"@jupyter-notebook/tree-extension",
|
||||
"@jupyterlab/application-extension",
|
||||
"@jupyterlab/apputils-extension",
|
||||
"@jupyterlab/codemirror-extension",
|
||||
"@jupyterlab/completer-extension",
|
||||
"@jupyterlab/console-extension",
|
||||
"@jupyterlab/docmanager-extension",
|
||||
"@jupyterlab/documentsearch-extension",
|
||||
"@jupyterlab/filebrowser-extension",
|
||||
"@jupyterlab/fileeditor-extension",
|
||||
"@jupyterlab/hub-extension",
|
||||
"@jupyterlab/mainmenu-extension",
|
||||
"@jupyterlab/mathjax2-extension",
|
||||
"@jupyterlab/notebook-extension",
|
||||
"@jupyterlab/rendermime-extension",
|
||||
"@jupyterlab/running-extension",
|
||||
"@jupyterlab/shortcuts-extension",
|
||||
"@jupyterlab/terminal-extension",
|
||||
"@jupyterlab/theme-dark-extension",
|
||||
"@jupyterlab/theme-light-extension",
|
||||
"@jupyterlab/tooltip-extension",
|
||||
"@jupyterlab/translation-extension",
|
||||
"@jupyterlab/user-extension"
|
||||
],
|
||||
"singletonPackages": [
|
||||
"@jupyterlab/application",
|
||||
"@jupyterlab/apputils",
|
||||
"@jupyterlab/celltags",
|
||||
"@jupyterlab/codeeditor",
|
||||
"@jupyterlab/completer",
|
||||
"@jupyterlab/console",
|
||||
"@jupyterlab/coreutils",
|
||||
"@jupyterlab/docmanager",
|
||||
"@jupyterlab/docprovider",
|
||||
"@jupyterlab/documentsearch",
|
||||
"@jupyterlab/filebrowser",
|
||||
"@jupyterlab/fileeditor",
|
||||
"@jupyterlab/mainmenu",
|
||||
"@jupyterlab/notebook",
|
||||
"@jupyterlab/observables",
|
||||
"@jupyterlab/outputarea",
|
||||
"@jupyterlab/rendermime",
|
||||
"@jupyterlab/rendermime-interfaces",
|
||||
"@jupyterlab/services",
|
||||
"@jupyterlab/settingregistry",
|
||||
"@jupyterlab/shared-models",
|
||||
"@jupyterlab/statedb",
|
||||
"@jupyterlab/statusbar",
|
||||
"@jupyterlab/terminal",
|
||||
"@jupyterlab/tooltip",
|
||||
"@jupyterlab/translation",
|
||||
"@jupyterlab/user",
|
||||
"@jupyterlab/ui-components",
|
||||
"@lumino/algorithm",
|
||||
"@lumino/application",
|
||||
"@lumino/commands",
|
||||
"@lumino/coreutils",
|
||||
"@lumino/disposable",
|
||||
"@lumino/domutils",
|
||||
"@lumino/dragdrop",
|
||||
"@lumino/messaging",
|
||||
"@lumino/properties",
|
||||
"@lumino/signaling",
|
||||
"@lumino/virtualdom",
|
||||
"@lumino/widgets",
|
||||
"react",
|
||||
"react-dom",
|
||||
"yjs"
|
||||
],
|
||||
"mimeExtensions": {},
|
||||
"linkedPackages": {}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// We dynamically set the webpack public path based on the page config
|
||||
// settings from the JupyterLab app. We copy some of the pageconfig parsing
|
||||
// logic in @jupyterlab/coreutils below, since this must run before any other
|
||||
// files are loaded (including @jupyterlab/coreutils).
|
||||
|
||||
/**
|
||||
* Get global configuration data for the Jupyter application.
|
||||
*
|
||||
* @param name - The name of the configuration option.
|
||||
*
|
||||
* @returns The config value or an empty string if not found.
|
||||
*
|
||||
* #### Notes
|
||||
* All values are treated as strings.
|
||||
* For browser based applications, it is assumed that the page HTML
|
||||
* includes a script tag with the id `jupyter-config-data` containing the
|
||||
* configuration as valid JSON. In order to support the classic Notebook,
|
||||
* we fall back on checking for `body` data of the given `name`.
|
||||
*/
|
||||
function getOption(name) {
|
||||
let configData = Object.create(null);
|
||||
// Use script tag if available.
|
||||
if (typeof document !== 'undefined' && document) {
|
||||
const el = document.getElementById('jupyter-config-data');
|
||||
|
||||
if (el) {
|
||||
configData = JSON.parse(el.textContent || '{}');
|
||||
}
|
||||
}
|
||||
return configData[name] || '';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
__webpack_public_path__ = getOption('fullStaticUrl') + '/';
|
||||
@ -0,0 +1 @@
|
||||
import '@jupyterlab/celltags/style/index.js';
|
||||
@ -0,0 +1,180 @@
|
||||
// Copyright (c) Jupyter Development Team.
|
||||
// Distributed under the terms of the Modified BSD License.
|
||||
|
||||
// Heavily inspired (and slightly tweaked) from:
|
||||
// https://github.com/jupyterlab/jupyterlab/blob/master/examples/federated/core_package/webpack.config.js
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const merge = require('webpack-merge').default;
|
||||
const { ModuleFederationPlugin } = webpack.container;
|
||||
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
|
||||
.BundleAnalyzerPlugin;
|
||||
|
||||
const Build = require('@jupyterlab/builder').Build;
|
||||
const baseConfig = require('@jupyterlab/builder/lib/webpack.config.base');
|
||||
|
||||
const data = require('./package.json');
|
||||
|
||||
const names = Object.keys(data.dependencies).filter(name => {
|
||||
const packageData = require(path.join(name, 'package.json'));
|
||||
return packageData.jupyterlab !== undefined;
|
||||
});
|
||||
|
||||
// Ensure a clear build directory.
|
||||
const buildDir = path.resolve(__dirname, 'build');
|
||||
if (fs.existsSync(buildDir)) {
|
||||
fs.removeSync(buildDir);
|
||||
}
|
||||
fs.ensureDirSync(buildDir);
|
||||
|
||||
// Copy extra files
|
||||
const index = path.resolve(__dirname, 'index.js');
|
||||
const cssImports = path.resolve(__dirname, 'style.js');
|
||||
fs.copySync(index, path.resolve(buildDir, 'index.js'));
|
||||
fs.copySync(cssImports, path.resolve(buildDir, 'extraStyle.js'));
|
||||
|
||||
const extras = Build.ensureAssets({
|
||||
packageNames: names,
|
||||
output: buildDir,
|
||||
schemaOutput: path.resolve(__dirname, '..', 'notebook')
|
||||
});
|
||||
|
||||
/**
|
||||
* Create the webpack ``shared`` configuration
|
||||
*/
|
||||
function createShared(packageData) {
|
||||
// Set up module federation sharing config
|
||||
const shared = {};
|
||||
const extensionPackages = packageData.jupyterlab.extensions;
|
||||
|
||||
// Make sure any resolutions are shared
|
||||
for (let [pkg, requiredVersion] of Object.entries(packageData.resolutions)) {
|
||||
shared[pkg] = { requiredVersion };
|
||||
}
|
||||
|
||||
// Add any extension packages that are not in resolutions (i.e., installed from npm)
|
||||
for (let pkg of extensionPackages) {
|
||||
if (!shared[pkg]) {
|
||||
shared[pkg] = {
|
||||
requiredVersion: require(`${pkg}/package.json`).version
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Add dependencies and sharedPackage config from extension packages if they
|
||||
// are not already in the shared config. This means that if there is a
|
||||
// conflict, the resolutions package version is the one that is shared.
|
||||
const extraShared = [];
|
||||
for (let pkg of extensionPackages) {
|
||||
let pkgShared = {};
|
||||
let {
|
||||
dependencies = {},
|
||||
jupyterlab: { sharedPackages = {} } = {}
|
||||
} = require(`${pkg}/package.json`);
|
||||
for (let [dep, requiredVersion] of Object.entries(dependencies)) {
|
||||
if (!shared[dep]) {
|
||||
pkgShared[dep] = { requiredVersion };
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite automatic dependency sharing with custom sharing config
|
||||
for (let [dep, config] of Object.entries(sharedPackages)) {
|
||||
if (config === false) {
|
||||
delete pkgShared[dep];
|
||||
} else {
|
||||
if ('bundled' in config) {
|
||||
config.import = config.bundled;
|
||||
delete config.bundled;
|
||||
}
|
||||
pkgShared[dep] = config;
|
||||
}
|
||||
}
|
||||
extraShared.push(pkgShared);
|
||||
}
|
||||
|
||||
// Now merge the extra shared config
|
||||
const mergedShare = {};
|
||||
for (let sharedConfig of extraShared) {
|
||||
for (let [pkg, config] of Object.entries(sharedConfig)) {
|
||||
// Do not override the basic share config from resolutions
|
||||
if (shared[pkg]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add if we haven't seen the config before
|
||||
if (!mergedShare[pkg]) {
|
||||
mergedShare[pkg] = config;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Choose between the existing config and this new config. We do not try
|
||||
// to merge configs, which may yield a config no one wants
|
||||
let oldConfig = mergedShare[pkg];
|
||||
|
||||
// if the old one has import: false, use the new one
|
||||
if (oldConfig.import === false) {
|
||||
mergedShare[pkg] = config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(shared, mergedShare);
|
||||
|
||||
// Transform any file:// requiredVersion to the version number from the
|
||||
// imported package. This assumes (for simplicity) that the version we get
|
||||
// importing was installed from the file.
|
||||
for (let [pkg, { requiredVersion }] of Object.entries(shared)) {
|
||||
if (requiredVersion && requiredVersion.startsWith('file:')) {
|
||||
shared[pkg].requiredVersion = require(`${pkg}/package.json`).version;
|
||||
}
|
||||
}
|
||||
|
||||
// Add singleton package information
|
||||
for (let pkg of packageData.jupyterlab.singletonPackages) {
|
||||
if (shared[pkg]) {
|
||||
shared[pkg].singleton = true;
|
||||
}
|
||||
}
|
||||
|
||||
return shared;
|
||||
}
|
||||
|
||||
// Make a bootstrap entrypoint
|
||||
const entryPoint = path.join(buildDir, 'bootstrap.js');
|
||||
const bootstrap = 'import("./index.js");';
|
||||
fs.writeFileSync(entryPoint, bootstrap);
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
baseConfig.mode = 'production';
|
||||
}
|
||||
|
||||
if (process.argv.includes('--analyze')) {
|
||||
extras.push(new BundleAnalyzerPlugin());
|
||||
}
|
||||
|
||||
module.exports = [
|
||||
merge(baseConfig, {
|
||||
mode: 'development',
|
||||
entry: ['./publicpath.js', './' + path.relative(__dirname, entryPoint)],
|
||||
output: {
|
||||
path: path.resolve(__dirname, '..', 'notebook/static/'),
|
||||
library: {
|
||||
type: 'var',
|
||||
name: ['_JUPYTERLAB', 'CORE_OUTPUT']
|
||||
},
|
||||
filename: 'bundle.js'
|
||||
},
|
||||
plugins: [
|
||||
new ModuleFederationPlugin({
|
||||
library: {
|
||||
type: 'var',
|
||||
name: ['_JUPYTERLAB', 'CORE_LIBRARY_FEDERATION']
|
||||
},
|
||||
name: 'CORE_FEDERATION',
|
||||
shared: createShared(data)
|
||||
})
|
||||
]
|
||||
})
|
||||
].concat(extras);
|
||||
@ -0,0 +1,17 @@
|
||||
const base = require('./webpack.config');
|
||||
const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin');
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
...base[0],
|
||||
bail: false,
|
||||
watch: true,
|
||||
plugins: [
|
||||
...base[0].plugins,
|
||||
new ExtraWatchWebpackPlugin({
|
||||
files: ['../packages/_metapackage/tsconfig.tsbuildinfo']
|
||||
})
|
||||
]
|
||||
},
|
||||
...base.slice(1)
|
||||
];
|
||||
@ -0,0 +1,13 @@
|
||||
name: notebook
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- ipywidgets=7.6
|
||||
- jupyterlab=3
|
||||
- jupyterlab-language-pack-fr-FR
|
||||
- jupyterlab-link-share>=0.2
|
||||
- matplotlib
|
||||
- numpy
|
||||
- nodejs
|
||||
- python >=3.9,<3.10
|
||||
- xeus-python
|
||||
@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
python -m pip install -e .
|
||||
jlpm && jlpm run build
|
||||
jlpm run develop
|
||||
@ -1,29 +0,0 @@
|
||||
{
|
||||
"name": "jupyter-notebook-deps",
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"backbone": "components/backbone#~1.2",
|
||||
"bootstrap": "bootstrap#~3.4",
|
||||
"bootstrap-tour": "0.9.0",
|
||||
"codemirror": "components/codemirror#5.56.0+components1",
|
||||
"create-react-class": "https://cdn.jsdelivr.net/npm/create-react-class@15.6.3/create-react-class.min.js",
|
||||
"es6-promise": "~1.0",
|
||||
"font-awesome": "components/font-awesome#~4.7.0",
|
||||
"jed": "~1.1.1",
|
||||
"jquery": "components/jquery#~3.5.0",
|
||||
"jquery-typeahead": "~2.10.6",
|
||||
"jquery-ui": "components/jqueryui#~1.12",
|
||||
"marked": "~0.7",
|
||||
"MathJax": "^2.7.4",
|
||||
"moment": "~2.19.3",
|
||||
"react": "~16.0.0",
|
||||
"requirejs": "~2.2",
|
||||
"requirejs-text": "~2.0.15",
|
||||
"requirejs-plugins": "~1.0.3",
|
||||
"text-encoding": "~0.1",
|
||||
"underscore": "components/underscore#~1.8.3",
|
||||
"xterm.js": "https://unpkg.com/xterm@~3.1.0/dist/xterm.js",
|
||||
"xterm.js-css": "https://unpkg.com/xterm@~3.1.0/dist/xterm.css",
|
||||
"xterm.js-fit": "https://unpkg.com/xterm@~3.1.0/dist/addons/fit/fit.js"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@jupyter-notebook/buildutils",
|
||||
"version": "7.0.0-alpha.0",
|
||||
"private": true,
|
||||
"description": "Jupyter Notebook - Build Utilities",
|
||||
"homepage": "https://github.com/jupyter/notebook",
|
||||
"bugs": {
|
||||
"url": "https://github.com/jupyter/notebook/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/jupyter/notebook.git"
|
||||
},
|
||||
"license": "BSD-3-Clause",
|
||||
"author": "Project Jupyter",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"directories": {
|
||||
"lib": "lib/"
|
||||
},
|
||||
"files": [
|
||||
"lib/*.d.ts",
|
||||
"lib/*.js.map",
|
||||
"lib/*.js"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"clean": "rimraf lib && rimraf tsconfig.tsbuildinfo",
|
||||
"prepublishOnly": "npm run build",
|
||||
"watch": "tsc -w --listEmittedFiles"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jupyterlab/buildutils": "^4.0.0-alpha.5",
|
||||
"commander": "^6.2.0",
|
||||
"fs-extra": "^9.1.0",
|
||||
"typescript": "~4.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fs-extra": "^9.0.10",
|
||||
"@types/node": "^14.6.1",
|
||||
"rimraf": "~3.0.0"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
| Copyright (c) Jupyter Development Team.
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
import commander from 'commander';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import process from 'process';
|
||||
|
||||
import { run } from '@jupyterlab/buildutils';
|
||||
|
||||
commander
|
||||
.description('Setup the repository for develop mode')
|
||||
.option('--overwrite', 'Force linking the notebook schemas')
|
||||
.option('--source', 'The path to the notebook package')
|
||||
.action((options: any) => {
|
||||
const { overwrite } = options;
|
||||
const prefix = run(
|
||||
'python -c "import sys; print(sys.prefix)"',
|
||||
{
|
||||
stdio: 'pipe'
|
||||
},
|
||||
true
|
||||
);
|
||||
const source = path.resolve(options.source ?? process.cwd());
|
||||
const sourceDir = path.join(
|
||||
source,
|
||||
'notebook',
|
||||
'schemas',
|
||||
'@jupyter-notebook'
|
||||
);
|
||||
const destDir = path.join(
|
||||
prefix,
|
||||
'share',
|
||||
'jupyter',
|
||||
'lab',
|
||||
'schemas',
|
||||
'@jupyter-notebook'
|
||||
);
|
||||
if (overwrite) {
|
||||
try {
|
||||
fs.unlinkSync(destDir);
|
||||
console.log('Removed previous symlink:', destDir);
|
||||
} catch (e) {
|
||||
console.info('Skip unlinkink', destDir);
|
||||
}
|
||||
}
|
||||
console.log('Symlinking:', sourceDir, destDir);
|
||||
fs.symlinkSync(sourceDir, destDir, 'dir');
|
||||
});
|
||||
|
||||
commander.parse(process.argv);
|
||||
@ -0,0 +1,38 @@
|
||||
import * as path from 'path';
|
||||
|
||||
import * as fs from 'fs-extra';
|
||||
|
||||
import { writePackageData } from '@jupyterlab/buildutils';
|
||||
|
||||
/**
|
||||
* Ensure the application package resolutions.
|
||||
*/
|
||||
function ensureResolutions(): string[] {
|
||||
const basePath = path.resolve('.');
|
||||
const corePath = path.join(basePath, 'app', 'package.json');
|
||||
const corePackage = fs.readJSONSync(corePath);
|
||||
|
||||
corePackage.jupyterlab.mimeExtensions = {};
|
||||
corePackage.jupyterlab.linkedPackages = {};
|
||||
corePackage.resolutions = {};
|
||||
|
||||
const packages = Object.keys(corePackage.dependencies).concat(
|
||||
corePackage.jupyterlab.singletonPackages
|
||||
);
|
||||
|
||||
packages.forEach(name => {
|
||||
const data = require(`${name}/package.json`);
|
||||
// Insist on a restricted version in the yarn resolution.
|
||||
corePackage.resolutions[name] = `~${data.version}`;
|
||||
});
|
||||
|
||||
// Write the package.json back to disk.
|
||||
if (writePackageData(corePath, corePackage)) {
|
||||
return ['Updated dev mode'];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
void ensureResolutions();
|
||||
}
|
||||
@ -0,0 +1,122 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
| Copyright (c) Jupyter Development Team.
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/buildutils/src/bumpversion.ts
|
||||
*/
|
||||
|
||||
import * as utils from '@jupyterlab/buildutils';
|
||||
|
||||
import commander from 'commander';
|
||||
|
||||
import { getPythonVersion, postbump } from './utils';
|
||||
|
||||
// Specify the program signature.
|
||||
commander
|
||||
.description('Update the version')
|
||||
.option('--dry-run', 'Dry run')
|
||||
.option('--force', 'Force the upgrade')
|
||||
.option('--skip-commit', 'Whether to skip commit changes')
|
||||
.arguments('<spec>')
|
||||
.action((spec: any, opts: any) => {
|
||||
// Get the previous version.
|
||||
const prev = getPythonVersion();
|
||||
const isFinal = /\d+\.\d+\.\d+$/.test(prev);
|
||||
|
||||
// Whether to commit after bumping
|
||||
const commit = opts.skipCommit !== true;
|
||||
|
||||
// for "next", determine whether to use "patch" or "build"
|
||||
if (spec === 'next') {
|
||||
spec = isFinal ? 'patch' : 'build';
|
||||
}
|
||||
|
||||
// For patch, defer to `patch:release` command
|
||||
if (spec === 'patch') {
|
||||
let cmd = 'jlpm run release:patch';
|
||||
if (opts.force) {
|
||||
cmd += ' --force';
|
||||
}
|
||||
if (!commit) {
|
||||
cmd += ' --skip-commit';
|
||||
}
|
||||
utils.run(cmd);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Make sure we have a valid version spec.
|
||||
const options = ['major', 'minor', 'release', 'build'];
|
||||
if (options.indexOf(spec) === -1) {
|
||||
throw new Error(`Version spec must be one of: ${options}`);
|
||||
}
|
||||
if (isFinal && spec === 'release') {
|
||||
throw new Error('Use "major" or "minor" to switch back to alpha release');
|
||||
}
|
||||
if (isFinal && spec === 'build') {
|
||||
throw new Error('Cannot increment a build on a final release');
|
||||
}
|
||||
|
||||
// Run pre-bump script.
|
||||
utils.prebump();
|
||||
|
||||
// Handle dry runs.
|
||||
if (opts.dryRun) {
|
||||
utils.run(`bumpversion --dry-run --verbose ${spec}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is a major release during the alpha cycle, bump
|
||||
// just the Python version.
|
||||
if (prev.indexOf('a') !== -1 && spec === 'major') {
|
||||
// Bump the version.
|
||||
utils.run(`bumpversion ${spec}`);
|
||||
|
||||
// Run the post-bump script.
|
||||
postbump(commit);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine the version spec to use for lerna.
|
||||
let lernaVersion = 'preminor';
|
||||
if (spec === 'build') {
|
||||
lernaVersion = 'prerelease';
|
||||
// a -> b
|
||||
} else if (spec === 'release' && prev.indexOf('a') !== -1) {
|
||||
lernaVersion = 'prerelease --preid=beta';
|
||||
// b -> rc
|
||||
} else if (spec === 'release' && prev.indexOf('b') !== -1) {
|
||||
lernaVersion = 'prerelease --preid=rc';
|
||||
// rc -> final
|
||||
} else if (spec === 'release' && prev.indexOf('rc') !== -1) {
|
||||
lernaVersion = 'patch';
|
||||
}
|
||||
if (lernaVersion === 'preminor') {
|
||||
lernaVersion += ' --preid=alpha';
|
||||
}
|
||||
|
||||
let cmd = `jlpm run lerna version --force-publish --no-push --no-git-tag-version ${lernaVersion}`;
|
||||
if (opts.force) {
|
||||
cmd += ' --yes';
|
||||
}
|
||||
// For a preminor release, we bump 10 minor versions so that we do
|
||||
// not conflict with versions during minor releases of the top
|
||||
// level package.
|
||||
if (lernaVersion === 'preminor') {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
utils.run(cmd);
|
||||
}
|
||||
} else {
|
||||
utils.run(cmd);
|
||||
}
|
||||
|
||||
// Bump the version.
|
||||
utils.run(`bumpversion ${spec} --allow-dirty`);
|
||||
|
||||
// Run the post-bump script.
|
||||
postbump(commit);
|
||||
});
|
||||
|
||||
commander.parse(process.argv);
|
||||
@ -0,0 +1,54 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
| Copyright (c) Jupyter Development Team.
|
||||
| Distributed under the terms of the Modified BSD License.
|
||||
|----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* Inspired by: https://github.com/jupyterlab/jupyterlab/blob/master/buildutils/src/patch-release.ts
|
||||
*/
|
||||
|
||||
import * as utils from '@jupyterlab/buildutils';
|
||||
|
||||
import commander from 'commander';
|
||||
|
||||
import { getPythonVersion, postbump } from './utils';
|
||||
|
||||
// Specify the program signature.
|
||||
commander
|
||||
.description('Create a patch release')
|
||||
.option('--force', 'Force the upgrade')
|
||||
.option('--skip-commit', 'Whether to skip commit changes')
|
||||
.action((options: any) => {
|
||||
// Make sure we can patch release.
|
||||
const pyVersion = getPythonVersion();
|
||||
if (
|
||||
pyVersion.includes('a') ||
|
||||
pyVersion.includes('b') ||
|
||||
pyVersion.includes('rc')
|
||||
) {
|
||||
throw new Error('Can only make a patch release from a final version');
|
||||
}
|
||||
|
||||
// Run pre-bump actions.
|
||||
utils.prebump();
|
||||
|
||||
// Patch the python version
|
||||
utils.run('bumpversion patch'); // switches to alpha
|
||||
utils.run('bumpversion release --allow-dirty'); // switches to beta
|
||||
utils.run('bumpversion release --allow-dirty'); // switches to rc.
|
||||
utils.run('bumpversion release --allow-dirty'); // switches to final.
|
||||
|
||||
// Version the changed
|
||||
let cmd =
|
||||
'jlpm run lerna version patch --no-push --force-publish --no-git-tag-version';
|
||||
if (options.force) {
|
||||
cmd += ' --yes';
|
||||
}
|
||||
utils.run(cmd);
|
||||
|
||||
// Whether to commit after bumping
|
||||
const commit = options.skipCommit !== true;
|
||||
postbump(commit);
|
||||
});
|
||||
|
||||
commander.parse(process.argv);
|
||||
@ -0,0 +1,23 @@
|
||||
import { run } from '@jupyterlab/buildutils';
|
||||
|
||||
/**
|
||||
* Get the current version of notebook
|
||||
*/
|
||||
export function getPythonVersion(): string {
|
||||
const cmd = 'python setup.py --version';
|
||||
const lines = run(cmd, { stdio: 'pipe' }, true).split('\n');
|
||||
return lines[lines.length - 1];
|
||||
}
|
||||
|
||||
export function postbump(commit = true): void {
|
||||
// run the integrity
|
||||
run('jlpm integrity');
|
||||
|
||||
const newPyVersion = getPythonVersion();
|
||||
|
||||
// Commit changes.
|
||||
if (commit) {
|
||||
run(`git commit -am "Release ${newPyVersion}"`);
|
||||
run(`git tag ${newPyVersion}`);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../tsconfigbase",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib",
|
||||
"rootDir": "src",
|
||||
"module": "commonjs"
|
||||
},
|
||||
"include": ["src/*"],
|
||||
"references": []
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
# Jupyter Notebook
|
||||
|
||||
[](https://groups.google.com/forum/#!forum/jupyter)
|
||||
[](https://travis-ci.org/jupyter/notebook)
|
||||
[](https://jupyter-notebook.readthedocs.io/en/latest/?badge=latest)
|
||||
|
||||
|
||||
|
||||
Jupyter नोटबुक इंटरैक्टिव के लिए एक वेब-आधारित नोटबुक वातावरण है
|
||||
कंप्यूटिंग।
|
||||
|
||||

|
||||
|
||||
### नोटिस
|
||||
कृपया ध्यान दें कि इस भंडार का रखरखाव वर्तमान में जुपिटर समुदाय के एक कंकाल के दल द्वारा किया जाता है। हम उपयोगकर्ताओं को जुपिटरलैब में संक्रमण के लिए प्रोत्साहित करते हैं, जहां अधिक तत्काल समर्थन हो सकता है। हमारा दृष्टिकोण आगे बढ़ेगा:
|
||||
|
||||
1. जुपिटर नोटबुक की सुरक्षा बनाए रखने के लिए। इसका मतलब है कि सुरक्षा से संबंधित मुद्दे और पुल अनुरोध हमारी सर्वोच्च प्राथमिकता है।
|
||||
2. JupyterLab को संबोधित करने के लिए [समता मुद्दों की सुविधा](https://github.com/jupyterlab/jupyterlab/issues?q=is%3Aopen+is%3Aissue+label%3A%22tag%3AFeature+Parity%22)| इस प्रयास के हिस्से के रूप में, हम एक बेहतर [नोटबुक-ओनली एक्सपीरियंस](https://github.com/jupyterlab/jupyterlab/issues/8450)JupyterLab में उन उपयोगकर्ताओं के लिए जो क्लासिक Jupyter नोटबुक के UI को पसंद करते हैं।
|
||||
3. समुदाय के सदस्यों की कड़ी मेहनत के प्रति उत्तरदायी होना जिन्होंने पुल अनुरोधों को खोला है। हम इन पीआर को ट्राई कर रहे हैं। हम इस समय नई सुविधाओं का समर्थन या रखरखाव नहीं कर सकते हैं, लेकिन हम सुरक्षा और अन्य स्थिरता सुधारों का स्वागत करते हैं।
|
||||
|
||||
यदि आपके पास एक नई सुविधा के साथ एक खुला पुल अनुरोध है या यदि आप एक खोलने की योजना बना रहे हैं, तो कृपया इसे [नोटबुक एक्सटेंशन](https://jupyter-notebook.readthedocs.io/en/stable/extending/) के रूप में शिपिंग करने पर विचार करें। बजाय।
|
||||
|
||||
##### `नोटबुक` में योगदान करने के लिए विकल्प
|
||||
इसके अतिरिक्त, कृपया विचार करें कि क्या आपका योगदान Jupyter फ्रंट-एंड के लिए अंतर्निहित सर्वर के लिए उपयुक्त होगा, [jupyter server](https://github.com/jupyter/jupyter_server) या में [JupyterLab फ़्रंट एंड](https://github.com/jupyterlab/jupyterlab).
|
||||
|
||||
### जुपिटर नोटबुक, आइपीथॉन नोटबुक की भाषा-अज्ञेय विकास
|
||||
Jupyter नोटबुक एक भाषा-अज्ञेय HTML नोटबुक अनुप्रयोग है
|
||||
प्रोजेक्ट जुपिटर। 2015 में, जुपिटर नोटबुक के एक भाग के रूप में जारी किया गया था
|
||||
IPython कोडबेस का बिग स्प्लिट ™। IPython 3 अंतिम प्रमुख अखंड था
|
||||
दोनों भाषा-अज्ञेयवादी कोड, जैसे *IPython नोटबुक*,
|
||||
और भाषा विशिष्ट कोड, जैसे कि *अजगर के लिए आईपीथॉन कर्नेल*। जैसा
|
||||
कई भाषाओं में कंप्यूटिंग स्पैन, प्रोजेक्ट जुपिटर विकसित करना जारी रखेगा
|
||||
भाषा-अज्ञेय **जुपिटर नोटबुक** इस रेपो में और की मदद से
|
||||
समुदाय भाषा विशिष्ट गुठली विकसित करते हैं जो अपने आप में पाए जाते हैं
|
||||
असतत रेपो।
|
||||
[[Big Split™ घोषणा](https://blog.jupyter.org/the-big-split-9d7b88a031a7)]
|
||||
[[Jupyter आरोही ब्लॉग पोस्ट](https://blog.jupyter.org/jupyter-ascending-1bf5b362d97e)]
|
||||
|
||||
## स्थापना
|
||||
आप के लिए स्थापना प्रलेखन पा सकते हैं
|
||||
[बृहस्पति मंच, ReadTheDocs पर](https://jupyter.readthedocs.io/en/latest/install.html).
|
||||
जुपिटर नोटबुक के उन्नत उपयोग के लिए दस्तावेज पाया जा सकता है
|
||||
[यहाँ](https://jupyter-notebook.readthedocs.io/en/latest/).
|
||||
|
||||
स्थानीय स्थापना के लिए, सुनिश्चित करें कि आपके पास है
|
||||
[pip स्थापित](https://pip.readthedocs.io/en/stable/installing/) और भाग खड़ा हुआ:
|
||||
|
||||
$ pip install notebook
|
||||
|
||||
## उपयोग - जुपिटर नोटबुक चल रहा है
|
||||
|
||||
### स्थानीय स्थापना में चल रहा है
|
||||
|
||||
इसके साथ लॉन्च करें:
|
||||
|
||||
$ jupyter notebook
|
||||
|
||||
### एक दूरस्थ स्थापना में चल रहा है
|
||||
|
||||
आपको बृहस्पति नोटबुक को दूरस्थ रूप से शुरू करने से पहले कुछ कॉन्फ़िगरेशन की आवश्यकता है। देखें [नोटबुक सर्वर चला रहा है](https://jupyter-notebook.readthedocs.io/en/stable/public_server.html).
|
||||
|
||||
## विकास स्थापना
|
||||
|
||||
स्थानीय विकास की स्थापना कैसे करें, इसके लिए [`CONTRIBUTING.rst`](CONTRIBUTING.rst) देखें।
|
||||
|
||||
## योगदान
|
||||
|
||||
यदि आप इस परियोजना में योगदान देने में रुचि रखते हैं, तो [`CONTRIBUTING.rst`](CONTRIBUTING.rst) देखें।
|
||||
|
||||
## साधन
|
||||
- [Project Jupyter website](https://jupyter.org)
|
||||
- [Online Demo at jupyter.org/try](https://jupyter.org/try)
|
||||
- [Documentation for Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/latest/) [[PDF](https://media.readthedocs.org/pdf/jupyter-notebook/latest/jupyter-notebook.pdf)]
|
||||
- [Korean Version of Installation](https://github.com/ChungJooHo/Jupyter_Kor_doc/)
|
||||
- [Documentation for Project Jupyter](https://jupyter.readthedocs.io/en/latest/index.html) [[PDF](https://media.readthedocs.org/pdf/jupyter/latest/jupyter.pdf)]
|
||||
- [Issues](https://github.com/jupyter/notebook/issues)
|
||||
- [Technical support - Jupyter Google Group](https://groups.google.com/forum/#!forum/jupyter)
|
||||
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 331 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 331 KiB |
@ -1,57 +0,0 @@
|
||||
# Notebook 실행하기
|
||||
|
||||
## 첫 걸음
|
||||
1. 다음 명령어를 통해 Notebook 서버를 시작하세요 :
|
||||
|
||||
$ jupyter notebook
|
||||
|
||||
2. 브라우저에 Notebook이 실행된 것을 확인할 수 있습니다.
|
||||
|
||||
|
||||
# Notebook 서버 시작하기
|
||||
|
||||
Notebook을 컴퓨터에 설치하였으면 Notebook 서버를 시작할 수 있습니다. 다음 명령어를 이용하여 Notebook서버를 시작할 수 있습니다.
|
||||
|
||||
$ jupyter notebook
|
||||
|
||||
이 명령어를 실행하면, 터미널에 웹 응용프로그램의 주소와 서버에 대한 정보가 출력됩니다.
|
||||
|
||||
$ jupyter notebook
|
||||
$ [I 08:58:24.417 NotebookApp] Serving notebooks from local directory: /Users/catherline
|
||||
$ [I 08:58:24.417 NotebookApp] 0 active kernels
|
||||
$ [I 08:58:24.417 NotebookApp] The Jupyter Notebook is running at: http://localhost:8888/
|
||||
$ [I 08:58:24.417 NotebookApp] Use Control-C to stop this server and shut down all kernels
|
||||
|
||||
기본 브라우저를 통해 이 주소가 열립니다.
|
||||
|
||||
Notebook이 브라우저에 열리면, Notebook의 목록을 보여주는 Notebook Dashboard를 볼 수 있습니다. 대체로 가장 상위의 디렉토리를 열어줄 것입니다.
|
||||
|
||||
**Notebook Dashboard**
|
||||
|
||||

|
||||
|
||||
# Notebook 서버의 명령어 소개
|
||||
|
||||
## 커스텀 IP 나 포트를 이용하여 시작하려면 어떻게 해야할까?
|
||||
|
||||
기본값으로, Notebook 서버는 포트 8888로 시작됩니다. 만약 포트8888이 사용할 수 없다면, Notebook 서버는 다른 가능한 포트를 찾습니다. 또한 임의로 포트를 설정해주는 것도 가능합니다. 예를 들어 포트 9999로 실행하면 :
|
||||
|
||||
$ jupyter notebook --port 9999
|
||||
|
||||
|
||||
## 브라우저를 열지않고 Notebook를 열기
|
||||
|
||||
브라우저를 열지 않고 Notebook 서버를 시작하려면 :
|
||||
|
||||
$ jupyter notebook --no-browser
|
||||
|
||||
|
||||
## Notebook 서버 옵션 도움말 보기
|
||||
|
||||
Notebook 서버는 --help 옵션을 통해 도움말 메시지를 제공합니다 :
|
||||
|
||||
$ jupyter notebook --help
|
||||
|
||||
|
||||
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
# Jupyter Notebook 설치하기
|
||||
|
||||
## 필요한 것 : Python
|
||||
|
||||
Jupyter Notebook 을 설치하기 위해선 Jupyter가 많은 프로그래밍 언어들로 동작되기 때문에, Python이 필요합니다. (Python 3.3이상, Python 2.7)
|
||||
|
||||
Python과 Jupyter를 설치할 때 Anaconda를 이용하는 것을 추천합니다. 밑에서 이를 이용하여 설치할 것입니다.
|
||||
|
||||
## Anaconda 와 conda 를 이용하여 Jupyter 설치하기
|
||||
|
||||
새로운 이용자들은 Anaconda를 설치하는 것을 강력하게 추천합니다. Anaconda는 Python과 Jupyter를 쉽게 설치하게 해주고, 과학적인 계산과 데이터를 위한 자주 사용되는 패키지들의 설치에도 유용합니다.
|
||||
|
||||
설치 순서 :
|
||||
|
||||
1. Anaconda를 다운받으세요. Anaconda의 가장 최신의 Python 3버전을 다운 받는 것을 추천합니다.
|
||||
2. 다운 받은 Anaconda 의 다운로드 페이지에 있는 설명을 읽고 설치해주세요.
|
||||
3. 축하합니다. Jupyter Notebook 을 설치하셨습니다. Jupyter Notebook을 실행하려면 :
|
||||
|
||||
$ jupyter notebook
|
||||
|
||||
## 숙련된 Python 이용자 : pip을 통해 설치하기
|
||||
|
||||
Python 이용자라면, Anaconda 대신에 Python의 패키지 매니저 pip을 이용하여 설치하세요.
|
||||
|
||||
첫째로, 가장 최신의 pip인지를 확인하세요; 구 버전은 독립성에 문제가 있을 수 있습니다.
|
||||
|
||||
$ pip install --upgrade pip
|
||||
|
||||
이제 다음을 이용하여 Jupyter Notebook를 설치하세요 :
|
||||
|
||||
$ pip install jupyter
|
||||
|
||||
(축하합니다. Jupyter Notebook를 설치하셨습니다.)
|
||||
@ -1,55 +0,0 @@
|
||||
# Jupyter Notebook
|
||||
|
||||
[](https://groups.google.com/forum/#!forum/jupyter)
|
||||
[](https://travis-ci.org/jupyter/notebook)
|
||||
[](http://jupyter-notebook.readthedocs.io/en/latest/?badge=latest)
|
||||
|
||||
English 버전 링크 : [[English Version](http://github.com/jupyter/notebook/)]
|
||||
|
||||
Jupyter notebook 은 상호 교환을 위한 웹 기반 환경입니다.
|
||||
|
||||

|
||||
|
||||
### Jupyter notebook, 사용자의 언어에 독립적인 IPython notebook의 진화
|
||||
Jupyter notebook은 Jupyter 프로젝트를 위한 사용자 언어에 독립적인 HTML 응용 프로그램입니다.
|
||||
2015년에 Jupyter notebook은 IPython 코드 기반의 The Big Split™ 의 일부분으로 시작되었습니다.
|
||||
IPython 3는 *IPython notebook* 과 같은 사용자 언어에 독립적인 코드와 *IPython kernel for Python* 과 같은 특정 언어 기반의 코드의 기능을 가지고 출시되었습니다.
|
||||
컴퓨터에는 많은 언어가 사용되기 때문에, Jupyter 프로젝트는 사용자 언어에 독립적인 **Jupyter notebook** 을 이 저장소와 개인의 독립적인 저장소에 있는 특정 언어 중심의 커널의 도움으로 지속적으로 개발할 것입니다.
|
||||
[[The Big Split™ announcement](https://blog.jupyter.org/2015/04/15/the-big-split/)]
|
||||
[[Jupyter Ascending blog post](http://blog.jupyter.org/2015/08/12/first-release-of-jupyter/)]
|
||||
|
||||
## 설치
|
||||
설치법 문서는 다음 주소에서 찾을 수 있습니다.
|
||||
You can find the installation documentation for the
|
||||
[Jupyter platform, on ReadTheDocs](https://jupyter.readthedocs.io/en/latest/install.html).
|
||||
조금 더 심화된 Jupyter notebook의 사용은 다음 주소에서 볼 수 있습니다.
|
||||
[here](https://jupyter-notebook.readthedocs.io/en/latest/).
|
||||
|
||||
설치를 위해서는
|
||||
[pip installed](https://pip.readthedocs.io/en/stable/installing/) 가 있는지 확인한 후 다음을 실행해주세요:
|
||||
|
||||
$ pip install notebook
|
||||
|
||||
## 활용 - Jupyter notebook 실행하기
|
||||
|
||||
### 로컬에서 실행할 때
|
||||
|
||||
이와 같이 실행하세요:
|
||||
|
||||
$ jupyter notebook
|
||||
|
||||
## 개발 설치
|
||||
|
||||
[`CONTRIBUTING.rst`](CONTRIBUTING.rst) 을 통해 설치법을 확인하세요.
|
||||
|
||||
## 기여하기
|
||||
|
||||
이 프로젝트에 기여를 하고 싶다면, [`CONTRIBUTING.rst`](CONTRIBUTING.rst) 을 참고해주세요.
|
||||
|
||||
## 자료
|
||||
- [Project Jupyter website](https://jupyter.org)
|
||||
- [Online Demo at try.jupyter.org](https://try.jupyter.org)
|
||||
- [Documentation for Jupyter notebook](https://jupyter-notebook.readthedocs.io/en/latest/) [[PDF](https://media.readthedocs.org/pdf/jupyter-notebook/latest/jupyter-notebook.pdf)]
|
||||
- [Documentation for Project Jupyter](https://jupyter.readthedocs.io/en/latest/index.html) [[PDF](https://media.readthedocs.org/pdf/jupyter/latest/jupyter.pdf)]
|
||||
- [Issues](https://github.com/jupyter/notebook/issues)
|
||||
- [Technical support - Jupyter Google Group](https://groups.google.com/forum/#!forum/jupyter)
|
||||
@ -1,38 +0,0 @@
|
||||
# UI 기능
|
||||
|
||||
버그 리포트나 Jupyter Mailing list에 메일을 보내려고 할 때, 개발자나 사용자들이 버그를 진단하거나 해결해줄 경우 다른 UI를 사용하면 시간이 단축된다.
|
||||
이번 장에서는 Notebook과 Notebook의 다른 모드의 UI 요소를 알려줄 것이다.
|
||||
|
||||
## Notebook Dashboard
|
||||
|
||||
jupyter notebook 명령어를 실행하면 가장 먼저 Notebook Dashboard가 나타난다.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
## Notebook 편집기
|
||||
|
||||
편집을 위해 Notebook을 선택했다면, Notebook은 Notebook편집기를 열어준다.
|
||||
|
||||

|
||||
## Notebook 의 사용자 도움 인터페이스
|
||||
|
||||
만약 Notebook 편집기의 특정 요소를 더 배우고 싶다면, 도움 메뉴 - 사용자 인터페이스 를 선택함으로써 사용사 인터페이스 도움말을 볼 수 있습니다.
|
||||
|
||||
## 편집 모드와 Notebook편집기
|
||||
|
||||
셀이 편집모드에 있다면, 셀 모드 지시자는 셀의 상태를 반영합니다. 이 상태는 오른쪽 위의 작은 연필모양으로 선택가능합니다. 셀이 명령 모드에 있다면, 그 위치에 아이콘이 없습니다.
|
||||
|
||||

|
||||
|
||||
## 파일 편집기
|
||||
|
||||
이제 Notebook Dashboard 안의 Notebook 파일이 아닌 표시된 파일을 선택하여 열어야한다고 한다면, 파일은 파일 편집기로 열립니다.
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 331 KiB |
|
Before Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 331 KiB |
@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
from notebook.notebookapp import NotebookApp
|
||||
|
||||
header = """\
|
||||
.. _config:
|
||||
|
||||
|
||||
Config file and command line options
|
||||
====================================
|
||||
|
||||
The notebook server can be run with a variety of command line arguments.
|
||||
A list of available options can be found below in the :ref:`options section
|
||||
<options>`.
|
||||
|
||||
Defaults for these options can also be set by creating a file named
|
||||
``jupyter_notebook_config.py`` in your Jupyter folder. The Jupyter
|
||||
folder is in your home directory, ``~/.jupyter``.
|
||||
|
||||
To create a ``jupyter_notebook_config.py`` file, with all the defaults
|
||||
commented out, you can use the following command line::
|
||||
|
||||
$ jupyter notebook --generate-config
|
||||
|
||||
|
||||
.. _options:
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
This list of options can be generated by running the following and hitting
|
||||
enter::
|
||||
|
||||
$ jupyter notebook --help
|
||||
|
||||
"""
|
||||
try:
|
||||
destination = os.path.join(os.path.dirname(__file__), 'source/config.rst')
|
||||
except:
|
||||
destination = os.path.join(os.getcwd(), 'config.rst')
|
||||
|
||||
with open(destination, 'w') as f:
|
||||
f.write(header)
|
||||
f.write(NotebookApp().document_config_options())
|
||||
@ -1,5 +1,5 @@
|
||||
sphinx>=1.3.6
|
||||
sphinx-rtd-theme
|
||||
pydata-sphinx-theme
|
||||
nbsphinx
|
||||
sphinxcontrib_github_alt
|
||||
myst_parser
|
||||
|
||||
@ -1,21 +1,16 @@
|
||||
{
|
||||
"markdown": {
|
||||
"parser": "gfm"
|
||||
},
|
||||
"plugins": [
|
||||
"plugins/markdown" ,
|
||||
"jsdoc_plugin.js"
|
||||
],
|
||||
"source": {
|
||||
"include": [
|
||||
"../notebook/static/notebook/js/notebook.js"
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"allowUnknownTags": true
|
||||
},
|
||||
"templates": {
|
||||
"cleverLinks": false,
|
||||
"monospaceLinks": false
|
||||
}
|
||||
"markdown": {
|
||||
"parser": "gfm"
|
||||
},
|
||||
"plugins": ["plugins/markdown", "jsdoc_plugin.js"],
|
||||
"source": {
|
||||
"include": ["../notebook/static/notebook/js/notebook.js"]
|
||||
},
|
||||
"tags": {
|
||||
"allowUnknownTags": true
|
||||
},
|
||||
"templates": {
|
||||
"cleverLinks": false,
|
||||
"monospaceLinks": false
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
exports.handlers = {
|
||||
newDoclet: function(e) {
|
||||
// e.doclet will refer to the newly created doclet
|
||||
// you can read and modify properties of that doclet if you wish
|
||||
if (typeof e.doclet.name === 'string') {
|
||||
if (e.doclet.name[0] == '_') {
|
||||
console.log('Private method "' + e.doclet.longname + '" not documented.');
|
||||
e.doclet.memberof = '<anonymous>';
|
||||
}
|
||||
}
|
||||
newDoclet: function(e) {
|
||||
// e.doclet will refer to the newly created doclet
|
||||
// you can read and modify properties of that doclet if you wish
|
||||
if (typeof e.doclet.name === 'string') {
|
||||
if (e.doclet.name[0] == '_') {
|
||||
console.log(
|
||||
'Private method "' + e.doclet.longname + '" not documented.'
|
||||
);
|
||||
e.doclet.memberof = '<anonymous>';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
Before Width: | Height: | Size: 566 KiB |
|
Before Width: | Height: | Size: 614 KiB |
|
Before Width: | Height: | Size: 331 KiB After Width: | Height: | Size: 940 KiB |
|
After Width: | Height: | Size: 940 KiB |
@ -1,98 +0,0 @@
|
||||
Comms
|
||||
=====
|
||||
|
||||
*Comms* allow custom messages between the frontend and the kernel. They are used,
|
||||
for instance, in `ipywidgets <https://ipywidgets.readthedocs.io/en/latest/>`__ to
|
||||
update widget state.
|
||||
|
||||
A comm consists of a pair of objects, in the kernel and the frontend, with an
|
||||
automatically assigned unique ID. When one side sends a message, a callback on
|
||||
the other side is triggered with that message data. Either side, the frontend
|
||||
or kernel, can open or close the comm.
|
||||
|
||||
.. seealso::
|
||||
|
||||
`Custom Messages <https://jupyter-client.readthedocs.io/en/latest/messaging.html#custom-messages>`__
|
||||
The messaging specification section on comms
|
||||
|
||||
Opening a comm from the kernel
|
||||
------------------------------
|
||||
|
||||
First, the function to accept the comm must be available on the frontend. This
|
||||
can either be specified in a `requirejs` module, or registered in a registry, for
|
||||
example when an :doc:`extension <extending/frontend_extensions>` is loaded.
|
||||
This example shows a frontend comm target registered in a registry:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
Jupyter.notebook.kernel.comm_manager.register_target('my_comm_target',
|
||||
function(comm, msg) {
|
||||
// comm is the frontend comm instance
|
||||
// msg is the comm_open message, which can carry data
|
||||
|
||||
// Register handlers for later messages:
|
||||
comm.on_msg(function(msg) {...});
|
||||
comm.on_close(function(msg) {...});
|
||||
comm.send({'foo': 0});
|
||||
});
|
||||
|
||||
Now that the frontend comm is registered, you can open the comm from the kernel:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from ipykernel.comm import Comm
|
||||
|
||||
# Use comm to send a message from the kernel
|
||||
my_comm = Comm(target_name='my_comm_target', data={'foo': 1})
|
||||
my_comm.send({'foo': 2})
|
||||
|
||||
# Add a callback for received messages.
|
||||
@my_comm.on_msg
|
||||
def _recv(msg):
|
||||
# Use msg['content']['data'] for the data in the message
|
||||
|
||||
|
||||
This example uses the IPython kernel; it's up to each language kernel what API,
|
||||
if any, it offers for using comms.
|
||||
|
||||
Opening a comm from the frontend
|
||||
--------------------------------
|
||||
|
||||
This is very similar to above, but in reverse. First, a comm target must be
|
||||
registered in the kernel. For instance, this may be done by code displaying
|
||||
output: it will register a target in the kernel, and then display output
|
||||
containing Javascript to connect to it.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def target_func(comm, open_msg):
|
||||
# comm is the kernel Comm instance
|
||||
# msg is the comm_open message
|
||||
|
||||
# Register handler for later messages
|
||||
@comm.on_msg
|
||||
def _recv(msg):
|
||||
# Use msg['content']['data'] for the data in the message
|
||||
comm.send({'echo': msg['content']['data']})
|
||||
|
||||
# Send data to the frontend on creation
|
||||
comm.send({'foo': 5})
|
||||
|
||||
get_ipython().kernel.comm_manager.register_target('my_comm_target', target_func)
|
||||
|
||||
This example uses the IPython kernel again; this example will be different in
|
||||
other kernels that support comms. Refer to the specific language kernel's
|
||||
documentation for comms support.
|
||||
|
||||
And then open the comm from the frontend:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const comm = Jupyter.notebook.kernel.comm_manager.new_comm('my_comm_target', {'foo': 6})
|
||||
// Send data
|
||||
comm.send({'foo': 7})
|
||||
|
||||
// Register a handler
|
||||
comm.on_msg(function(msg) {
|
||||
console.log(msg.content.data.foo);
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
=============
|
||||
Configuration
|
||||
=============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Configuration
|
||||
|
||||
config_overview
|
||||
config
|
||||
Security <https://jupyter-server.readthedocs.io/en/stable/operators/security.html>
|
||||
extending/index.rst
|
||||
@ -0,0 +1,10 @@
|
||||
=========================
|
||||
Contributor Documentation
|
||||
=========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Contributor Documentation
|
||||
|
||||
contributing
|
||||
development_faq
|
||||
@ -1,603 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Embracing web standards"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"One of the main reasons why we developed the current notebook web application \n",
|
||||
"was to embrace the web technology. \n",
|
||||
"\n",
|
||||
"By being a pure web application using HTML, JavaScript, and CSS, the Notebook can get \n",
|
||||
"all the web technology improvement for free. Thus, as browser support for different \n",
|
||||
"media extend, the notebook web app should be able to be compatible without modification. \n",
|
||||
"\n",
|
||||
"This is also true with performance of the User Interface as the speed of JavaScript VM increases. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The other advantage of using only web technology is that the code of the interface is fully accessible to the end user and is modifiable live.\n",
|
||||
"Even if this task is not always easy, we strive to keep our code as accessible and reusable as possible.\n",
|
||||
"This should allow us - with minimum effort - development of small extensions that customize the behavior of the web interface. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Tampering with the Notebook application"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The first tool that is available to you and that you should be aware of are browser \"developers tool\". The exact naming can change across browser and might require the installation of extensions. But basically they can allow you to inspect/modify the DOM, and interact with the JavaScript code that runs the frontend.\n",
|
||||
"\n",
|
||||
" - In Chrome and Safari, Developer tools are in the menu `View > Developer > JavaScript Console` \n",
|
||||
" - In Firefox you might need to install [Firebug](http://getfirebug.com/)\n",
|
||||
" \n",
|
||||
"Those will be your best friends to debug and try different approaches for your extensions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Injecting JS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Using magics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"The above tools can be tedious for editing edit long JavaScript files. Therefore we provide the `%%javascript` magic. This allows you to quickly inject JavaScript into the notebook. Still the JavaScript injected this way will not survive reloading. Hence, it is a good tool for testing and refining a script.\n",
|
||||
"\n",
|
||||
"You might see here and there people modifying css and injecting js into the notebook by reading file(s) and publishing them into the notebook.\n",
|
||||
"Not only does this often break the flow of the notebook and make the re-execution of the notebook broken, but it also means that you need to execute those cells in the entire notebook every time you need to update the code.\n",
|
||||
"\n",
|
||||
"This can still be useful in some cases, like the `%autosave` magic that allows you to control the time between each save. But this can be replaced by a JavaScript dropdown menu to select the save interval."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"## you can inspect the autosave code to see what it does.\n",
|
||||
"%autosave??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### custom.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"To inject JavaScript we provide an entry point: `custom.js` that allows the user to execute and load other resources into the notebook.\n",
|
||||
"JavaScript code in `custom.js` will be executed when the notebook app starts and can then be used to customize almost anything in the UI and in the behavior of the notebook.\n",
|
||||
"\n",
|
||||
"`custom.js` can be found in the `~/.jupyter/custom/custom.js`. You can share your custom.js with others."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"##### Back to theory"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from jupyter_core.paths import jupyter_config_dir\n",
|
||||
"jupyter_dir = jupyter_config_dir()\n",
|
||||
"jupyter_dir"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"and custom js is in "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os.path\n",
|
||||
"custom_js_path = os.path.join(jupyter_dir, 'custom', 'custom.js')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# my custom js\n",
|
||||
"if os.path.isfile(custom_js_path):\n",
|
||||
" with open(custom_js_path) as f:\n",
|
||||
" print(f.read())\n",
|
||||
"else:\n",
|
||||
" print(\"You don't have a custom.js file\") "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Note that `custom.js` is meant to be modified by user. When writing a script, you can define it in a separate file and add a line of configuration into `custom.js` that will fetch and execute the file."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Warning** : even if modification of `custom.js` takes effect immediately after browser refresh (except if browser cache is aggressive), *creating* a file in `static/` directory needs a **server restart**."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Exercise :"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
" - Create a `custom.js` in the right location with the following content:\n",
|
||||
"```javascript\n",
|
||||
"alert(\"hello world from custom.js\")\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
" - Restart your server and open any notebook.\n",
|
||||
" - Be greeted by custom.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Have a look at [default custom.js](https://github.com/jupyter/notebook/blob/4.0.x/notebook/static/custom/custom.js), to see it's content and for more explanation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### For the quick ones : "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"We've seen above that you can change the autosave rate by using a magic. This is typically something I don't want to type every time, and that I don't like to embed into my workflow and documents. (readers don't care what my autosave time is). Let's build an extension that allows us to do it. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"foo": true
|
||||
},
|
||||
"source": [
|
||||
"Create a dropdown element in the toolbar (DOM `Jupyter.toolbar.element`), you will need \n",
|
||||
"\n",
|
||||
"- `Jupyter.notebook.set_autosave_interval(milliseconds)`\n",
|
||||
"- know that 1 min = 60 sec, and 1 sec = 1000 ms"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"```javascript\n",
|
||||
"\n",
|
||||
"var label = jQuery('<label/>').text('AutoScroll Limit:');\n",
|
||||
"var select = jQuery('<select/>')\n",
|
||||
" //.append(jQuery('<option/>').attr('value', '2').text('2min (default)'))\n",
|
||||
" .append(jQuery('<option/>').attr('value', undefined).text('disabled'))\n",
|
||||
"\n",
|
||||
" // TODO:\n",
|
||||
" //the_toolbar_element.append(label)\n",
|
||||
" //the_toolbar_element.append(select);\n",
|
||||
" \n",
|
||||
"select.change(function() {\n",
|
||||
" var val = jQuery(this).val() // val will be the value in [2]\n",
|
||||
" // TODO\n",
|
||||
" // this will be called when dropdown changes\n",
|
||||
"\n",
|
||||
"});\n",
|
||||
"\n",
|
||||
"var time_m = [1,5,10,15,30];\n",
|
||||
"for (var i=0; i < time_m.length; i++) {\n",
|
||||
" var ts = time_m[i];\n",
|
||||
" //[2] ____ this will be `val` on [1] \n",
|
||||
" // | \n",
|
||||
" // v \n",
|
||||
" select.append($('<option/>').attr('value', ts).text(thr+'min'));\n",
|
||||
" // this will fill up the dropdown `select` with\n",
|
||||
" // 1 min\n",
|
||||
" // 5 min\n",
|
||||
" // 10 min\n",
|
||||
" // 10 min\n",
|
||||
" // ...\n",
|
||||
"}\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### A non-interactive example first"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"I like my cython to be nicely highlighted\n",
|
||||
"\n",
|
||||
"```javascript\n",
|
||||
"Jupyter.config.cell_magic_highlight['magic_text/x-cython'] = {}\n",
|
||||
"Jupyter.config.cell_magic_highlight['magic_text/x-cython'].reg = [/^%%cython/]\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"`text/x-cython` is the name of CodeMirror mode name, `magic_` prefix will just patch the mode so that the first line that contains a magic does not screw up the highlighting. `reg`is a list or regular expression that will trigger the change of mode."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Get more documentation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Sadly, you will have to read the js source file (but there are lots of comments) and/or build the JavaScript documentation using yuidoc.\n",
|
||||
"If you have `node` and `yui-doc` installed:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"```bash\n",
|
||||
"$ cd ~/jupyter/notebook/notebook/static/notebook/js/\n",
|
||||
"$ yuidoc . --server\n",
|
||||
"warn: (yuidoc): Failed to extract port, setting to the default :3000\n",
|
||||
"info: (yuidoc): Starting YUIDoc@0.3.45 using YUI@3.9.1 with NodeJS@0.10.15\n",
|
||||
"info: (yuidoc): Scanning for yuidoc.json file.\n",
|
||||
"info: (yuidoc): Starting YUIDoc with the following options:\n",
|
||||
"info: (yuidoc):\n",
|
||||
"{ port: 3000,\n",
|
||||
" nocode: false,\n",
|
||||
" paths: [ '.' ],\n",
|
||||
" server: true,\n",
|
||||
" outdir: './out' }\n",
|
||||
"info: (yuidoc): Scanning for yuidoc.json file.\n",
|
||||
"info: (server): Starting server: http://127.0.0.1:3000\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"and browse http://127.0.0.1:3000 to get documentation"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"foo": true
|
||||
},
|
||||
"source": [
|
||||
"#### Some convenience methods"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"By browsing the documentation you will see that we have some convenience methods that allows us to avoid re-inventing the UI every time :\n",
|
||||
"```javascript\n",
|
||||
"Jupyter.toolbar.add_buttons_group([\n",
|
||||
" {\n",
|
||||
" 'label' : 'run qtconsole',\n",
|
||||
" 'icon' : 'fa-terminal', // select your icon from \n",
|
||||
" // http://fontawesome.io/icons/\n",
|
||||
" 'callback': function(){Jupyter.notebook.kernel.execute('%qtconsole')}\n",
|
||||
" }\n",
|
||||
" // add more button here if needed.\n",
|
||||
" ]);\n",
|
||||
"```\n",
|
||||
"with a [lot of icons] you can select from. \n",
|
||||
"\n",
|
||||
"[lot of icons]: http://fontawesome.io/icons/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"foo": true
|
||||
},
|
||||
"source": [
|
||||
"## Cell Metadata"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"foo": true
|
||||
},
|
||||
"source": [
|
||||
"The most requested feature is generally to be able to distinguish an individual cell in the notebook, or run a specific action with them.\n",
|
||||
"To do so, you can either use `Jupyter.notebook.get_selected_cell()`, or rely on `CellToolbar`. This allows you to register a set of actions and graphical elements that will be attached to individual cells."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Cell Toolbar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You can see some example of what can be done by toggling the `Cell Toolbar` selector in the toolbar on top of the notebook. It provides two default `presets` that are `Default` and `slideshow`. Default allows the user to edit the metadata attached to each cell manually."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"First we define a function that takes at first parameter an element on the DOM in which to inject UI element. The second element is the cell this element wis registered with. Then we will need to register that function and give it a name.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Register a callback"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%javascript\n",
|
||||
"var CellToolbar = Jupyter.CellToolbar\n",
|
||||
"var toggle = function(div, cell) {\n",
|
||||
" var button_container = $(div)\n",
|
||||
"\n",
|
||||
" // let's create a button that shows the current value of the metadata\n",
|
||||
" var button = $('<button/>').addClass('btn btn-mini').text(String(cell.metadata.foo));\n",
|
||||
"\n",
|
||||
" // On click, change the metadata value and update the button label\n",
|
||||
" button.click(function(){\n",
|
||||
" var v = cell.metadata.foo;\n",
|
||||
" cell.metadata.foo = !v;\n",
|
||||
" button.text(String(!v));\n",
|
||||
" })\n",
|
||||
"\n",
|
||||
" // add the button to the DOM div.\n",
|
||||
" button_container.append(button);\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
" // now we register the callback under the name foo to give the\n",
|
||||
" // user the ability to use it later\n",
|
||||
" CellToolbar.register_callback('tuto.foo', toggle);"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Registering a preset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"This function can now be part of many `preset` of the CellToolBar."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": false,
|
||||
"foo": true,
|
||||
"slideshow": {
|
||||
"slide_type": "subslide"
|
||||
}
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%javascript\n",
|
||||
"Jupyter.CellToolbar.register_preset('Tutorial 1',['tuto.foo','default.rawedit'])\n",
|
||||
"Jupyter.CellToolbar.register_preset('Tutorial 2',['slideshow.select','tuto.foo'])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"You should now have access to two presets :\n",
|
||||
"\n",
|
||||
" - Tutorial 1\n",
|
||||
" - Tutorial 2\n",
|
||||
" \n",
|
||||
"And check that the buttons you defined share state when you toggle preset. \n",
|
||||
"Also check that the metadata of the cell is modified when you click the button, and that when saved on reloaded the metadata is still available."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Exercise:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Try to wrap the all code in a file, put this file in `{jupyter_dir}/custom/<a-name>.js`, and add \n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"require(['custom/<a-name>']);\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"in `custom.js` to have this script automatically loaded in all your notebooks.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"`require` is provided by a [JavaScript library](http://requirejs.org/) that allow you to express dependency. For simple extension like the previous one we directly mute the global namespace, but for more complex extension you could pass a callback to `require([...], <callback>)` call, to allow the user to pass configuration information to your plugin.\n",
|
||||
"\n",
|
||||
"In Python language, \n",
|
||||
"\n",
|
||||
"```javascript\n",
|
||||
"require(['a/b', 'c/d'], function( e, f){\n",
|
||||
" e.something()\n",
|
||||
" f.something()\n",
|
||||
"})\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"could be read as\n",
|
||||
"```python\n",
|
||||
"import a.b as e\n",
|
||||
"import c.d as f\n",
|
||||
"e.something()\n",
|
||||
"f.something()\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"See for example @damianavila [\"ZenMode\" plugin](https://github.com/ipython-contrib/jupyter_contrib_nbextensions/blob/b29c698394239a6931fa4911440550df214812cb/src/jupyter_contrib_nbextensions/nbextensions/zenmode/main.js#L32) :\n",
|
||||
"\n",
|
||||
"```javascript\n",
|
||||
"\n",
|
||||
"// read that as\n",
|
||||
"// import custom.zenmode.main as zenmode\n",
|
||||
"require(['custom/zenmode/main'],function(zenmode){\n",
|
||||
" zenmode.background('images/back12.jpg');\n",
|
||||
"})\n",
|
||||
"```\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### For the quickest"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Try to use [the following](https://github.com/ipython/ipython/blob/1.x/IPython/html/static/notebook/js/celltoolbar.js#L367) to bind a dropdown list to `cell.metadata.difficulty.select`. \n",
|
||||
"\n",
|
||||
"It should be able to take the 4 following values :\n",
|
||||
"\n",
|
||||
" - `<None>`\n",
|
||||
" - `Easy`\n",
|
||||
" - `Medium`\n",
|
||||
" - `Hard`\n",
|
||||
" \n",
|
||||
"We will use it to customize the output of the converted notebook depending on the tag on each cell"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"metadata": {
|
||||
"collapsed": false
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# %load soln/celldiff.js"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.5.2"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 1
|
||||
}
|
||||
@ -1,183 +0,0 @@
|
||||
Custom bundler extensions
|
||||
=========================
|
||||
|
||||
The notebook server supports the writing of *bundler extensions* that
|
||||
transform, package, and download/deploy notebook files. As a developer, you
|
||||
need only write a single Python function to implement a bundler. The notebook
|
||||
server automatically generates a *File -> Download as* or *File -> Deploy as*
|
||||
menu item in the notebook front-end to trigger your bundler.
|
||||
|
||||
Here are some examples of what you can implement using bundler extensions:
|
||||
|
||||
* Convert a notebook file to a HTML document and publish it as a post on a
|
||||
blog site
|
||||
* Create a snapshot of the current notebook environment and bundle that
|
||||
definition plus notebook into a zip download
|
||||
* Deploy a notebook as a standalone, interactive `dashboard <https://github.com/jupyter-incubator/dashboards_bundlers>`_
|
||||
|
||||
To implement a bundler extension, you must do all of the following:
|
||||
|
||||
* Declare bundler extension metadata in your Python package
|
||||
* Write a `bundle` function that responds to bundle requests
|
||||
* Instruct your users on how to enable/disable your bundler extension
|
||||
|
||||
The following sections describe these steps in detail.
|
||||
|
||||
Declaring bundler metadata
|
||||
--------------------------
|
||||
|
||||
You must provide information about the bundler extension(s) your package
|
||||
provides by implementing a `_jupyter_bundlerextensions_paths` function. This
|
||||
function can reside anywhere in your package so long as it can be imported
|
||||
when enabling the bundler extension. (See :ref:`enabling-bundlers`.)
|
||||
|
||||
.. code:: python
|
||||
|
||||
# in mypackage.hello_bundler
|
||||
|
||||
def _jupyter_bundlerextension_paths():
|
||||
"""Example "hello world" bundler extension"""
|
||||
return [{
|
||||
'name': 'hello_bundler', # unique bundler name
|
||||
'label': 'Hello Bundler', # human-readable menu item label
|
||||
'module_name': 'mypackage.hello_bundler', # module containing bundle()
|
||||
'group': 'deploy' # group under 'deploy' or 'download' menu
|
||||
}]
|
||||
|
||||
Note that the return value is a list. By returning multiple dictionaries in
|
||||
the list, you allow users to enable/disable sets of bundlers all at once.
|
||||
|
||||
Writing the `bundle` function
|
||||
-----------------------------
|
||||
|
||||
At runtime, a menu item with the given label appears either in the
|
||||
*File -> Deploy as* or *File -> Download as* menu depending on the `group`
|
||||
value in your metadata. When a user clicks the menu item, a new browser tab
|
||||
opens and notebook server invokes a `bundle` function in the `module_name`
|
||||
specified in the metadata.
|
||||
|
||||
You must implement a `bundle` function that matches the signature of the
|
||||
following example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# in mypackage.hello_bundler
|
||||
|
||||
def bundle(handler, model):
|
||||
"""Transform, convert, bundle, etc. the notebook referenced by the given
|
||||
model.
|
||||
|
||||
Then issue a Tornado web response using the `handler` to redirect
|
||||
the user's browser, download a file, show a HTML page, etc. This function
|
||||
must finish the handler response before returning either explicitly or by
|
||||
raising an exception.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
handler : tornado.web.RequestHandler
|
||||
Handler that serviced the bundle request
|
||||
model : dict
|
||||
Notebook model from the configured ContentManager
|
||||
"""
|
||||
handler.finish('I bundled {}!'.format(model['path']))
|
||||
|
||||
Your `bundle` function is free to do whatever it wants with the request and
|
||||
respond in any manner. For example, it may read additional query parameters
|
||||
from the request, issue a redirect to another site, run a local process (e.g.,
|
||||
`nbconvert`), make a HTTP request to another service, etc.
|
||||
|
||||
The caller of the `bundle` function is `@tornado.gen.coroutine` decorated and
|
||||
wraps its call with `torando.gen.maybe_future`. This behavior means you may
|
||||
handle the web request synchronously, as in the example above, or
|
||||
asynchronously using `@tornado.gen.coroutine` and `yield`, as in the example
|
||||
below.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from tornado import gen
|
||||
|
||||
@gen.coroutine
|
||||
def bundle(handler, model):
|
||||
# simulate a long running IO op (e.g., deploying to a remote host)
|
||||
yield gen.sleep(10)
|
||||
|
||||
# now respond
|
||||
handler.finish('I spent 10 seconds bundling {}!'.format(model['path']))
|
||||
|
||||
You should prefer the second, asynchronous approach when your bundle operation
|
||||
is long-running and would otherwise block the notebook server main loop if
|
||||
handled synchronously.
|
||||
|
||||
For more details about the data flow from menu item click to bundle function
|
||||
invocation, see :ref:`bundler-details`.
|
||||
|
||||
.. _enabling-bundlers:
|
||||
|
||||
Enabling/disabling bundler extensions
|
||||
-------------------------------------
|
||||
|
||||
The notebook server includes a command line interface (CLI) for enabling and
|
||||
disabling bundler extensions.
|
||||
|
||||
You should document the basic commands for enabling and disabling your
|
||||
bundler. One possible command for enabling the `hello_bundler` example is the
|
||||
following:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension enable --py mypackage.hello_bundler --sys-prefix
|
||||
|
||||
The above updates the notebook configuration file in the current
|
||||
conda/virtualenv environment (`--sys-prefix`) with the metadata returned by
|
||||
the `mypackage.hellow_bundler._jupyter_bundlerextension_paths` function.
|
||||
|
||||
The corresponding command to later disable the bundler extension is the
|
||||
following:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension disable --py mypackage.hello_bundler --sys-prefix
|
||||
|
||||
For more help using the `bundlerextension` subcommand, run the following.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension --help
|
||||
|
||||
The output describes options for listing enabled bundlers, configuring
|
||||
bundlers for single users, configuring bundlers system-wide, etc.
|
||||
|
||||
Example: IPython Notebook bundle (.zip)
|
||||
---------------------------------------
|
||||
|
||||
The `hello_bundler` example in this documentation is simplistic in the name
|
||||
of brevity. For more meaningful examples, see
|
||||
`notebook/bundler/zip_bundler.py` and `notebook/bundler/tarball_bundler.py`.
|
||||
You can enable them to try them like so:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension enable --py notebook.bundler.zip_bundler --sys-prefix
|
||||
jupyter bundlerextension enable --py notebook.bundler.tarball_bundler --sys-prefix
|
||||
|
||||
.. _bundler-details:
|
||||
|
||||
Bundler invocation details
|
||||
--------------------------
|
||||
|
||||
Support for bundler extensions comes from Python modules in `notebook/bundler`
|
||||
and JavaScript in `notebook/static/notebook/js/menubar.js`. The flow of data
|
||||
between the various components proceeds roughly as follows:
|
||||
|
||||
1. User opens a notebook document
|
||||
2. Notebook front-end JavaScript loads notebook configuration
|
||||
3. Bundler front-end JS creates menu items for all bundler extensions in the
|
||||
config
|
||||
4. User clicks a bundler menu item
|
||||
5. JS click handler opens a new browser window/tab to
|
||||
`<notebook base_url>/bundle/<path/to/notebook>?bundler=<name>` (i.e., a
|
||||
HTTP GET request)
|
||||
6. Bundle handler validates the notebook path and bundler `name`
|
||||
7. Bundle handler delegates the request to the `bundle` function in the
|
||||
bundler's `module_name`
|
||||
8. `bundle` function finishes the HTTP request
|
||||
@ -1,293 +0,0 @@
|
||||
.. _contents_api:
|
||||
|
||||
Contents API
|
||||
============
|
||||
|
||||
.. currentmodule:: notebook.services.contents
|
||||
|
||||
The Jupyter Notebook web application provides a graphical interface for
|
||||
creating, opening, renaming, and deleting files in a virtual filesystem.
|
||||
|
||||
The :class:`~manager.ContentsManager` class defines an abstract
|
||||
API for translating these interactions into operations on a particular storage
|
||||
medium. The default implementation,
|
||||
:class:`~filemanager.FileContentsManager`, uses the local
|
||||
filesystem of the server for storage and straightforwardly serializes notebooks
|
||||
into JSON. Users can override these behaviors by supplying custom subclasses
|
||||
of ContentsManager.
|
||||
|
||||
This section describes the interface implemented by ContentsManager subclasses.
|
||||
We refer to this interface as the **Contents API**.
|
||||
|
||||
Data Model
|
||||
----------
|
||||
|
||||
.. currentmodule:: notebook.services.contents.manager
|
||||
|
||||
Filesystem Entities
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
.. _notebook models:
|
||||
|
||||
ContentsManager methods represent virtual filesystem entities as dictionaries,
|
||||
which we refer to as **models**.
|
||||
|
||||
Models may contain the following entries:
|
||||
|
||||
+--------------------+-----------+------------------------------+
|
||||
| Key | Type |Info |
|
||||
+====================+===========+==============================+
|
||||
|**name** |unicode |Basename of the entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**path** |unicode |Full |
|
||||
| | |(:ref:`API-style<apipaths>`) |
|
||||
| | |path to the entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**type** |unicode |The entity type. One of |
|
||||
| | |``"notebook"``, ``"file"`` or |
|
||||
| | |``"directory"``. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**created** |datetime |Creation date of the entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**last_modified** |datetime |Last modified date of the |
|
||||
| | |entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**content** |variable |The "content" of the entity. |
|
||||
| | |(:ref:`See |
|
||||
| | |Below<modelcontent>`) |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**mimetype** |unicode or |The mimetype of ``content``, |
|
||||
| |``None`` |if any. (:ref:`See |
|
||||
| | |Below<modelcontent>`) |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**format** |unicode or |The format of ``content``, |
|
||||
| |``None`` |if any. (:ref:`See |
|
||||
| | |Below<modelcontent>`) |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|
||||
.. _modelcontent:
|
||||
|
||||
Certain model fields vary in structure depending on the ``type`` field of the
|
||||
model. There are three model types: **notebook**, **file**, and **directory**.
|
||||
|
||||
- ``notebook`` models
|
||||
- The ``format`` field is always ``"json"``.
|
||||
- The ``mimetype`` field is always ``None``.
|
||||
- The ``content`` field contains a
|
||||
:class:`nbformat.notebooknode.NotebookNode` representing the .ipynb file
|
||||
represented by the model. See the `NBFormat`_ documentation for a full
|
||||
description.
|
||||
|
||||
- ``file`` models
|
||||
- The ``format`` field is either ``"text"`` or ``"base64"``.
|
||||
- The ``mimetype`` field can be any mimetype string, but defaults to
|
||||
``text/plain`` for text-format models and
|
||||
``application/octet-stream`` for base64-format models. For files with
|
||||
unknown mime types (e.g. unknown file extensions), this field may be
|
||||
`None`.
|
||||
- The ``content`` field is always of type ``unicode``. For text-format
|
||||
file models, ``content`` simply contains the file's bytes after decoding
|
||||
as UTF-8. Non-text (``base64``) files are read as bytes, base64 encoded,
|
||||
and then decoded as UTF-8.
|
||||
|
||||
- ``directory`` models
|
||||
- The ``format`` field is always ``"json"``.
|
||||
- The ``mimetype`` field is always ``None``.
|
||||
- The ``content`` field contains a list of :ref:`content-free<contentfree>`
|
||||
models representing the entities in the directory.
|
||||
|
||||
.. note::
|
||||
|
||||
.. _contentfree:
|
||||
|
||||
In certain circumstances, we don't need the full content of an entity to
|
||||
complete a Contents API request. In such cases, we omit the ``content``, and
|
||||
``format`` keys from the model. The default values for the ``mimetype``
|
||||
field will might also not be evaluated, in which case it will be set as `None`.
|
||||
This reduced reply most commonly occurs when listing a directory, in
|
||||
which circumstance we represent files within the directory as content-less
|
||||
models to avoid having to recursively traverse and serialize the entire
|
||||
filesystem.
|
||||
|
||||
**Sample Models**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Notebook Model with Content
|
||||
{
|
||||
'content': {
|
||||
'metadata': {},
|
||||
'nbformat': 4,
|
||||
'nbformat_minor': 0,
|
||||
'cells': [
|
||||
{
|
||||
'cell_type': 'markdown',
|
||||
'metadata': {},
|
||||
'source': 'Some **Markdown**',
|
||||
},
|
||||
],
|
||||
},
|
||||
'created': datetime(2015, 7, 25, 19, 50, 19, 19865),
|
||||
'format': 'json',
|
||||
'last_modified': datetime(2015, 7, 25, 19, 50, 19, 19865),
|
||||
'mimetype': None,
|
||||
'name': 'a.ipynb',
|
||||
'path': 'foo/a.ipynb',
|
||||
'type': 'notebook',
|
||||
'writable': True,
|
||||
}
|
||||
|
||||
# Notebook Model without Content
|
||||
{
|
||||
'content': None,
|
||||
'created': datetime.datetime(2015, 7, 25, 20, 17, 33, 271931),
|
||||
'format': None,
|
||||
'last_modified': datetime.datetime(2015, 7, 25, 20, 17, 33, 271931),
|
||||
'mimetype': None,
|
||||
'name': 'a.ipynb',
|
||||
'path': 'foo/a.ipynb',
|
||||
'type': 'notebook',
|
||||
'writable': True
|
||||
}
|
||||
|
||||
|
||||
API Paths
|
||||
~~~~~~~~~
|
||||
.. _apipaths:
|
||||
|
||||
ContentsManager methods represent the locations of filesystem resources as
|
||||
**API-style paths**. Such paths are interpreted as relative to the root
|
||||
directory of the notebook server. For compatibility across systems, the
|
||||
following guarantees are made:
|
||||
|
||||
* Paths are always ``unicode``, not ``bytes``.
|
||||
* Paths are not URL-escaped.
|
||||
* Paths are always forward-slash (/) delimited, even on Windows.
|
||||
* Leading and trailing slashes are stripped. For example, ``/foo/bar/buzz/``
|
||||
becomes ``foo/bar/buzz``.
|
||||
* The empty string (``""``) represents the root directory.
|
||||
|
||||
|
||||
Writing a Custom ContentsManager
|
||||
--------------------------------
|
||||
|
||||
The default ContentsManager is designed for users running the notebook as an
|
||||
application on a personal computer. It stores notebooks as .ipynb files on the
|
||||
local filesystem, and it maps files and directories in the Notebook UI to files
|
||||
and directories on disk. It is possible to override how notebooks are stored
|
||||
by implementing your own custom subclass of ``ContentsManager``. For example,
|
||||
if you deploy the notebook in a context where you don't trust or don't have
|
||||
access to the filesystem of the notebook server, it's possible to write your
|
||||
own ContentsManager that stores notebooks and files in a database.
|
||||
|
||||
|
||||
Required Methods
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
A minimal complete implementation of a custom
|
||||
:class:`~manager.ContentsManager` must implement the following
|
||||
methods:
|
||||
|
||||
.. autosummary::
|
||||
ContentsManager.get
|
||||
ContentsManager.save
|
||||
ContentsManager.delete_file
|
||||
ContentsManager.rename_file
|
||||
ContentsManager.file_exists
|
||||
ContentsManager.dir_exists
|
||||
ContentsManager.is_hidden
|
||||
|
||||
You may be required to specify a Checkpoints object, as the default one,
|
||||
``FileCheckpoints``, could be incompatible with your custom
|
||||
ContentsManager.
|
||||
|
||||
|
||||
Chunked Saving
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The contents API allows for "chunked" saving of files, i.e.
|
||||
saving/transmitting in partial pieces:
|
||||
|
||||
* This can only be used when the ``type`` of the model is ``file``.
|
||||
* The model should be as otherwise expected for
|
||||
:meth:`~manager.ContentsManager.save`, with an added field ``chunk``.
|
||||
* The value of ``chunk`` should be an integer starting at ``1``, and incrementing
|
||||
for each subsequent chunk, except for the final chunk, which should be
|
||||
indicated with a value of ``-1``.
|
||||
* The model returned from using :meth:`~manager.ContentsManager.save` with
|
||||
``chunk`` should be treated as unreliable for all chunks except the final one.
|
||||
* Any interaction with a file being saved in a chunked manner is unreliable
|
||||
until the final chunk has been saved. This includes directory listings.
|
||||
|
||||
|
||||
Customizing Checkpoints
|
||||
-----------------------
|
||||
.. currentmodule:: notebook.services.contents.checkpoints
|
||||
|
||||
Customized Checkpoint definitions allows behavior to be
|
||||
altered and extended.
|
||||
|
||||
The ``Checkpoints`` and ``GenericCheckpointsMixin`` classes
|
||||
(from :mod:`notebook.services.contents.checkpoints`)
|
||||
have reusable code and are intended to be used together,
|
||||
but require the following methods to be implemented.
|
||||
|
||||
.. autosummary::
|
||||
Checkpoints.rename_checkpoint
|
||||
Checkpoints.list_checkpoints
|
||||
Checkpoints.delete_checkpoint
|
||||
GenericCheckpointsMixin.create_file_checkpoint
|
||||
GenericCheckpointsMixin.create_notebook_checkpoint
|
||||
GenericCheckpointsMixin.get_file_checkpoint
|
||||
GenericCheckpointsMixin.get_notebook_checkpoint
|
||||
|
||||
No-op example
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Here is an example of a no-op checkpoints object - note the mixin
|
||||
comes first. The docstrings indicate what each method should do or
|
||||
return for a more complete implementation.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class NoOpCheckpoints(GenericCheckpointsMixin, Checkpoints):
|
||||
"""requires the following methods:"""
|
||||
def create_file_checkpoint(self, content, format, path):
|
||||
""" -> checkpoint model"""
|
||||
def create_notebook_checkpoint(self, nb, path):
|
||||
""" -> checkpoint model"""
|
||||
def get_file_checkpoint(self, checkpoint_id, path):
|
||||
""" -> {'type': 'file', 'content': <str>, 'format': {'text', 'base64'}}"""
|
||||
def get_notebook_checkpoint(self, checkpoint_id, path):
|
||||
""" -> {'type': 'notebook', 'content': <output of nbformat.read>}"""
|
||||
def delete_checkpoint(self, checkpoint_id, path):
|
||||
"""deletes a checkpoint for a file"""
|
||||
def list_checkpoints(self, path):
|
||||
"""returns a list of checkpoint models for a given file,
|
||||
default just does one per file
|
||||
"""
|
||||
return []
|
||||
def rename_checkpoint(self, checkpoint_id, old_path, new_path):
|
||||
"""renames checkpoint from old path to new path"""
|
||||
|
||||
See ``GenericFileCheckpoints`` in :mod:`notebook.services.contents.filecheckpoints`
|
||||
for a more complete example.
|
||||
|
||||
Testing
|
||||
-------
|
||||
.. currentmodule:: notebook.services.contents.tests
|
||||
|
||||
:mod:`notebook.services.contents.tests` includes several test suites written
|
||||
against the abstract Contents API. This means that an excellent way to test a
|
||||
new ContentsManager subclass is to subclass our tests to make them use your
|
||||
ContentsManager.
|
||||
|
||||
.. note::
|
||||
|
||||
PGContents_ is an example of a complete implementation of a custom
|
||||
``ContentsManager``. It stores notebooks and files in PostgreSQL_ and encodes
|
||||
directories as SQL relations. PGContents also provides an example of how to
|
||||
re-use the notebook's tests.
|
||||
|
||||
.. _NBFormat: https://nbformat.readthedocs.io/en/latest/index.html
|
||||
.. _PGContents: https://github.com/quantopian/pgcontents
|
||||
.. _PostgreSQL: https://www.postgresql.org/
|
||||
@ -1,280 +1,19 @@
|
||||
.. _frontend_extensions:
|
||||
|
||||
===========================
|
||||
Custom front-end extensions
|
||||
===========================
|
||||
|
||||
This describes the basic steps to write a JavaScript extension for the Jupyter
|
||||
This describes the basic steps to write a TypeScript extension for the Jupyter
|
||||
notebook front-end. This allows you to customize the behaviour of the various
|
||||
pages like the dashboard, the notebook, or the text editor.
|
||||
|
||||
The structure of a front-end extension
|
||||
--------------------------------------
|
||||
|
||||
.. note::
|
||||
|
||||
The notebook front-end and Javascript API are not stable, and are subject
|
||||
to a lot of changes. Any extension written for the current notebook is
|
||||
almost guaranteed to break in the next release.
|
||||
|
||||
.. _AMD module: https://en.wikipedia.org/wiki/Asynchronous_module_definition
|
||||
|
||||
A front-end extension is a JavaScript file that defines an `AMD module`_
|
||||
which exposes at least a function called ``load_ipython_extension``, which
|
||||
takes no arguments. We will not get into the details of what each of these
|
||||
terms consists of yet, but here is the minimal code needed for a working
|
||||
extension:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file my_extension/main.js
|
||||
|
||||
define(function(){
|
||||
|
||||
function load_ipython_extension(){
|
||||
console.info('this is my first extension');
|
||||
}
|
||||
|
||||
return {
|
||||
load_ipython_extension: load_ipython_extension
|
||||
};
|
||||
});
|
||||
|
||||
.. note::
|
||||
|
||||
Although for historical reasons the function is called
|
||||
``load_ipython_extension``, it does apply to the Jupyter notebook in
|
||||
general, and will work regardless of the kernel in use.
|
||||
|
||||
If you are familiar with JavaScript, you can use this template to require any
|
||||
Jupyter module and modify its configuration, or do anything else in client-side
|
||||
Javascript. Your extension will be loaded at the right time during the notebook
|
||||
page initialisation for you to set up a listener for the various events that
|
||||
the page can trigger.
|
||||
|
||||
You might want access to the current instances of the various Jupyter notebook
|
||||
components on the page, as opposed to the classes defined in the modules. The
|
||||
current instances are exposed by a module named ``base/js/namespace``. If you
|
||||
plan on accessing instances on the page, you should ``require`` this module
|
||||
rather than accessing the global variable ``Jupyter``, which will be removed in
|
||||
future. The following example demonstrates how to access the current notebook
|
||||
instance:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file my_extension/main.js
|
||||
|
||||
define([
|
||||
'base/js/namespace'
|
||||
], function(
|
||||
Jupyter
|
||||
) {
|
||||
function load_ipython_extension() {
|
||||
console.log(
|
||||
'This is the current notebook application instance:',
|
||||
Jupyter.notebook
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
load_ipython_extension: load_ipython_extension
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
Modifying key bindings
|
||||
----------------------
|
||||
|
||||
One of the abilities of extensions is to modify key bindings, although once
|
||||
again this is an API which is not guaranteed to be stable. However, custom key
|
||||
bindings are frequently requested, and are helpful to increase accessibility,
|
||||
so in the following we show how to access them.
|
||||
|
||||
Here is an example of an extension that will unbind the shortcut ``0,0`` in
|
||||
command mode, which normally restarts the kernel, and bind ``0,0,0`` in its
|
||||
place:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file my_extension/main.js
|
||||
|
||||
define([
|
||||
'base/js/namespace'
|
||||
], function(
|
||||
Jupyter
|
||||
) {
|
||||
|
||||
function load_ipython_extension() {
|
||||
Jupyter.keyboard_manager.command_shortcuts.remove_shortcut('0,0');
|
||||
Jupyter.keyboard_manager.command_shortcuts.add_shortcut('0,0,0', 'jupyter-notebook:restart-kernel');
|
||||
}
|
||||
|
||||
return {
|
||||
load_ipython_extension: load_ipython_extension
|
||||
};
|
||||
});
|
||||
|
||||
.. note::
|
||||
|
||||
The standard keybindings might not work correctly on non-US keyboards.
|
||||
Unfortunately, this is a limitation of browser implementations and the
|
||||
status of keyboard event handling on the web in general. We appreciate your
|
||||
feedback if you have issues binding keys, or have any ideas to help improve
|
||||
the situation.
|
||||
|
||||
You can see that I have used the **action name**
|
||||
``jupyter-notebook:restart-kernel`` to bind the new shortcut. There is no API
|
||||
yet to access the list of all available *actions*, though the following in the
|
||||
JavaScript console of your browser on a notebook page should give you an idea
|
||||
of what is available:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
Object.keys(require('base/js/namespace').actions._actions);
|
||||
|
||||
In this example, we changed a keyboard shortcut in **command mode**; you
|
||||
can also customize keyboard shortcuts in **edit mode**.
|
||||
However, most of the keyboard shortcuts in edit mode are handled by CodeMirror,
|
||||
which supports custom key bindings via a completely different API.
|
||||
|
||||
|
||||
Defining and registering your own actions
|
||||
-----------------------------------------
|
||||
|
||||
As part of your front-end extension, you may wish to define actions, which can
|
||||
be attached to toolbar buttons, or called from the command palette. Here is an
|
||||
example of an extension that defines an (not very useful!) action to show an
|
||||
alert, and adds a toolbar button using the full action name:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file my_extension/main.js
|
||||
|
||||
define([
|
||||
'base/js/namespace'
|
||||
], function(
|
||||
Jupyter
|
||||
) {
|
||||
function load_ipython_extension() {
|
||||
|
||||
var handler = function () {
|
||||
alert('this is an alert from my_extension!');
|
||||
};
|
||||
|
||||
var action = {
|
||||
icon: 'fa-comment-o', // a font-awesome class used on buttons, etc
|
||||
help : 'Show an alert',
|
||||
help_index : 'zz',
|
||||
handler : handler
|
||||
};
|
||||
var prefix = 'my_extension';
|
||||
var action_name = 'show-alert';
|
||||
|
||||
var full_action_name = Jupyter.actions.register(action, action_name, prefix); // returns 'my_extension:show-alert'
|
||||
Jupyter.toolbar.add_buttons_group([full_action_name]);
|
||||
}
|
||||
|
||||
return {
|
||||
load_ipython_extension: load_ipython_extension
|
||||
};
|
||||
});
|
||||
|
||||
Every action needs a name, which, when joined with its prefix to make the full
|
||||
action name, should be unique. Built-in actions, like the
|
||||
``jupyter-notebook:restart-kernel`` we bound in the earlier
|
||||
`Modifying key bindings`_ example, use the prefix ``jupyter-notebook``. For
|
||||
actions defined in an extension, it makes sense to use the extension name as
|
||||
the prefix. For the action name, the following guidelines should be considered:
|
||||
|
||||
.. adapted from notebook/static/notebook/js/actions.js
|
||||
|
||||
* First pick a noun and a verb for the action. For example, if the action is
|
||||
"restart kernel," the verb is "restart" and the noun is "kernel".
|
||||
* Omit terms like "selected" and "active" by default, so "delete-cell", rather
|
||||
than "delete-selected-cell". Only provide a scope like "-all-" if it is other
|
||||
than the default "selected" or "active" scope.
|
||||
* If an action has a secondary action, separate the secondary action with
|
||||
"-and-", so "restart-kernel-and-clear-output".
|
||||
* Use above/below or previous/next to indicate spatial and sequential
|
||||
relationships.
|
||||
* Don't ever use before/after as they have a temporal connotation that is
|
||||
confusing when used in a spatial context.
|
||||
* For dialogs, use a verb that indicates what the dialog will accomplish, such
|
||||
as "confirm-restart-kernel".
|
||||
|
||||
|
||||
Installing and enabling extensions
|
||||
----------------------------------
|
||||
|
||||
You can install your nbextension with the command::
|
||||
|
||||
jupyter nbextension install path/to/my_extension/ [--user|--sys-prefix]
|
||||
|
||||
The default installation is system-wide. You can use ``--user`` to do a
|
||||
per-user installation, or ``--sys-prefix`` to install to Python's prefix (e.g.
|
||||
in a virtual or conda environment). Where my_extension is the directory
|
||||
containing the Javascript files. This will copy it to a Jupyter data directory
|
||||
(the exact location is platform dependent - see :ref:`jupyter_path`).
|
||||
|
||||
For development, you can use the ``--symlink`` flag to symlink your extension
|
||||
rather than copying it, so there's no need to reinstall after changes.
|
||||
|
||||
To use your extension, you'll also need to **enable** it, which tells the
|
||||
notebook interface to load it. You can do that with another command::
|
||||
|
||||
jupyter nbextension enable my_extension/main [--sys-prefix][--section='common']
|
||||
|
||||
The argument refers to the Javascript module containing your
|
||||
``load_ipython_extension`` function, which is ``my_extension/main.js`` in this
|
||||
example. The ``--section='common'`` argument will affect all pages, by default
|
||||
it will be loaded on the notebook view only.
|
||||
There is a corresponding ``disable`` command to stop using an
|
||||
extension without uninstalling it.
|
||||
|
||||
.. versionchanged:: 4.2
|
||||
|
||||
Added ``--sys-prefix`` argument
|
||||
|
||||
|
||||
Kernel Specific extensions
|
||||
--------------------------
|
||||
|
||||
.. warning::
|
||||
|
||||
This feature serves as a stopgap for kernel developers who need specific
|
||||
JavaScript injected onto the page. The availability and API are subject to
|
||||
change at anytime.
|
||||
|
||||
|
||||
It is possible to load some JavaScript on the page on a per kernel basis. Be
|
||||
aware that doing so will make the browser page reload without warning as
|
||||
soon as the user switches the kernel without notice.
|
||||
|
||||
If you, a kernel developer, need a particular piece of JavaScript to be loaded
|
||||
on a "per kernel" basis, such as:
|
||||
|
||||
* if you are developing a CodeMirror mode for your language
|
||||
* if you need to enable some specific debugging options
|
||||
|
||||
your ``kernelspecs`` are allowed to contain a ``kernel.js`` file that defines
|
||||
an AMD module. The AMD module should define an `onload` function that will be
|
||||
called when the kernelspec loads, such as:
|
||||
|
||||
* when you load a notebook that uses your kernelspec
|
||||
* change the active kernelspec of a notebook to your kernelspec.
|
||||
|
||||
Note that adding a `kernel.js` to your kernelspec will add an unexpected side
|
||||
effect to changing a kernel in the notebook. As it is impossible to "unload"
|
||||
JavaScript, any attempt to change the kernelspec again will save the current
|
||||
notebook and reload the page without confirmations.
|
||||
|
||||
Here is an example of ``kernel.js``:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
define(function(){
|
||||
return {onload: function(){
|
||||
console.info('Kernel specific javascript loaded');
|
||||
Starting with Notebook v7, front-end extensions for the notebook can be developed
|
||||
as prebuilt JupyterLab extensions.
|
||||
|
||||
// do more things here, like define a codemirror mode
|
||||
This means Notebook v7 is able to reuse many of the existing extensions from the JupyterLab ecosystem as is.
|
||||
|
||||
}}
|
||||
If you would like to develop a prebuilt extension for Notebook v7, check out:
|
||||
|
||||
});
|
||||
- `JupyterLab Extension Tutorial <https://jupyterlab.readthedocs.io/en/latest/extension/extension_tutorial.html>`_: A tutorial to learn how to make a simple JupyterLab extension.
|
||||
- The `JupyterLab Extension Examples Repository <https://github.com/jupyterlab/extension-examples>`_: A short tutorial series to learn how to develop extensions for JupyterLab by example.
|
||||
@ -1,174 +0,0 @@
|
||||
Custom request handlers
|
||||
=======================
|
||||
|
||||
The notebook webserver can be interacted with using a well `defined
|
||||
RESTful
|
||||
API <http://petstore.swagger.io/?url=https://raw.githubusercontent.com/jupyter/notebook/master/notebook/services/api/api.yaml>`__.
|
||||
You can define custom RESTful API handlers in addition to the ones
|
||||
provided by the notebook. As described below, to define a custom handler
|
||||
you need to first write a notebook server extension. Then, in the
|
||||
extension, you can register the custom handler.
|
||||
|
||||
Writing a notebook server extension
|
||||
-----------------------------------
|
||||
|
||||
The notebook webserver is written in Python, hence your server extension
|
||||
should be written in Python too. Server extensions, like IPython
|
||||
extensions, are Python modules that define a specially named load
|
||||
function, ``load_jupyter_server_extension``. This function is called
|
||||
when the extension is loaded.
|
||||
|
||||
.. code:: python
|
||||
|
||||
def load_jupyter_server_extension(nb_server_app):
|
||||
"""
|
||||
Called when the extension is loaded.
|
||||
|
||||
Args:
|
||||
nb_server_app (NotebookWebApplication): handle to the Notebook webserver instance.
|
||||
"""
|
||||
pass
|
||||
|
||||
To get the notebook server to load your custom extension, you'll need to
|
||||
add it to the list of extensions to be loaded. You can do this using the
|
||||
config system. ``NotebookApp.nbserver_extensions`` is a config variable
|
||||
which is a dictionary of strings, each a Python module to be imported, mapping
|
||||
to ``True`` to enable or ``False`` to disable each extension.
|
||||
Because this variable is notebook config, you can set it two different
|
||||
ways, using config files or via the command line.
|
||||
|
||||
For example, to get your extension to load via the command line add a
|
||||
double dash before the variable name, and put the Python dictionary in
|
||||
double quotes. If your package is "mypackage" and module is
|
||||
"mymodule", this would look like
|
||||
``jupyter notebook --NotebookApp.nbserver_extensions="{'mypackage.mymodule':True}"``
|
||||
.
|
||||
Basically the string should be Python importable.
|
||||
|
||||
Alternatively, you can have your extension loaded regardless of the
|
||||
command line args by setting the variable in the Jupyter config file.
|
||||
The default location of the Jupyter config file is
|
||||
``~/.jupyter/jupyter_notebook_config.py`` (see :doc:`/config_overview`). Inside
|
||||
the config file, you can use Python to set the variable. For example,
|
||||
the following config does the same as the previous command line example.
|
||||
|
||||
.. code:: python
|
||||
|
||||
c = get_config()
|
||||
c.NotebookApp.nbserver_extensions = {
|
||||
'mypackage.mymodule': True,
|
||||
}
|
||||
|
||||
Before continuing, it's a good idea to verify that your extension is
|
||||
being loaded. Use a print statement to print something unique. Launch
|
||||
the notebook server and you should see your statement printed to the
|
||||
console.
|
||||
|
||||
Registering custom handlers
|
||||
---------------------------
|
||||
|
||||
Once you've defined a server extension, you can register custom handlers
|
||||
because you have a handle to the Notebook server app instance
|
||||
(``nb_server_app`` above). However, you first need to define your custom
|
||||
handler. To declare a custom handler, inherit from
|
||||
``notebook.base.handlers.IPythonHandler``. The example below[1] is a
|
||||
Hello World handler:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from notebook.base.handlers import IPythonHandler
|
||||
|
||||
class HelloWorldHandler(IPythonHandler):
|
||||
def get(self):
|
||||
self.finish('Hello, world!')
|
||||
|
||||
The Jupyter Notebook server use
|
||||
`Tornado <http://www.tornadoweb.org/en/stable/>`__ as its web framework.
|
||||
For more information on how to implement request handlers, refer to the
|
||||
`Tornado documentation on the
|
||||
matter <http://www.tornadoweb.org/en/stable/web.html#request-handlers>`__.
|
||||
|
||||
After defining the handler, you need to register the handler with the
|
||||
Notebook server. See the following example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
web_app = nb_server_app.web_app
|
||||
host_pattern = '.*$'
|
||||
route_pattern = url_path_join(web_app.settings['base_url'], '/hello')
|
||||
web_app.add_handlers(host_pattern, [(route_pattern, HelloWorldHandler)])
|
||||
|
||||
Putting this together with the extension code, the example looks like the
|
||||
following:
|
||||
|
||||
.. code:: python
|
||||
|
||||
from notebook.utils import url_path_join
|
||||
from notebook.base.handlers import IPythonHandler
|
||||
|
||||
class HelloWorldHandler(IPythonHandler):
|
||||
def get(self):
|
||||
self.finish('Hello, world!')
|
||||
|
||||
def load_jupyter_server_extension(nb_server_app):
|
||||
"""
|
||||
Called when the extension is loaded.
|
||||
|
||||
Args:
|
||||
nb_server_app (NotebookWebApplication): handle to the Notebook webserver instance.
|
||||
"""
|
||||
web_app = nb_server_app.web_app
|
||||
host_pattern = '.*$'
|
||||
route_pattern = url_path_join(web_app.settings['base_url'], '/hello')
|
||||
web_app.add_handlers(host_pattern, [(route_pattern, HelloWorldHandler)])
|
||||
|
||||
|
||||
Extra Parameters and authentication
|
||||
===================================
|
||||
|
||||
Here is a quick rundown of what you need to know to pass extra parameters to the handler and enable authentication:
|
||||
|
||||
- extra arguments to the ``__init__`` constructor are given in a dictionary after the handler class in ``add_handlers``:
|
||||
|
||||
.. code:: python
|
||||
|
||||
|
||||
class HelloWorldHandler(IPythonHandler):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.extra = kwargs.pop('extra')
|
||||
...
|
||||
|
||||
def load_jupyter_server_extension(nb_server_app):
|
||||
|
||||
...
|
||||
|
||||
web_app.add_handlers(host_pattern,
|
||||
[
|
||||
(route_pattern, HelloWorldHandler, {"extra": nb_server_app.extra})
|
||||
])
|
||||
|
||||
|
||||
All handler methods that require authentication _MUST_ be decorated with ``@tornado.web.authenticated``:
|
||||
|
||||
|
||||
.. code:: python
|
||||
|
||||
from tornado import web
|
||||
|
||||
class HelloWorldHandler(IPythonHandler):
|
||||
|
||||
...
|
||||
|
||||
@web.authenticated
|
||||
def get(self, *args, **kwargs):
|
||||
...
|
||||
|
||||
@web.authenticated
|
||||
def post(self, *args, **kwargs):
|
||||
...
|
||||
|
||||
|
||||
References:
|
||||
|
||||
1. `Peter Parente's Mindtrove <https://mindtrove.info/4-ways-to-extend-jupyter-notebook/#nb-server-exts>`__
|
||||
@ -1,91 +0,0 @@
|
||||
Customize keymaps
|
||||
=================
|
||||
|
||||
.. note::
|
||||
|
||||
Declarative Custom Keymaps is a provisional feature with unstable API
|
||||
which is not guaranteed to be kept in future versions of the notebook,
|
||||
and can be removed or changed without warnings.
|
||||
|
||||
The notebook shortcuts that are defined by jupyter both in edit mode and
|
||||
command mode are configurable in the frontend configuration file
|
||||
``~/.jupyter/nbconfig/notebook.json``. The modification of keyboard
|
||||
shortcuts suffers from several limitations, mainly that your Browser and OS
|
||||
might prevent certain shortcuts from working correctly. If this is the case,
|
||||
there is unfortunately not much that can be done. The second issue can arise
|
||||
with keyboards that have a layout different than US English. Again, even if
|
||||
we are aware of the issue, there is not much that can be done.
|
||||
|
||||
Shortcuts are also limited by the underlying library that handles code and
|
||||
text editing: CodeMirror. If some keyboard shortcuts are conflicting, the
|
||||
method described below might not work to create new keyboard shortcuts,
|
||||
especially in the ``edit`` mode of the notebook.
|
||||
|
||||
|
||||
The 4 sections of interest in ``~/.jupyter/nbconfig/notebook.json`` are the
|
||||
following:
|
||||
|
||||
- ``keys.command.unbind``
|
||||
- ``keys.edit.unbind``
|
||||
- ``keys.command.bind``
|
||||
- ``keys.edit.bind``
|
||||
|
||||
The first two sections describe which default keyboard shortcuts not to
|
||||
register at notebook startup time. These are mostly useful if you need to
|
||||
``unbind`` a default keyboard shortcut before binding it to a new
|
||||
``command``.
|
||||
|
||||
The first two sections apply respectively to the ``command`` and ``edit``
|
||||
mode of the notebook. They take a list of shortcuts to ``unbind``.
|
||||
|
||||
For example, to unbind the shortcut to split a cell at the position of the
|
||||
cursor (``Ctrl-Shift-Minus``) use the following:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file ~/.jupyter/nbconfig/notebook.json
|
||||
|
||||
{
|
||||
"keys": {
|
||||
"edit": {
|
||||
"unbind": [
|
||||
"Ctrl-Shift-Minus"
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
The last two sections describe which new keyboard shortcuts to register
|
||||
at notebook startup time and which actions they trigger.
|
||||
|
||||
The last two sections apply respectively to the ``command`` and ``edit``
|
||||
mode of the notebook. They take a dictionary with shortcuts as ``keys`` and
|
||||
``commands`` name as value.
|
||||
|
||||
For example, to bind the shortcut ``G,G,G`` (Press G three time in a row) in
|
||||
command mode to the command that restarts the kernel and runs all cells, use
|
||||
the following:
|
||||
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file ~/.jupyter/nbconfig/notebook.json
|
||||
|
||||
{
|
||||
"keys": {
|
||||
"command": {
|
||||
"bind": {
|
||||
"G,G,G":"jupyter-notebook:restart-kernel-and-run-all-cells"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
The name of the available ``commands`` can be find by hovering over the
|
||||
right end of a row in the command palette.
|
||||
@ -1,87 +0,0 @@
|
||||
File save hooks
|
||||
===============
|
||||
|
||||
You can configure functions that are run whenever a file is saved. There are
|
||||
two hooks available:
|
||||
|
||||
* ``ContentsManager.pre_save_hook`` runs on the API path and model with
|
||||
content. This can be used for things like stripping output that people don't
|
||||
like adding to VCS noise.
|
||||
* ``FileContentsManager.post_save_hook`` runs on the filesystem path and model
|
||||
without content. This could be used to commit changes after every save, for
|
||||
instance.
|
||||
|
||||
They are both called with keyword arguments:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pre_save_hook(model=model, path=path, contents_manager=cm)
|
||||
post_save_hook(model=model, os_path=os_path, contents_manager=cm)
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
These can both be added to :file:`jupyter_notebook_config.py`.
|
||||
|
||||
A pre-save hook for stripping output:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def scrub_output_pre_save(model, **kwargs):
|
||||
"""scrub output before saving notebooks"""
|
||||
# only run on notebooks
|
||||
if model['type'] != 'notebook':
|
||||
return
|
||||
# only run on nbformat v4
|
||||
if model['content']['nbformat'] != 4:
|
||||
return
|
||||
|
||||
for cell in model['content']['cells']:
|
||||
if cell['cell_type'] != 'code':
|
||||
continue
|
||||
cell['outputs'] = []
|
||||
cell['execution_count'] = None
|
||||
|
||||
c.FileContentsManager.pre_save_hook = scrub_output_pre_save
|
||||
|
||||
A post-save hook to make a script equivalent whenever the notebook is saved
|
||||
(replacing the ``--script`` option in older versions of the notebook):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import io
|
||||
import os
|
||||
from notebook.utils import to_api_path
|
||||
|
||||
_script_exporter = None
|
||||
|
||||
def script_post_save(model, os_path, contents_manager, **kwargs):
|
||||
"""convert notebooks to Python script after save with nbconvert
|
||||
|
||||
replaces `jupyter notebook --script`
|
||||
"""
|
||||
from nbconvert.exporters.script import ScriptExporter
|
||||
|
||||
if model['type'] != 'notebook':
|
||||
return
|
||||
|
||||
global _script_exporter
|
||||
|
||||
if _script_exporter is None:
|
||||
_script_exporter = ScriptExporter(parent=contents_manager)
|
||||
|
||||
log = contents_manager.log
|
||||
|
||||
base, ext = os.path.splitext(os_path)
|
||||
script, resources = _script_exporter.from_filename(os_path)
|
||||
script_fname = base + resources.get('output_extension', '.txt')
|
||||
log.info("Saving script /%s", to_api_path(script_fname, contents_manager.root_dir))
|
||||
|
||||
with io.open(script_fname, 'w', encoding='utf-8') as f:
|
||||
f.write(script)
|
||||
|
||||
c.FileContentsManager.post_save_hook = script_post_save
|
||||
|
||||
|
||||
This could be a simple call to ``jupyter nbconvert --to script``, but spawning
|
||||
the subprocess every time is quite slow.
|
||||
@ -1,91 +0,0 @@
|
||||
.. _frontend_config:
|
||||
|
||||
Configuring the notebook frontend
|
||||
=================================
|
||||
|
||||
.. note::
|
||||
|
||||
The ability to configure the notebook frontend UI and preferences is
|
||||
still a work in progress.
|
||||
|
||||
This document is a rough explanation on how you can persist some configuration
|
||||
options for the notebook JavaScript.
|
||||
|
||||
There is no exhaustive list of all the configuration options as most options
|
||||
are passed down to other libraries, which means that non valid
|
||||
configuration can be ignored without any error messages.
|
||||
|
||||
|
||||
How front end configuration works
|
||||
---------------------------------
|
||||
The frontend configuration system works as follows:
|
||||
|
||||
- get a handle of a configurable JavaScript object.
|
||||
- access its configuration attribute.
|
||||
- update its configuration attribute with a JSON patch.
|
||||
|
||||
|
||||
Example - Changing the notebook's default indentation
|
||||
-----------------------------------------------------
|
||||
This example explains how to change the default setting ``indentUnit``
|
||||
for CodeMirror Code Cells::
|
||||
|
||||
var cell = Jupyter.notebook.get_selected_cell();
|
||||
var config = cell.config;
|
||||
var patch = {
|
||||
CodeCell:{
|
||||
cm_config:{indentUnit:2}
|
||||
}
|
||||
}
|
||||
config.update(patch)
|
||||
|
||||
You can enter the previous snippet in your browser's JavaScript console once.
|
||||
Then reload the notebook page in your browser. Now, the preferred indent unit
|
||||
should be equal to two spaces. The custom setting persists and you do not need
|
||||
to reissue the patch on new notebooks.
|
||||
|
||||
``indentUnit``, used in this example, is one of the many `CodeMirror options
|
||||
<https://codemirror.net/doc/manual.html#option_indentUnit>`_ which are available
|
||||
for configuration.
|
||||
|
||||
You can similarly change the options of the file editor by entering the following
|
||||
snippet in the browser's Javascript console once (from a file editing page).::
|
||||
|
||||
var config = Jupyter.editor.config
|
||||
var patch = {
|
||||
Editor: {
|
||||
codemirror_options: {
|
||||
indentUnit: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
config.update(patch)
|
||||
|
||||
Example - Restoring the notebook's default indentation
|
||||
------------------------------------------------------
|
||||
If you want to restore a notebook frontend preference to its default value,
|
||||
you will enter a JSON patch with a ``null`` value for the preference setting.
|
||||
|
||||
For example, let's restore the indent setting ``indentUnit`` to its default of
|
||||
four spaces. Enter the following code snippet in your JavaScript console::
|
||||
|
||||
var cell = Jupyter.notebook.get_selected_cell();
|
||||
var config = cell.config;
|
||||
var patch = {
|
||||
CodeCell:{
|
||||
cm_config:{indentUnit: null} // only change here.
|
||||
}
|
||||
}
|
||||
config.update(patch)
|
||||
|
||||
Reload the notebook in your browser and the default indent should again be two
|
||||
spaces.
|
||||
|
||||
Persisting configuration settings
|
||||
---------------------------------
|
||||
Under the hood, Jupyter will persist the preferred configuration settings in
|
||||
``~/.jupyter/nbconfig/<section>.json``, with ``<section>``
|
||||
taking various value depending on the page where the configuration is issued.
|
||||
``<section>`` can take various values like ``notebook``, ``tree``, and
|
||||
``editor``. A ``common`` section contains configuration settings shared by all
|
||||
pages.
|
||||