mirror of
https://github.com/jupyter/notebook.git
synced 2024-12-03 03:41:14 +08:00
New bundler extensions developer doc
* Improve some docstrings too * Fix a busted extension link along the way (c) Copyright IBM Corp. 2016
This commit is contained in:
parent
c82cce050b
commit
4a3386663c
141
docs/source/extending/bundler_extensions.rst
Normal file
141
docs/source/extending/bundler_extensions.rst
Normal file
@ -0,0 +1,141 @@
|
||||
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-redable 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 simplisitic in the name of brevity. For a more meaningful example, see the source in `notebook/bundler/zip_bundler.py`. It parses Markdown cells in the active notebook for gitignore-syntax file references, and bundles those files alongside the notebook in a zip download. You can enable it like so:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension enable --py notebook.bundler.zip_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
|
@ -124,4 +124,4 @@ following:
|
||||
|
||||
References:
|
||||
1. `Peter Parente's
|
||||
Mindtrove <http://mindtrove.info/#nb-server-exts>`__
|
||||
Mindtrove <http://mindtrove.info/4-ways-to-extend-jupyter-notebook/#nb-server-exts>`__
|
||||
|
@ -14,3 +14,4 @@ override the notebook's defaults with your own custom behavior.
|
||||
handlers
|
||||
frontend_extensions
|
||||
keymaps
|
||||
bundler_extensions
|
||||
|
@ -23,10 +23,10 @@ def bundle(handler, model):
|
||||
|
||||
Parameters
|
||||
----------
|
||||
handler : tornado.web.Handler
|
||||
handler : tornado.web.RequestHandler
|
||||
Handler that serviced the bundle request
|
||||
model : dict
|
||||
Notebook model from a ContentManager
|
||||
Notebook model from the configured ContentManager
|
||||
"""
|
||||
abs_nb_path = os.path.join(handler.settings['contents_manager'].root_dir,
|
||||
model['path'])
|
||||
|
Loading…
Reference in New Issue
Block a user