Cleanup Configuration section in the docs

This commit is contained in:
Jeremy Tuloup 2022-03-04 16:44:44 +01:00
parent f487ec8c7d
commit 58d701a383
11 changed files with 22 additions and 1673 deletions

View File

@ -8,8 +8,7 @@ options to suit your workflow. Here are areas that are commonly configured
when using Jupyter Notebook:
- :ref:`Jupyter's common configuration system <configure_common>`
- :ref:`Notebook server <configure_nbserver>`
- :ref:`Notebook front-end client <configure_nbclient>`
- :ref:`Jupyter Server <configure_jupyter_server>`
- :ref:`Notebook extensions <configure_nbextensions>`
Let's look at highlights of each area.
@ -28,48 +27,37 @@ and editing settings is similar for all the Jupyter applications.
- `traitlets <https://traitlets.readthedocs.io/en/latest/config.html#module-traitlets.config>`_
provide a low-level architecture for configuration.
.. _configure_nbserver:
.. _configure_jupyter_server:
Notebook server
Jupyter server
---------------
The Notebook server runs the language kernel and communicates with the
The Jupyter Server runs the language kernel and communicates with the
front-end Notebook client (i.e. the familiar notebook interface).
- Configuring the Notebook server
- Configuring the Jupyter Server
To create a ``jupyter_notebook_config.py`` file in the ``.jupyter``
To create a ``jupyter_server_config.py`` file in the ``.jupyter``
directory, with all the defaults commented out, use the following
command::
$ jupyter notebook --generate-config
$ jupyter server --generate-config
:ref:`Command line arguments for configuration <config>` settings are
documented in the configuration file and the user documentation.
- :ref:`Running a Notebook server <working_remotely>`
- `Running a Jupyter Server <https://jupyter-server.readthedocs.io/en/stable/operators/public-server.html>`_
- Related: `Configuring a language kernel <https://ipython.readthedocs.io/en/latest/install/kernel_install.html>`_
to run in the Notebook server enables your server to run other languages, like R or Julia.
.. _configure_nbclient:
Notebook front-end client
-------------------------
.. toctree::
:maxdepth: 2
frontend_config
to run in the Jupyter Server enables your server to run other languages, like R or Julia.
.. _configure_nbextensions:
Notebook extensions
-------------------
- `Distributing Jupyter Extensions as Python Packages <https://jupyter-notebook.readthedocs.io/en/latest/examples/Notebook/Distributing%20Jupyter%20Extensions%20as%20Python%20Packages.html#Distributing-Jupyter-Extensions-as-Python-Packages>`_
- `Extending the Notebook <https://jupyter-notebook.readthedocs.io/en/latest/extending/index.html>`_
The Notebook frontend can be extending with JupyterLab extensions.
:ref:`Security in Jupyter notebooks: <notebook_security>` Since security
policies vary from organization to organization, we encourage you to
See the :ref:`Frontend Extension Guide <frontend_extensions>` for more information.
`Security in Jupyter notebooks: <https://jupyter-server.readthedocs.io/en/stable/operators/security.html>`_
Since security policies vary from organization to organization, we encourage you to
consult with your security team on settings that would be best for your use
cases. Our documentation offers some responsible security practices, and we
recommend becoming familiar with the practices.

View File

@ -8,8 +8,5 @@ Configuration
config_overview
config
public_server
security
frontend_config
examples/Notebook/Distributing Jupyter Extensions as Python Packages
Security <https://jupyter-server.readthedocs.io/en/stable/operators/security.html>
extending/index.rst

View File

@ -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

View File

@ -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/

View File

@ -1,280 +1,11 @@
.. _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');
// do more things here, like define a codemirror mode
}}
});
TODO: link to the JupyterLab documentation

View File

