Compare commits
86 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
16a058794d | 9 years ago |
|
|
268a79cbc6 | 9 years ago |
|
|
5cc11ec85c | 9 years ago |
|
|
5affc1d4ff | 9 years ago |
|
|
25678af6ac | 9 years ago |
|
|
e0fac939c4 | 10 years ago |
|
|
f3e37754b0 | 10 years ago |
|
|
d7fd3e2803 | 10 years ago |
|
|
eb6526dc39 | 10 years ago |
|
|
4ad7bb3eac | 10 years ago |
|
|
799665e9ca | 10 years ago |
|
|
03b463ffaa | 10 years ago |
|
|
c99f859954 | 10 years ago |
|
|
9136fbd769 | 10 years ago |
|
|
4997a0ee8b | 10 years ago |
|
|
70f9133f4d | 10 years ago |
|
|
56d54dd805 | 10 years ago |
|
|
e145fdce86 | 10 years ago |
|
|
f8ed239874 | 10 years ago |
|
|
2ca689be59 | 10 years ago |
|
|
1a5d8be45f | 10 years ago |
|
|
a6c312923f | 10 years ago |
|
|
0ce9daac27 | 10 years ago |
|
|
e15938d1a6 | 10 years ago |
|
|
557586474c | 10 years ago |
|
|
d9ad88f32e | 10 years ago |
|
|
e075309e7f | 10 years ago |
|
|
783fc14930 | 10 years ago |
|
|
3d39c92b57 | 10 years ago |
|
|
a58b9d736a | 10 years ago |
|
|
7330bd491a | 10 years ago |
|
|
885979d506 | 10 years ago |
|
|
ee44d1947f | 10 years ago |
|
|
cc7e592f48 | 10 years ago |
|
|
a90b4bc54d | 10 years ago |
|
|
ea4a0c7ccc | 10 years ago |
|
|
f10ce5d4ed | 10 years ago |
|
|
c5eb054f4d | 10 years ago |
|
|
ba5e7fe1b2 | 10 years ago |
|
|
925fd7febe | 10 years ago |
|
|
69535708ff | 10 years ago |
|
|
f42030b997 | 10 years ago |
|
|
945dd808a5 | 10 years ago |
|
|
6172f337d2 | 10 years ago |
|
|
8e9fa93492 | 10 years ago |
|
|
9d250230a8 | 10 years ago |
|
|
d542745a68 | 10 years ago |
|
|
46b6829839 | 10 years ago |
|
|
02e8e4377e | 10 years ago |
|
|
efe922cd99 | 10 years ago |
|
|
42132cac86 | 10 years ago |
|
|
5a43bdb685 | 10 years ago |
|
|
6d3731c9b8 | 10 years ago |
|
|
8151f2f35c | 10 years ago |
|
|
ebaa23a027 | 10 years ago |
|
|
6a20fb3bb6 | 10 years ago |
|
|
c73dac23d7 | 10 years ago |
|
|
f6f763ec37 | 10 years ago |
|
|
8d9ebbafe8 | 10 years ago |
|
|
3eaaeb864c | 10 years ago |
|
|
4f7825fe92 | 10 years ago |
|
|
3d218d6ee5 | 10 years ago |
|
|
ae6096e2ae | 10 years ago |
|
|
41ede3ec56 | 10 years ago |
|
|
b3c97cd6a6 | 10 years ago |
|
|
25b0846998 | 10 years ago |
|
|
8120459e4e | 10 years ago |
|
|
fd765a4e8d | 10 years ago |
|
|
89044886e3 | 10 years ago |
|
|
cb92bc323f | 10 years ago |
|
|
a247c6689d | 10 years ago |
|
|
c206169d78 | 10 years ago |
|
|
a79f634716 | 10 years ago |
|
|
f778c9cd33 | 10 years ago |
|
|
d3b9da68b1 | 10 years ago |
|
|
e4f5d040d1 | 10 years ago |
|
|
5fa6c34f40 | 10 years ago |
|
|
8a3b21816d | 10 years ago |
|
|
67e60d954f | 10 years ago |
|
|
8f147f1dfc | 10 years ago |
|
|
205bfcf220 | 10 years ago |
|
|
f365612924 | 10 years ago |
|
|
eb31a3c652 | 10 years ago |
|
|
fb98f7e71f | 10 years ago |
|
|
1faa5ee631 | 10 years ago |
|
|
ac6baa7d22 | 10 years ago |
@ -0,0 +1,281 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Distributing Jupyter Extensions as Python Packages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"### How can the notebook be extended?\n",
|
||||
"The Jupyter Notebook client and server application are both deeply customizable. Their behavior can be extended by creating, respectively:\n",
|
||||
"\n",
|
||||
"- nbextension: a notebook extension\n",
|
||||
" - a single JS file, or directory of JavaScript, Cascading StyleSheets, etc. that contain at\n",
|
||||
" minimum a JavaScript module packaged as an\n",
|
||||
" [AMD modules](https://en.wikipedia.org/wiki/Asynchronous_module_definition)\n",
|
||||
" that exports a function `load_ipython_extension`\n",
|
||||
"- server extension: an importable Python module\n",
|
||||
" - that implements `load_jupyter_server_extension`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Why create a Python package for Jupyter extensions?\n",
|
||||
"Since it is rare to have a server extension that does not have any frontend components (an nbextension), for convenience and consistency, all these client and server extensions with their assets can be packaged and versioned together as a Python package with a few simple commands. This makes installing the package of extensions easier and less error-prone for the user. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Installation of Jupyter Extensions\n",
|
||||
"### Install a Python package containing Jupyter Extensions\n",
|
||||
"There are several ways that you may get a Python package containing Jupyter Extensions. Commonly, you will use a package manager for your system:\n",
|
||||
"```shell\n",
|
||||
"pip install helpful_package\n",
|
||||
"# or\n",
|
||||
"conda install helpful_package\n",
|
||||
"# or\n",
|
||||
"apt-get install helpful_package\n",
|
||||
"\n",
|
||||
"# where 'helpful_package' is a Python package containing one or more Jupyter Extensions\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Enable a Server Extension\n",
|
||||
"\n",
|
||||
"The simplest case would be to enable a server extension which has no frontend components. \n",
|
||||
"\n",
|
||||
"A `pip` user that wants their configuration stored in their home directory would type the following command:\n",
|
||||
"```shell\n",
|
||||
"jupyter serverextension enable --py helpful_package\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"Alternatively, a `virtualenv` or `conda` user can pass `--sys-prefix` which keeps their environment isolated and reproducible. For example:\n",
|
||||
"```shell\n",
|
||||
"# Make sure that your virtualenv or conda environment is activated\n",
|
||||
"[source] activate my-environment\n",
|
||||
"\n",
|
||||
"jupyter serverextension enable --py helpful_package --sys-prefix\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Install the nbextension assets"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"If a package also has an nbextension with frontend assets that must be available (but not neccessarily enabled by default), install these assets with the following command:\n",
|
||||
"```shell\n",
|
||||
"jupyter nbextension install --py helpful_package # or --sys-prefix if using virtualenv or conda\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Enable nbextension assets\n",
|
||||
"If a package has assets that should be loaded every time a Jupyter app (e.g. lab, notebook, dashboard, terminal) is loaded in the browser, the following command can be used to enable the nbextension:\n",
|
||||
"```shell\n",
|
||||
"jupyter nbextension enable --py helpful_package # or --sys-prefix if using virtualenv or conda\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Did it work? Check by listing Jupyter Extensions.\n",
|
||||
"After running one or more extension installation steps, you can list what is presently known about nbextensions or server extension. The following commands will list which extensions are available, whether they are enabled, and other extension details:\n",
|
||||
"\n",
|
||||
"```shell\n",
|
||||
"jupyter nbextension list\n",
|
||||
"jupyter serverextension list\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Additional resources on creating and distributing packages \n",
|
||||
"\n",
|
||||
"> Of course, in addition to the files listed, there are number of other files one needs to build a proper package. Here are some good resources:\n",
|
||||
"- [The Hitchhiker's Guide to Packaging](http://the-hitchhikers-guide-to-packaging.readthedocs.org/en/latest/quickstart.html)\n",
|
||||
"- [Repository Structure and Python](http://www.kennethreitz.org/essays/repository-structure-and-python) by Kenneth Reitz\n",
|
||||
"\n",
|
||||
"> How you distribute them, too, is important:\n",
|
||||
"- [Packaging and Distributing Projects](http://python-packaging-user-guide.readthedocs.org/en/latest/distributing/)\n",
|
||||
"- [conda: Building packages](http://conda.pydata.org/docs/building/build.html)\n",
|
||||
"\n",
|
||||
"> Here are some tools to get you started:\n",
|
||||
"- [generator-nbextension](https://github.com/Anaconda-Server/generator-nbextension)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example - Server extension"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Creating a Python package with a server extension\n",
|
||||
"\n",
|
||||
"Here is an example of a python module which contains a server extension directly on itself. It has this directory structure:\n",
|
||||
"```\n",
|
||||
"- setup.py\n",
|
||||
"- MANIFEST.in\n",
|
||||
"- my_module/\n",
|
||||
" - __init__.py\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Defining the server extension\n",
|
||||
"This example shows that the server extension and its `load_jupyter_server_extension` function are defined in the `__init__.py` file.\n",
|
||||
"\n",
|
||||
"#### `my_module/__init__.py`\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"def _jupyter_server_extension_paths():\n",
|
||||
" return [{\n",
|
||||
" \"module\": \"my_module\"\n",
|
||||
" }]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_jupyter_server_extension(nbapp):\n",
|
||||
" nbapp.log.info(\"my module enabled!\")\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Install and enable the server extension\n",
|
||||
"Which a user can install with:\n",
|
||||
"```\n",
|
||||
"jupyter serverextension enable --py my_module [--sys-prefix]\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Example - Server extension and nbextension"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Creating a Python package with a server extension and nbextension\n",
|
||||
"Here is another server extension, with a front-end module. It assumes this directory structure:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"- setup.py\n",
|
||||
"- MANIFEST.in\n",
|
||||
"- my_fancy_module/\n",
|
||||
" - __init__.py\n",
|
||||
" - static/\n",
|
||||
" index.js\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"collapsed": true
|
||||
},
|
||||
"source": [
|
||||
"### Defining the server extension and nbextension\n",
|
||||
"This example again shows that the server extension and its `load_jupyter_server_extension` function are defined in the `__init__.py` file. This time, there is also a function `_jupyter_nbextension_path` for the nbextension.\n",
|
||||
"\n",
|
||||
"#### `my_fancy_module/__init__.py`\n",
|
||||
"\n",
|
||||
"```python\n",
|
||||
"def _jupyter_server_extension_paths():\n",
|
||||
" return [{\n",
|
||||
" \"module\": \"my_fancy_module\"\n",
|
||||
" }]\n",
|
||||
"\n",
|
||||
"# Jupyter Extension points\n",
|
||||
"def _jupyter_nbextension_paths():\n",
|
||||
" return [dict(\n",
|
||||
" section=\"notebook\",\n",
|
||||
" # the path is relative to the `my_fancy_module` directory\n",
|
||||
" src=\"static\",\n",
|
||||
" # directory in the `nbextension/` namespace\n",
|
||||
" dest=\"my_fancy_module\",\n",
|
||||
" # _also_ in the `nbextension/` namespace\n",
|
||||
" require=\"my_fancy_module/index\")]\n",
|
||||
"\n",
|
||||
"def load_jupyter_server_extension(nbapp):\n",
|
||||
" nbapp.log.info(\"my module enabled!\")\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Install and enable the server extension and nbextension\n",
|
||||
"\n",
|
||||
"The user can install and enable the extensions with the following set of commands:\n",
|
||||
"```\n",
|
||||
"jupyter nbextension install --py my_fancy_module [--sys-prefix|--user]\n",
|
||||
"jupyter nbextension enable --py my_fancy_module [--sys-prefix|--system]\n",
|
||||
"jupyter serverextension enable --py my_fancy_module [--sys-prefix|--system]\n",
|
||||
"```"
|
||||
]
|
||||
}
|
||||
],
|
||||
"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.1"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@ -0,0 +1,245 @@
|
||||
|
||||
`View the original notebook on nbviewer <http://nbviewer.jupyter.org/github/jupyter/notebook/blob/master/docs/source/examples/Notebook/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.ipynb>`__
|
||||
|
||||
Distributing Jupyter Extensions as Python Packages
|
||||
==================================================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
How can the notebook be extended?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Jupyter Notebook client and server application are both deeply
|
||||
customizable. Their behavior can be extended by creating, respectively:
|
||||
|
||||
- nbextension: a notebook extension
|
||||
|
||||
- a single JS file, or directory of JavaScript, Cascading
|
||||
StyleSheets, etc. that contain at minimum a JavaScript module
|
||||
packaged as an `AMD
|
||||
modules <https://en.wikipedia.org/wiki/Asynchronous_module_definition>`__
|
||||
that exports a function ``load_ipython_extension``
|
||||
|
||||
- server extension: an importable Python module
|
||||
|
||||
- that implements ``load_jupyter_server_extension``
|
||||
|
||||
Why create a Python package for Jupyter extensions?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since it is rare to have a server extension that does not have any
|
||||
frontend components (an nbextension), for convenience and consistency,
|
||||
all these client and server extensions with their assets can be packaged
|
||||
and versioned together as a Python package with a few simple commands.
|
||||
This makes installing the package of extensions easier and less
|
||||
error-prone for the user.
|
||||
|
||||
Installation of Jupyter Extensions
|
||||
----------------------------------
|
||||
|
||||
Install a Python package containing Jupyter Extensions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
There are several ways that you may get a Python package containing
|
||||
Jupyter Extensions. Commonly, you will use a package manager for your
|
||||
system:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
pip install helpful_package
|
||||
# or
|
||||
conda install helpful_package
|
||||
# or
|
||||
apt-get install helpful_package
|
||||
|
||||
# where 'helpful_package' is a Python package containing one or more Jupyter Extensions
|
||||
|
||||
Enable a Server Extension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The simplest case would be to enable a server extension which has no
|
||||
frontend components.
|
||||
|
||||
A ``pip`` user that wants their configuration stored in their home
|
||||
directory would type the following command:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
jupyter serverextension enable --py helpful_package
|
||||
|
||||
Alternatively, a ``virtualenv`` or ``conda`` user can pass
|
||||
``--sys-prefix`` which keeps their environment isolated and
|
||||
reproducible. For example:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
# Make sure that your virtualenv or conda environment is activated
|
||||
[source] activate my-environment
|
||||
|
||||
jupyter serverextension enable --py helpful_package --sys-prefix
|
||||
|
||||
Install the nbextension assets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a package also has an nbextension with frontend assets that must be
|
||||
available (but not neccessarily enabled by default), install these
|
||||
assets with the following command:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
jupyter nbextension install --py helpful_package # or --sys-prefix if using virtualenv or conda
|
||||
|
||||
Enable nbextension assets
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If a package has assets that should be loaded every time a Jupyter app
|
||||
(e.g. lab, notebook, dashboard, terminal) is loaded in the browser, the
|
||||
following command can be used to enable the nbextension:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
jupyter nbextension enable --py helpful_package # or --sys-prefix if using virtualenv or conda
|
||||
|
||||
Did it work? Check by listing Jupyter Extensions.
|
||||
-------------------------------------------------
|
||||
|
||||
After running one or more extension installation steps, you can list
|
||||
what is presently known about nbextensions or server extension. The
|
||||
following commands will list which extensions are available, whether
|
||||
they are enabled, and other extension details:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
jupyter nbextension list
|
||||
jupyter serverextension list
|
||||
|
||||
Additional resources on creating and distributing packages
|
||||
----------------------------------------------------------
|
||||
|
||||
Of course, in addition to the files listed, there are number of
|
||||
other files one needs to build a proper package. Here are some good
|
||||
resources: - `The Hitchhiker's Guide to
|
||||
Packaging <http://the-hitchhikers-guide-to-packaging.readthedocs.org/en/latest/quickstart.html>`__
|
||||
- `Repository Structure and
|
||||
Python <http://www.kennethreitz.org/essays/repository-structure-and-python>`__
|
||||
by Kenneth Reitz
|
||||
|
||||
How you distribute them, too, is important: - `Packaging and
|
||||
Distributing
|
||||
Projects <http://python-packaging-user-guide.readthedocs.org/en/latest/distributing/>`__
|
||||
- `conda: Building
|
||||
packages <http://conda.pydata.org/docs/building/build.html>`__
|
||||
|
||||
Here are some tools to get you started: -
|
||||
`generator-nbextension <https://github.com/Anaconda-Server/generator-nbextension>`__
|
||||
|
||||
Example - Server extension
|
||||
--------------------------
|
||||
|
||||
Creating a Python package with a server extension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is an example of a python module which contains a server extension
|
||||
directly on itself. It has this directory structure:
|
||||
|
||||
::
|
||||
|
||||
- setup.py
|
||||
- MANIFEST.in
|
||||
- my_module/
|
||||
- __init__.py
|
||||
|
||||
Defining the server extension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This example shows that the server extension and its
|
||||
``load_jupyter_server_extension`` function are defined in the
|
||||
``__init__.py`` file.
|
||||
|
||||
``my_module/__init__.py``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code:: python
|
||||
|
||||
def _jupyter_server_extension_paths():
|
||||
return [{
|
||||
"module": "my_module"
|
||||
}]
|
||||
|
||||
|
||||
def load_jupyter_server_extension(nbapp):
|
||||
nbapp.log.info("my module enabled!")
|
||||
|
||||
Install and enable the server extension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Which a user can install with:
|
||||
|
||||
::
|
||||
|
||||
jupyter serverextension enable --py my_module [--sys-prefix]
|
||||
|
||||
Example - Server extension and nbextension
|
||||
------------------------------------------
|
||||
|
||||
Creating a Python package with a server extension and nbextension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is another server extension, with a front-end module. It assumes
|
||||
this directory structure:
|
||||
|
||||
::
|
||||
|
||||
- setup.py
|
||||
- MANIFEST.in
|
||||
- my_fancy_module/
|
||||
- __init__.py
|
||||
- static/
|
||||
index.js
|
||||
|
||||
Defining the server extension and nbextension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This example again shows that the server extension and its
|
||||
``load_jupyter_server_extension`` function are defined in the
|
||||
``__init__.py`` file. This time, there is also a function
|
||||
``_jupyter_nbextension_path`` for the nbextension.
|
||||
|
||||
``my_fancy_module/__init__.py``
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code:: python
|
||||
|
||||
def _jupyter_server_extension_paths():
|
||||
return [{
|
||||
"module": "my_fancy_module"
|
||||
}]
|
||||
|
||||
# Jupyter Extension points
|
||||
def _jupyter_nbextension_paths():
|
||||
return [dict(
|
||||
section="notebook",
|
||||
# the path is relative to the `my_fancy_module` directory
|
||||
src="static",
|
||||
# directory in the `nbextension/` namespace
|
||||
dest="my_fancy_module",
|
||||
# _also_ in the `nbextension/` namespace
|
||||
require="my_fancy_module/index")]
|
||||
|
||||
def load_jupyter_server_extension(nbapp):
|
||||
nbapp.log.info("my module enabled!")
|
||||
|
||||
Install and enable the server extension and nbextension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The user can install and enable the extensions with the following set of
|
||||
commands:
|
||||
|
||||
::
|
||||
|
||||
jupyter nbextension install --py my_fancy_module [--sys-prefix|--user]
|
||||
jupyter nbextension enable --py my_fancy_module [--sys-prefix|--system]
|
||||
jupyter serverextension enable --py my_fancy_module [--sys-prefix|--system]
|
||||
|
||||
`View the original notebook on nbviewer <http://nbviewer.jupyter.org/github/jupyter/notebook/blob/master/docs/source/examples/Notebook/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.ipynb>`__
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,341 @@
|
||||
# coding: utf-8
|
||||
"""Utilities for installing server extensions for the notebook"""
|
||||
|
||||
# Copyright (c) Jupyter Development Team.
|
||||
# Distributed under the terms of the Modified BSD License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
|
||||
from jupyter_core.paths import jupyter_config_path
|
||||
from ._version import __version__
|
||||
from .nbextensions import (
|
||||
JupyterApp, BaseNBExtensionApp, _get_config_dir,
|
||||
GREEN_ENABLED, RED_DISABLED,
|
||||
GREEN_OK, RED_X,
|
||||
)
|
||||
|
||||
from traitlets import Bool
|
||||
from traitlets.utils.importstring import import_item
|
||||
from traitlets.config.manager import BaseJSONConfigManager
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public API
|
||||
# ------------------------------------------------------------------------------
|
||||
class ArgumentConflict(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def toggle_serverextension_python(import_name, enabled=None, parent=None,
|
||||
user=True, sys_prefix=False, logger=None):
|
||||
"""Toggle a server extension.
|
||||
|
||||
By default, toggles the extension in the system-wide Jupyter configuration
|
||||
location (e.g. /usr/local/etc/jupyter).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
import_name : str
|
||||
Importable Python module (dotted-notation) exposing the magic-named
|
||||
`load_jupyter_server_extension` function
|
||||
enabled : bool [default: None]
|
||||
Toggle state for the extension. Set to None to toggle, True to enable,
|
||||
and False to disable the extension.
|
||||
parent : Configurable [default: None]
|
||||
user : bool [default: True]
|
||||
Toggle in the user's configuration location (e.g. ~/.jupyter).
|
||||
sys_prefix : bool [default: False]
|
||||
Toggle in the current Python environment's configuration location
|
||||
(e.g. ~/.envs/my-env/etc/jupyter). Will override `user`.
|
||||
logger : Jupyter logger [optional]
|
||||
Logger instance to use
|
||||
"""
|
||||
user = False if sys_prefix else user
|
||||
config_dir = _get_config_dir(user=user, sys_prefix=sys_prefix)
|
||||
cm = BaseJSONConfigManager(parent=parent, config_dir=config_dir)
|
||||
cfg = cm.get("jupyter_notebook_config")
|
||||
server_extensions = (
|
||||
cfg.setdefault("NotebookApp", {})
|
||||
.setdefault("nbserver_extensions", {})
|
||||
)
|
||||
|
||||
old_enabled = server_extensions.get(import_name, None)
|
||||
new_enabled = enabled if enabled is not None else not old_enabled
|
||||
|
||||
if logger:
|
||||
if new_enabled:
|
||||
logger.info(u"Enabling: %s" % (import_name))
|
||||
else:
|
||||
logger.info(u"Disabling: %s" % (import_name))
|
||||
|
||||
server_extensions[import_name] = new_enabled
|
||||
|
||||
if logger:
|
||||
logger.info(u"- Writing config: {}".format(config_dir))
|
||||
|
||||
cm.update("jupyter_notebook_config", cfg)
|
||||
|
||||
if new_enabled:
|
||||
validate_serverextension(import_name, logger)
|
||||
|
||||
|
||||
def validate_serverextension(import_name, logger=None):
|
||||
"""Assess the health of an installed server extension
|
||||
|
||||
Returns a list of validation warnings.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
import_name : str
|
||||
Importable Python module (dotted-notation) exposing the magic-named
|
||||
`load_jupyter_server_extension` function
|
||||
logger : Jupyter logger [optional]
|
||||
Logger instance to use
|
||||
"""
|
||||
|
||||
warnings = []
|
||||
infos = []
|
||||
|
||||
func = None
|
||||
|
||||
if logger:
|
||||
logger.info(" - Validating...")
|
||||
|
||||
try:
|
||||
mod = importlib.import_module(import_name)
|
||||
func = getattr(mod, 'load_jupyter_server_extension', None)
|
||||
except Exception:
|
||||
logger.warning("Error loading server extension %s", import_name)
|
||||
|
||||
import_msg = u" {} is {} importable?"
|
||||
if func is not None:
|
||||
infos.append(import_msg.format(GREEN_OK, import_name))
|
||||
else:
|
||||
warnings.append(import_msg.format(RED_X, import_name))
|
||||
|
||||
post_mortem = u" {} {} {}"
|
||||
if logger:
|
||||
if warnings:
|
||||
[logger.info(info) for info in infos]
|
||||
[logger.warn(warning) for warning in warnings]
|
||||
else:
|
||||
logger.info(post_mortem.format(import_name, "", GREEN_OK))
|
||||
|
||||
return warnings
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Applications
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
flags = {}
|
||||
flags.update(JupyterApp.flags)
|
||||
flags.pop("y", None)
|
||||
flags.pop("generate-config", None)
|
||||
flags.update({
|
||||
"user" : ({
|
||||
"ToggleServerExtensionApp" : {
|
||||
"user" : True,
|
||||
}}, "Perform the operation for the current user"
|
||||
),
|
||||
"system" : ({
|
||||
"ToggleServerExtensionApp" : {
|
||||
"user" : False,
|
||||
"sys_prefix": False,
|
||||
}}, "Perform the operation system-wide"
|
||||
),
|
||||
"sys-prefix" : ({
|
||||
"ToggleServerExtensionApp" : {
|
||||
"sys_prefix" : True,
|
||||
}}, "Use sys.prefix as the prefix for installing server extensions"
|
||||
),
|
||||
"py" : ({
|
||||
"ToggleServerExtensionApp" : {
|
||||
"python" : True,
|
||||
}}, "Install from a Python package"
|
||||
),
|
||||
})
|
||||
flags['python'] = flags['py']
|
||||
|
||||
|
||||
class ToggleServerExtensionApp(BaseNBExtensionApp):
|
||||
"""A base class for enabling/disabling extensions"""
|
||||
name = "jupyter serverextension enable/disable"
|
||||
description = "Enable/disable a server extension using frontend configuration files."
|
||||
|
||||
aliases = {}
|
||||
flags = flags
|
||||
|
||||
user = Bool(True, config=True, help="Whether to do a user install")
|
||||
sys_prefix = Bool(False, config=True, help="Use the sys.prefix as the prefix")
|
||||
python = Bool(False, config=True, help="Install from a Python package")
|
||||
|
||||
def toggle_server_extension(self, import_name):
|
||||
"""Change the status of a named server extension.
|
||||
|
||||
Uses the value of `self._toggle_value`.
|
||||
|
||||
Parameters
|
||||
---------
|
||||
|
||||
import_name : str
|
||||
Importable Python module (dotted-notation) exposing the magic-named
|
||||
`load_jupyter_server_extension` function
|
||||
"""
|
||||
toggle_serverextension_python(
|
||||
import_name, self._toggle_value, parent=self, user=self.user,
|
||||
sys_prefix=self.sys_prefix, logger=self.log)
|
||||
|
||||
def toggle_server_extension_python(self, package):
|
||||
"""Change the status of some server extensions in a Python package.
|
||||
|
||||
Uses the value of `self._toggle_value`.
|
||||
|
||||
Parameters
|
||||
---------
|
||||
|
||||
package : str
|
||||
Importable Python module exposing the
|
||||
magic-named `_jupyter_server_extension_paths` function
|
||||
"""
|
||||
m, server_exts = _get_server_extension_metadata(package)
|
||||
for server_ext in server_exts:
|
||||
module = server_ext['module']
|
||||
self.toggle_server_extension(module)
|
||||
|
||||
def start(self):
|
||||
"""Perform the App's actions as configured"""
|
||||
if not self.extra_args:
|
||||
sys.exit('Please specify a server extension/package to enable or disable')
|
||||
for arg in self.extra_args:
|
||||
if self.python:
|
||||
self.toggle_server_extension_python(arg)
|
||||
else:
|
||||
self.toggle_server_extension(arg)
|
||||
|
||||
|
||||
class EnableServerExtensionApp(ToggleServerExtensionApp):
|
||||
"""An App that enables (and validates) Server Extensions"""
|
||||
name = "jupyter serverextension enable"
|
||||
description = """
|
||||
Enable a serverextension in configuration.
|
||||
|
||||
Usage
|
||||
jupyter serverextension enable [--system|--sys-prefix]
|
||||
"""
|
||||
_toggle_value = True
|
||||
|
||||
|
||||
class DisableServerExtensionApp(ToggleServerExtensionApp):
|
||||
"""An App that disables Server Extensions"""
|
||||
name = "jupyter serverextension disable"
|
||||
description = """
|
||||
Disable a serverextension in configuration.
|
||||
|
||||
Usage
|
||||
jupyter serverextension disable [--system|--sys-prefix]
|
||||
"""
|
||||
_toggle_value = False
|
||||
|
||||
|
||||
class ListServerExtensionsApp(BaseNBExtensionApp):
|
||||
"""An App that lists (and validates) Server Extensions"""
|
||||
name = "jupyter serverextension list"
|
||||
version = __version__
|
||||
description = "List all server extensions known by the configuration system"
|
||||
|
||||
def list_server_extensions(self):
|
||||
"""List all enabled and disabled server extensions, by config path
|
||||
|
||||
Enabled extensions are validated, potentially generating warnings.
|
||||
"""
|
||||
config_dirs = jupyter_config_path()
|
||||
for config_dir in config_dirs:
|
||||
cm = BaseJSONConfigManager(parent=self, config_dir=config_dir)
|
||||
data = cm.get("jupyter_notebook_config")
|
||||
server_extensions = (
|
||||
data.setdefault("NotebookApp", {})
|
||||
.setdefault("nbserver_extensions", {})
|
||||
)
|
||||
if server_extensions:
|
||||
print(u'config dir: {}'.format(config_dir))
|
||||
for import_name, enabled in server_extensions.items():
|
||||
print(u' {} {}'.format(
|
||||
import_name,
|
||||
GREEN_ENABLED if enabled else RED_DISABLED))
|
||||
validate_serverextension(import_name, self.log)
|
||||
|
||||
def start(self):
|
||||
"""Perform the App's actions as configured"""
|
||||
self.list_server_extensions()
|
||||
|
||||
|
||||
_examples = """
|
||||
jupyter serverextension list # list all configured server extensions
|
||||
jupyter serverextension enable --py <packagename> # enable all server extensions in a Python package
|
||||
jupyter serverextension disable --py <packagename> # disable all server extensions in a Python package
|
||||
"""
|
||||
|
||||
|
||||
class ServerExtensionApp(BaseNBExtensionApp):
|
||||
"""Root level server extension app"""
|
||||
name = "jupyter serverextension"
|
||||
version = __version__
|
||||
description = "Work with Jupyter server extensions"
|
||||
examples = _examples
|
||||
|
||||
subcommands = dict(
|
||||
enable=(EnableServerExtensionApp, "Enable an server extension"),
|
||||
disable=(DisableServerExtensionApp, "Disable an server extension"),
|
||||
list=(ListServerExtensionsApp, "List server extensions")
|
||||
)
|
||||
|
||||
def start(self):
|
||||
"""Perform the App's actions as configured"""
|
||||
super(ServerExtensionApp, self).start()
|
||||
|
||||
# The above should have called a subcommand and raised NoStart; if we
|
||||
# get here, it didn't, so we should self.log.info a message.
|
||||
subcmds = ", ".join(sorted(self.subcommands))
|
||||
sys.exit("Please supply at least one subcommand: %s" % subcmds)
|
||||
|
||||
|
||||
main = ServerExtensionApp.launch_instance
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private API
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _get_server_extension_metadata(module):
|
||||
"""Load server extension metadata from a module.
|
||||
|
||||
Returns a tuple of (
|
||||
the package as loaded
|
||||
a list of server extension specs: [
|
||||
{
|
||||
"module": "mockextension"
|
||||
}
|
||||
]
|
||||
)
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
module : str
|
||||
Importable Python module exposing the
|
||||
magic-named `_jupyter_server_extension_paths` function
|
||||
"""
|
||||
m = import_item(module)
|
||||
if not hasattr(m, '_jupyter_server_extension_paths'):
|
||||
raise KeyError(u'The Python module {} does not include any valid server extensions'.format(module))
|
||||
return m, m._jupyter_server_extension_paths()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@ -0,0 +1 @@
|
||||
console.log('z');
|
||||
@ -0,0 +1,56 @@
|
||||
//
|
||||
// Test buffering for execution requests.
|
||||
//
|
||||
casper.notebook_test(function () {
|
||||
this.then(function() {
|
||||
// make sure there are at least three cells for the tests below.
|
||||
this.append_cell();
|
||||
this.append_cell();
|
||||
this.append_cell();
|
||||
})
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
IPython.notebook.kernel.stop_channels();
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
cell.set_text('a=10; print(a)');
|
||||
IPython.notebook.execute_cells([0]);
|
||||
IPython.notebook.kernel.reconnect(1);
|
||||
});
|
||||
|
||||
this.wait_for_output(0);
|
||||
|
||||
this.then(function () {
|
||||
var result = this.get_output_cell(0);
|
||||
this.test.assertEquals(result.text, '10\n', 'kernels buffer messages if connection is down');
|
||||
});
|
||||
|
||||
this.thenEvaluate(function () {
|
||||
var cell = IPython.notebook.get_cell(0);
|
||||
var cellplus = IPython.notebook.get_cell(1);
|
||||
var cellprint = IPython.notebook.get_cell(2);
|
||||
cell.set_text('k=1');
|
||||
cellplus.set_text('k+=1');
|
||||
cellprint.set_text('k*=2')
|
||||
|
||||
IPython.notebook.kernel.stop_channels();
|
||||
|
||||
// Repeated execution of cell queued up in the kernel executes
|
||||
// each execution request in order.
|
||||
IPython.notebook.execute_cells([0]);
|
||||
IPython.notebook.execute_cells([2]);
|
||||
IPython.notebook.execute_cells([1]);
|
||||
IPython.notebook.execute_cells([1]);
|
||||
IPython.notebook.execute_cells([1]);
|
||||
cellprint.set_text('print(k)')
|
||||
IPython.notebook.execute_cells([2]);
|
||||
|
||||
IPython.notebook.kernel.reconnect(1);
|
||||
});
|
||||
|
||||
this.wait_for_output(2);
|
||||
|
||||
this.then(function () {
|
||||
var result = this.get_output_cell(2);
|
||||
this.test.assertEquals(result.text, '5\n', 'kernels send buffered messages in order');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,89 @@
|
||||
import os
|
||||
from unittest import TestCase
|
||||
try:
|
||||
from unittest.mock import patch
|
||||
except ImportError:
|
||||
from mock import patch # py2
|
||||
|
||||
from ipython_genutils.tempdir import TemporaryDirectory
|
||||
from ipython_genutils import py3compat
|
||||
|
||||
from traitlets.config.manager import BaseJSONConfigManager
|
||||
from traitlets.tests.utils import check_help_all_output
|
||||
|
||||
from notebook.serverextensions import toggle_serverextension_python
|
||||
from notebook import nbextensions
|
||||
from notebook.nbextensions import _get_config_dir
|
||||
|
||||
|
||||
def test_help_output():
|
||||
check_help_all_output('notebook.serverextensions')
|
||||
check_help_all_output('notebook.serverextensions', ['enable'])
|
||||
check_help_all_output('notebook.serverextensions', ['disable'])
|
||||
check_help_all_output('notebook.serverextensions', ['install'])
|
||||
check_help_all_output('notebook.serverextensions', ['uninstall'])
|
||||
|
||||
|
||||
class TestInstallServerExtension(TestCase):
|
||||
|
||||
def tempdir(self):
|
||||
td = TemporaryDirectory()
|
||||
self.tempdirs.append(td)
|
||||
return py3compat.cast_unicode(td.name)
|
||||
|
||||
def setUp(self):
|
||||
self.tempdirs = []
|
||||
|
||||
self.test_dir = self.tempdir()
|
||||
self.data_dir = os.path.join(self.test_dir, 'data')
|
||||
self.config_dir = os.path.join(self.test_dir, 'config')
|
||||
self.system_data_dir = os.path.join(self.test_dir, 'system_data')
|
||||
self.system_path = [self.system_data_dir]
|
||||
|
||||
self.patch_env = patch.dict('os.environ', {
|
||||
'JUPYTER_CONFIG_DIR': self.config_dir,
|
||||
'JUPYTER_DATA_DIR': self.data_dir,
|
||||
})
|
||||
self.patch_env.start()
|
||||
self.patch_system_path = patch.object(nbextensions,
|
||||
'SYSTEM_JUPYTER_PATH', self.system_path)
|
||||
self.patch_system_path.start()
|
||||
|
||||
def tearDown(self):
|
||||
self.patch_env.stop()
|
||||
self.patch_system_path.stop()
|
||||
|
||||
def _inject_mock_extension(self):
|
||||
outer_file = __file__
|
||||
|
||||
class mock():
|
||||
__file__ = outer_file
|
||||
|
||||
@staticmethod
|
||||
def _jupyter_server_extension_paths():
|
||||
return [{
|
||||
'module': '_mockdestination/index'
|
||||
}]
|
||||
|
||||
import sys
|
||||
sys.modules['mockextension'] = mock
|
||||
|
||||
def _get_config(self, user=True):
|
||||
cm = BaseJSONConfigManager(config_dir=_get_config_dir(user))
|
||||
data = cm.get("jupyter_notebook_config")
|
||||
return data.get("NotebookApp", {}).get("nbserver_extensions", {})
|
||||
|
||||
def test_enable(self):
|
||||
self._inject_mock_extension()
|
||||
toggle_serverextension_python('mockextension', True)
|
||||
|
||||
config = self._get_config()
|
||||
assert config['mockextension']
|
||||
|
||||
def test_disable(self):
|
||||
self._inject_mock_extension()
|
||||
toggle_serverextension_python('mockextension', True)
|
||||
toggle_serverextension_python('mockextension', False)
|
||||
|
||||
config = self._get_config()
|
||||
assert not config['mockextension']
|
||||
@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from notebook.serverextensions import main
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Reference in new issue