@ -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>`__

View File

@ -9,9 +9,5 @@ override the notebook's defaults with your own custom behavior.
.. toctree::
:maxdepth: 2
contents
savehooks
handlers
Extending the Jupyter Server <https://jupyter-server.readthedocs.io/en/stable/developers/index.html>
frontend_extensions
keymaps
bundler_extensions

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -1,444 +0,0 @@
.. _working_remotely:
Running a notebook server
=========================
The :doc:`Jupyter notebook <notebook>` web application is based on a
server-client structure. The notebook server uses a :ref:`two-process kernel
architecture <ipython:ipythonzmq>` based on ZeroMQ_, as well as Tornado_ for
serving HTTP requests.
.. note::
By default, a notebook server runs locally at 127.0.0.1:8888
and is accessible only from `localhost`. You may access the
notebook server from the browser using `http://127.0.0.1:8888`.
This document describes how you can
:ref:`secure a notebook server <notebook_server_security>` and how to
:ref:`run it on a public interface <notebook_public_server>`.
.. important::
**This is not the multi-user server you are looking for**. This document
describes how you can run a public server with a single user. This should
only be done by someone who wants remote access to their personal machine.
Even so, doing this requires a thorough understanding of the set-ups
limitations and security implications. If you allow multiple users to
access a notebook server as it is described in this document, their
commands may collide, clobber and overwrite each other.
If you want a multi-user server, the official solution is JupyterHub_.
To use JupyterHub, you need a Unix server (typically Linux) running
somewhere that is accessible to your users on a network. This may run over
the public internet, but doing so introduces additional
`security concerns <https://jupyterhub.readthedocs.io/en/latest/getting-started/security-basics.html>`_.
.. _ZeroMQ: http://zeromq.org
.. _Tornado: http://www.tornadoweb.org
.. _JupyterHub: https://jupyterhub.readthedocs.io/en/latest/
.. _notebook_server_security:
Securing a notebook server
--------------------------
You can protect your notebook server with a simple single password. As of notebook
5.0 this can be done automatically. To set up a password manually you can configure the
:attr:`NotebookApp.password` setting in :file:`jupyter_notebook_config.py`.
Prerequisite: A notebook configuration file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Check to see if you have a notebook configuration file,
:file:`jupyter_notebook_config.py`. The default location for this file
is your Jupyter folder located in your home directory:
- Windows: :file:`C:\\Users\\USERNAME\\.jupyter\\jupyter_notebook_config.py`
- OS X: :file:`/Users/USERNAME/.jupyter/jupyter_notebook_config.py`
- Linux: :file:`/home/USERNAME/.jupyter/jupyter_notebook_config.py`
If you don't already have a Jupyter folder, or if your Jupyter folder doesn't contain
a notebook configuration file, run the following command::
$ jupyter notebook --generate-config
This command will create the Jupyter folder if necessary, and create notebook
configuration file, :file:`jupyter_notebook_config.py`, in this folder.
Automatic Password setup
~~~~~~~~~~~~~~~~~~~~~~~~
As of notebook 5.3, the first time you log-in using a token, the notebook server
should give you the opportunity to setup a password from the user interface.
You will be presented with a form asking for the current _token_, as well as
your _new_ _password_ ; enter both and click on ``Login and setup new password``.
Next time you need to log in you'll be able to use the new password instead of
the login token, otherwise follow the procedure to set a password from the
command line.
The ability to change the password at first login time may be disabled by
integrations by setting the ``--NotebookApp.allow_password_change=False``
Starting at notebook version 5.0, you can enter and store a password for your
notebook server with a single command. :command:`jupyter notebook password` will
prompt you for your password and record the hashed password in your
:file:`jupyter_notebook_config.json`.
.. code-block:: bash
$ jupyter notebook password
Enter password: ****
Verify password: ****
[NotebookPasswordApp] Wrote hashed password to /Users/you/.jupyter/jupyter_notebook_config.json
This can be used to reset a lost password; or if you believe your credentials
have been leaked and desire to change your password. Changing your password will
invalidate all logged-in sessions after a server restart.
.. _hashed-pw:
Preparing a hashed password
~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can prepare a hashed password manually, using the function
:func:`notebook.auth.security.passwd`:
.. code-block:: ipython
In [1]: from notebook.auth import passwd
In [2]: passwd()
Enter password:
Verify password:
Out[2]: 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
.. caution::
:func:`~notebook.auth.security.passwd` when called with no arguments
will prompt you to enter and verify your password such as
in the above code snippet. Although the function can also
be passed a string as an argument such as ``passwd('mypassword')``, please
**do not** pass a string as an argument inside an IPython session, as it
will be saved in your input history.
Adding hashed password to your notebook configuration file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can then add the hashed password to your
:file:`jupyter_notebook_config.py`. The default location for this file
:file:`jupyter_notebook_config.py` is in your Jupyter folder in your home
directory, ``~/.jupyter``, e.g.::
c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
Automatic password setup will store the hash in ``jupyter_notebook_config.json``
while this method stores the hash in ``jupyter_notebook_config.py``. The ``.json``
configuration options take precedence over the ``.py`` one, thus the manual
password may not take effect if the Json file has a password set.
Using SSL for encrypted communication
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When using a password, it is a good idea to also use SSL with a web
certificate, so that your hashed password is not sent unencrypted by your
browser.
.. important::
Web security is rapidly changing and evolving. We provide this document
as a convenience to the user, and recommend that the user keep current on
changes that may impact security, such as new releases of OpenSSL.
The Open Web Application Security Project (`OWASP`_) website is a good resource
on general security issues and web practices.
You can start the notebook to communicate via a secure protocol mode by setting
the ``certfile`` option to your self-signed certificate, i.e. ``mycert.pem``,
with the command::
$ jupyter notebook --certfile=mycert.pem --keyfile mykey.key
.. tip::
A self-signed certificate can be generated with ``openssl``. For example,
the following command will create a certificate valid for 365 days with
both the key and certificate data written to the same file::
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout mykey.key -out mycert.pem
When starting the notebook server, your browser may warn that your self-signed
certificate is insecure or unrecognized. If you wish to have a fully
compliant self-signed certificate that will not raise warnings, it is possible
(but rather involved) to create one, as explained in detail in this
`tutorial`_. Alternatively, you may use `Let's Encrypt`_ to acquire a free SSL
certificate and follow the steps in :ref:`using-lets-encrypt` to set up a
public server.
.. _OWASP: https://www.owasp.org
.. _tutorial: https://arstechnica.com/information-technology/2009/12/how-to-get-set-with-a-secure-sertificate-for-free/
.. _notebook_public_server:
Running a public notebook server
--------------------------------
If you want to access your notebook server remotely via a web browser,
you can do so by running a public notebook server. For optimal security
when running a public notebook server, you should first secure the
server with a password and SSL/HTTPS as described in
:ref:`notebook_server_security`.
Start by creating a certificate file and a hashed password, as explained in
:ref:`notebook_server_security`.
If you don't already have one, create a
config file for the notebook using the following command line::
$ jupyter notebook --generate-config
In the ``~/.jupyter`` directory, edit the notebook config file,
``jupyter_notebook_config.py``. By default, the notebook config file has
all fields commented out. The minimum set of configuration options that
you should uncomment and edit in :file:`jupyter_notebook_config.py` is the
following::
# Set options for certfile, ip, password, and toggle off
# browser auto-opening
c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
c.NotebookApp.keyfile = u'/absolute/path/to/your/certificate/mykey.key'
# Set ip to '*' to bind on all interfaces (ips) for the public server
c.NotebookApp.ip = '*'
c.NotebookApp.password = u'sha1:bcd259ccf...<your hashed password here>'
c.NotebookApp.open_browser = False
# It is a good idea to set a known, fixed port for server access
c.NotebookApp.port = 9999
You can then start the notebook using the ``jupyter notebook`` command.
.. _using-lets-encrypt:
Using Let's Encrypt
~~~~~~~~~~~~~~~~~~~
`Let's Encrypt`_ provides free SSL/TLS certificates. You can also set up a
public server using a `Let's Encrypt`_ certificate.
:ref:`notebook_public_server` will be similar when using a Let's Encrypt
certificate with a few configuration changes. Here are the steps:
1. Create a `Let's Encrypt certificate <https://letsencrypt.org/getting-started/>`_.
2. Use :ref:`hashed-pw` to create one.
3. If you don't already have config file for the notebook, create one
using the following command:
.. code-block:: bash
$ jupyter notebook --generate-config
4. In the ``~/.jupyter`` directory, edit the notebook config file,
``jupyter_notebook_config.py``. By default, the notebook config file has
all fields commented out. The minimum set of configuration options that
you should to uncomment and edit in :file:`jupyter_notebook_config.py` is the
following::
# Set options for certfile, ip, password, and toggle off
# browser auto-opening
c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/fullchain.pem'
c.NotebookApp.keyfile = u'/absolute/path/to/your/certificate/privkey.pem'
# Set ip to '*' to bind on all interfaces (ips) for the public server
c.NotebookApp.ip = '*'
c.NotebookApp.password = u'sha1:bcd259ccf...<your hashed password here>'
c.NotebookApp.open_browser = False
# It is a good idea to set a known, fixed port for server access
c.NotebookApp.port = 9999
You can then start the notebook using the ``jupyter notebook`` command.
.. important::
**Use 'https'.**
Keep in mind that when you enable SSL support, you must access the
notebook server over ``https://``, not over plain ``http://``. The startup
message from the server prints a reminder in the console, but *it is easy
to overlook this detail and think the server is for some reason
non-responsive*.
**When using SSL, always access the notebook server with 'https://'.**
You may now access the public server by pointing your browser to
``https://your.host.com:9999`` where ``your.host.com`` is your public server's
domain.
.. _`Let's Encrypt`: https://letsencrypt.org
Firewall Setup
~~~~~~~~~~~~~~
To function correctly, the firewall on the computer running the jupyter
notebook server must be configured to allow connections from client
machines on the access port ``c.NotebookApp.port`` set in
:file:`jupyter_notebook_config.py` to allow connections to the
web interface. The firewall must also allow connections from
127.0.0.1 (localhost) on ports from 49152 to 65535.
These ports are used by the server to communicate with the notebook kernels.
The kernel communication ports are chosen randomly by ZeroMQ, and may require
multiple connections per kernel, so a large range of ports must be accessible.
Running the notebook with a customized URL prefix
-------------------------------------------------
The notebook dashboard, which is the landing page with an overview
of the notebooks in your working directory, is typically found and accessed
at the default URL ``http://localhost:8888/``.
If you prefer to customize the URL prefix for the notebook dashboard, you can
do so through modifying ``jupyter_notebook_config.py``. For example, if you
prefer that the notebook dashboard be located with a sub-directory that
contains other ipython files, e.g. ``http://localhost:8888/ipython/``,
you can do so with configuration options like the following (see above for
instructions about modifying ``jupyter_notebook_config.py``):
.. code-block:: python
c.NotebookApp.base_url = '/ipython/'
Embedding the notebook in another website
-----------------------------------------
Sometimes you may want to embed the notebook somewhere on your website,
e.g. in an IFrame. To do this, you may need to override the
Content-Security-Policy to allow embedding. Assuming your website is at
`https://mywebsite.example.com`, you can embed the notebook on your website
with the following configuration setting in
:file:`jupyter_notebook_config.py`:
.. code-block:: python
c.NotebookApp.tornado_settings = {
'headers': {
'Content-Security-Policy': "frame-ancestors https://mywebsite.example.com 'self' "
}
}
When embedding the notebook in a website using an iframe,
consider putting the notebook in single-tab mode.
Since the notebook opens some links in new tabs by default,
single-tab mode keeps the notebook from opening additional tabs.
Adding the following to :file:`~/.jupyter/custom/custom.js` will enable
single-tab mode:
.. code-block:: javascript
define(['base/js/namespace'], function(Jupyter){
Jupyter._target = '_self';
});
Using a gateway server for kernel management
--------------------------------------------
You are now able to redirect the management of your kernels to a Gateway Server
(i.e., `Jupyter Kernel Gateway <https://jupyter-kernel-gateway.readthedocs.io/en/latest/>`_ or
`Jupyter Enterprise Gateway <https://jupyter-enterprise-gateway.readthedocs.io/en/latest/>`_)
simply by specifying a Gateway url via the following command-line option:
.. code-block:: bash
$ jupyter notebook --gateway-url=http://my-gateway-server:8888
the environment:
.. code-block:: bash
JUPYTER_GATEWAY_URL=http://my-gateway-server:8888
or in :file:`jupyter_notebook_config.py`:
.. code-block:: python
c.GatewayClient.url = http://my-gateway-server:8888
When provided, all kernel specifications will be retrieved from the specified Gateway server and all
kernels will be managed by that server. This option enables the ability to target kernel processes
against managed clusters while allowing for the notebook's management to remain local to the Notebook
server.
Known issues
------------
Proxies
~~~~~~~
When behind a proxy, especially if your system or browser is set to autodetect
the proxy, the notebook web application might fail to connect to the server's
websockets, and present you with a warning at startup. In this case, you need
to configure your system not to use the proxy for the server's address.
For example, in Firefox, go to the Preferences panel, Advanced section,
Network tab, click 'Settings...', and add the address of the notebook server
to the 'No proxy for' field.
Content-Security-Policy (CSP)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Certain `security guidelines
<https://infosec.mozilla.org/guidelines/web_security.html#content-security-policy>`_
recommend that servers use a Content-Security-Policy (CSP) header to prevent
cross-site scripting vulnerabilities, specifically limiting to ``default-src:
https:`` when possible. This directive causes two problems with Jupyter.
First, it disables execution of inline javascript code, which is used
extensively by Jupyter. Second, it limits communication to the https scheme,
and prevents WebSockets from working because they communicate via the wss
scheme (or ws for insecure communication). Jupyter uses WebSockets for
interacting with kernels, so when you visit a server with such a CSP, your
browser will block attempts to use wss, which will cause you to see
"Connection failed" messages from jupyter notebooks, or simply no response
from jupyter terminals. By looking in your browser's javascript console, you
can see any error messages that will explain what is failing.
To avoid these problem, you need to add ``'unsafe-inline'`` and ``connect-src
https: wss:`` to your CSP header, at least for pages served by jupyter. (That
is, you can leave your CSP unchanged for other parts of your website.) Note
that multiple CSP headers are allowed, but successive CSP headers can only
restrict the policy; they cannot loosen it. For example, if your server sends
both of these headers
Content-Security-Policy "default-src https: 'unsafe-inline'"
Content-Security-Policy "connect-src https: wss:"
the first policy will already eliminate wss connections, so the second has no
effect. Therefore, you can't simply add the second header; you have to
actually modify your CSP header to look more like this:
Content-Security-Policy "default-src https: 'unsafe-inline'; connect-src https: wss:"
Docker CMD
~~~~~~~~~~
Using ``jupyter notebook`` as a
`Docker CMD <https://docs.docker.com/engine/reference/builder/#cmd>`_ results in
kernels repeatedly crashing, likely due to a lack of `PID reaping
<https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/>`_.
To avoid this, use the `tini <https://github.com/krallin/tini>`_ ``init`` as your
Dockerfile `ENTRYPOINT`::
# Add Tini. Tini operates as a process subreaper for jupyter. This prevents
# kernel crashes.
ENV TINI_VERSION v0.6.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /usr/bin/tini
RUN chmod +x /usr/bin/tini
ENTRYPOINT ["/usr/bin/tini", "--"]
EXPOSE 8888
CMD ["jupyter", "notebook", "--port=8888", "--no-browser", "--ip=0.0.0.0"]