mirror of
https://github.com/jupyter/notebook.git
synced 2025-02-17 12:39:54 +08:00
Cleanup Configuration section in the docs
This commit is contained in:
parent
f487ec8c7d
commit
58d701a383
@ -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.
|
||||
|
@ -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
|
||||
|
@ -1,183 +0,0 @@
|
||||
Custom bundler extensions
|
||||
=========================
|
||||
|
||||
The notebook server supports the writing of *bundler extensions* that
|
||||
transform, package, and download/deploy notebook files. As a developer, you
|
||||
need only write a single Python function to implement a bundler. The notebook
|
||||
server automatically generates a *File -> Download as* or *File -> Deploy as*
|
||||
menu item in the notebook front-end to trigger your bundler.
|
||||
|
||||
Here are some examples of what you can implement using bundler extensions:
|
||||
|
||||
* Convert a notebook file to a HTML document and publish it as a post on a
|
||||
blog site
|
||||
* Create a snapshot of the current notebook environment and bundle that
|
||||
definition plus notebook into a zip download
|
||||
* Deploy a notebook as a standalone, interactive `dashboard <https://github.com/jupyter-incubator/dashboards_bundlers>`_
|
||||
|
||||
To implement a bundler extension, you must do all of the following:
|
||||
|
||||
* Declare bundler extension metadata in your Python package
|
||||
* Write a `bundle` function that responds to bundle requests
|
||||
* Instruct your users on how to enable/disable your bundler extension
|
||||
|
||||
The following sections describe these steps in detail.
|
||||
|
||||
Declaring bundler metadata
|
||||
--------------------------
|
||||
|
||||
You must provide information about the bundler extension(s) your package
|
||||
provides by implementing a `_jupyter_bundlerextensions_paths` function. This
|
||||
function can reside anywhere in your package so long as it can be imported
|
||||
when enabling the bundler extension. (See :ref:`enabling-bundlers`.)
|
||||
|
||||
.. code:: python
|
||||
|
||||
# in mypackage.hello_bundler
|
||||
|
||||
def _jupyter_bundlerextension_paths():
|
||||
"""Example "hello world" bundler extension"""
|
||||
return [{
|
||||
'name': 'hello_bundler', # unique bundler name
|
||||
'label': 'Hello Bundler', # human-readable menu item label
|
||||
'module_name': 'mypackage.hello_bundler', # module containing bundle()
|
||||
'group': 'deploy' # group under 'deploy' or 'download' menu
|
||||
}]
|
||||
|
||||
Note that the return value is a list. By returning multiple dictionaries in
|
||||
the list, you allow users to enable/disable sets of bundlers all at once.
|
||||
|
||||
Writing the `bundle` function
|
||||
-----------------------------
|
||||
|
||||
At runtime, a menu item with the given label appears either in the
|
||||
*File -> Deploy as* or *File -> Download as* menu depending on the `group`
|
||||
value in your metadata. When a user clicks the menu item, a new browser tab
|
||||
opens and notebook server invokes a `bundle` function in the `module_name`
|
||||
specified in the metadata.
|
||||
|
||||
You must implement a `bundle` function that matches the signature of the
|
||||
following example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
# in mypackage.hello_bundler
|
||||
|
||||
def bundle(handler, model):
|
||||
"""Transform, convert, bundle, etc. the notebook referenced by the given
|
||||
model.
|
||||
|
||||
Then issue a Tornado web response using the `handler` to redirect
|
||||
the user's browser, download a file, show a HTML page, etc. This function
|
||||
must finish the handler response before returning either explicitly or by
|
||||
raising an exception.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
handler : tornado.web.RequestHandler
|
||||
Handler that serviced the bundle request
|
||||
model : dict
|
||||
Notebook model from the configured ContentManager
|
||||
"""
|
||||
handler.finish('I bundled {}!'.format(model['path']))
|
||||
|
||||
Your `bundle` function is free to do whatever it wants with the request and
|
||||
respond in any manner. For example, it may read additional query parameters
|
||||
from the request, issue a redirect to another site, run a local process (e.g.,
|
||||
`nbconvert`), make a HTTP request to another service, etc.
|
||||
|
||||
The caller of the `bundle` function is `@tornado.gen.coroutine` decorated and
|
||||
wraps its call with `torando.gen.maybe_future`. This behavior means you may
|
||||
handle the web request synchronously, as in the example above, or
|
||||
asynchronously using `@tornado.gen.coroutine` and `yield`, as in the example
|
||||
below.
|
||||
|
||||
.. code:: python
|
||||
|
||||
from tornado import gen
|
||||
|
||||
@gen.coroutine
|
||||
def bundle(handler, model):
|
||||
# simulate a long running IO op (e.g., deploying to a remote host)
|
||||
yield gen.sleep(10)
|
||||
|
||||
# now respond
|
||||
handler.finish('I spent 10 seconds bundling {}!'.format(model['path']))
|
||||
|
||||
You should prefer the second, asynchronous approach when your bundle operation
|
||||
is long-running and would otherwise block the notebook server main loop if
|
||||
handled synchronously.
|
||||
|
||||
For more details about the data flow from menu item click to bundle function
|
||||
invocation, see :ref:`bundler-details`.
|
||||
|
||||
.. _enabling-bundlers:
|
||||
|
||||
Enabling/disabling bundler extensions
|
||||
-------------------------------------
|
||||
|
||||
The notebook server includes a command line interface (CLI) for enabling and
|
||||
disabling bundler extensions.
|
||||
|
||||
You should document the basic commands for enabling and disabling your
|
||||
bundler. One possible command for enabling the `hello_bundler` example is the
|
||||
following:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension enable --py mypackage.hello_bundler --sys-prefix
|
||||
|
||||
The above updates the notebook configuration file in the current
|
||||
conda/virtualenv environment (`--sys-prefix`) with the metadata returned by
|
||||
the `mypackage.hellow_bundler._jupyter_bundlerextension_paths` function.
|
||||
|
||||
The corresponding command to later disable the bundler extension is the
|
||||
following:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension disable --py mypackage.hello_bundler --sys-prefix
|
||||
|
||||
For more help using the `bundlerextension` subcommand, run the following.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension --help
|
||||
|
||||
The output describes options for listing enabled bundlers, configuring
|
||||
bundlers for single users, configuring bundlers system-wide, etc.
|
||||
|
||||
Example: IPython Notebook bundle (.zip)
|
||||
---------------------------------------
|
||||
|
||||
The `hello_bundler` example in this documentation is simplistic in the name
|
||||
of brevity. For more meaningful examples, see
|
||||
`notebook/bundler/zip_bundler.py` and `notebook/bundler/tarball_bundler.py`.
|
||||
You can enable them to try them like so:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
jupyter bundlerextension enable --py notebook.bundler.zip_bundler --sys-prefix
|
||||
jupyter bundlerextension enable --py notebook.bundler.tarball_bundler --sys-prefix
|
||||
|
||||
.. _bundler-details:
|
||||
|
||||
Bundler invocation details
|
||||
--------------------------
|
||||
|
||||
Support for bundler extensions comes from Python modules in `notebook/bundler`
|
||||
and JavaScript in `notebook/static/notebook/js/menubar.js`. The flow of data
|
||||
between the various components proceeds roughly as follows:
|
||||
|
||||
1. User opens a notebook document
|
||||
2. Notebook front-end JavaScript loads notebook configuration
|
||||
3. Bundler front-end JS creates menu items for all bundler extensions in the
|
||||
config
|
||||
4. User clicks a bundler menu item
|
||||
5. JS click handler opens a new browser window/tab to
|
||||
`<notebook base_url>/bundle/<path/to/notebook>?bundler=<name>` (i.e., a
|
||||
HTTP GET request)
|
||||
6. Bundle handler validates the notebook path and bundler `name`
|
||||
7. Bundle handler delegates the request to the `bundle` function in the
|
||||
bundler's `module_name`
|
||||
8. `bundle` function finishes the HTTP request
|
@ -1,293 +0,0 @@
|
||||
.. _contents_api:
|
||||
|
||||
Contents API
|
||||
============
|
||||
|
||||
.. currentmodule:: notebook.services.contents
|
||||
|
||||
The Jupyter Notebook web application provides a graphical interface for
|
||||
creating, opening, renaming, and deleting files in a virtual filesystem.
|
||||
|
||||
The :class:`~manager.ContentsManager` class defines an abstract
|
||||
API for translating these interactions into operations on a particular storage
|
||||
medium. The default implementation,
|
||||
:class:`~filemanager.FileContentsManager`, uses the local
|
||||
filesystem of the server for storage and straightforwardly serializes notebooks
|
||||
into JSON. Users can override these behaviors by supplying custom subclasses
|
||||
of ContentsManager.
|
||||
|
||||
This section describes the interface implemented by ContentsManager subclasses.
|
||||
We refer to this interface as the **Contents API**.
|
||||
|
||||
Data Model
|
||||
----------
|
||||
|
||||
.. currentmodule:: notebook.services.contents.manager
|
||||
|
||||
Filesystem Entities
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
.. _notebook models:
|
||||
|
||||
ContentsManager methods represent virtual filesystem entities as dictionaries,
|
||||
which we refer to as **models**.
|
||||
|
||||
Models may contain the following entries:
|
||||
|
||||
+--------------------+-----------+------------------------------+
|
||||
| Key | Type |Info |
|
||||
+====================+===========+==============================+
|
||||
|**name** |unicode |Basename of the entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**path** |unicode |Full |
|
||||
| | |(:ref:`API-style<apipaths>`) |
|
||||
| | |path to the entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**type** |unicode |The entity type. One of |
|
||||
| | |``"notebook"``, ``"file"`` or |
|
||||
| | |``"directory"``. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**created** |datetime |Creation date of the entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**last_modified** |datetime |Last modified date of the |
|
||||
| | |entity. |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**content** |variable |The "content" of the entity. |
|
||||
| | |(:ref:`See |
|
||||
| | |Below<modelcontent>`) |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**mimetype** |unicode or |The mimetype of ``content``, |
|
||||
| |``None`` |if any. (:ref:`See |
|
||||
| | |Below<modelcontent>`) |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|**format** |unicode or |The format of ``content``, |
|
||||
| |``None`` |if any. (:ref:`See |
|
||||
| | |Below<modelcontent>`) |
|
||||
+--------------------+-----------+------------------------------+
|
||||
|
||||
.. _modelcontent:
|
||||
|
||||
Certain model fields vary in structure depending on the ``type`` field of the
|
||||
model. There are three model types: **notebook**, **file**, and **directory**.
|
||||
|
||||
- ``notebook`` models
|
||||
- The ``format`` field is always ``"json"``.
|
||||
- The ``mimetype`` field is always ``None``.
|
||||
- The ``content`` field contains a
|
||||
:class:`nbformat.notebooknode.NotebookNode` representing the .ipynb file
|
||||
represented by the model. See the `NBFormat`_ documentation for a full
|
||||
description.
|
||||
|
||||
- ``file`` models
|
||||
- The ``format`` field is either ``"text"`` or ``"base64"``.
|
||||
- The ``mimetype`` field can be any mimetype string, but defaults to
|
||||
``text/plain`` for text-format models and
|
||||
``application/octet-stream`` for base64-format models. For files with
|
||||
unknown mime types (e.g. unknown file extensions), this field may be
|
||||
`None`.
|
||||
- The ``content`` field is always of type ``unicode``. For text-format
|
||||
file models, ``content`` simply contains the file's bytes after decoding
|
||||
as UTF-8. Non-text (``base64``) files are read as bytes, base64 encoded,
|
||||
and then decoded as UTF-8.
|
||||
|
||||
- ``directory`` models
|
||||
- The ``format`` field is always ``"json"``.
|
||||
- The ``mimetype`` field is always ``None``.
|
||||
- The ``content`` field contains a list of :ref:`content-free<contentfree>`
|
||||
models representing the entities in the directory.
|
||||
|
||||
.. note::
|
||||
|
||||
.. _contentfree:
|
||||
|
||||
In certain circumstances, we don't need the full content of an entity to
|
||||
complete a Contents API request. In such cases, we omit the ``content``, and
|
||||
``format`` keys from the model. The default values for the ``mimetype``
|
||||
field will might also not be evaluated, in which case it will be set as `None`.
|
||||
This reduced reply most commonly occurs when listing a directory, in
|
||||
which circumstance we represent files within the directory as content-less
|
||||
models to avoid having to recursively traverse and serialize the entire
|
||||
filesystem.
|
||||
|
||||
**Sample Models**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Notebook Model with Content
|
||||
{
|
||||
'content': {
|
||||
'metadata': {},
|
||||
'nbformat': 4,
|
||||
'nbformat_minor': 0,
|
||||
'cells': [
|
||||
{
|
||||
'cell_type': 'markdown',
|
||||
'metadata': {},
|
||||
'source': 'Some **Markdown**',
|
||||
},
|
||||
],
|
||||
},
|
||||
'created': datetime(2015, 7, 25, 19, 50, 19, 19865),
|
||||
'format': 'json',
|
||||
'last_modified': datetime(2015, 7, 25, 19, 50, 19, 19865),
|
||||
'mimetype': None,
|
||||
'name': 'a.ipynb',
|
||||
'path': 'foo/a.ipynb',
|
||||
'type': 'notebook',
|
||||
'writable': True,
|
||||
}
|
||||
|
||||
# Notebook Model without Content
|
||||
{
|
||||
'content': None,
|
||||
'created': datetime.datetime(2015, 7, 25, 20, 17, 33, 271931),
|
||||
'format': None,
|
||||
'last_modified': datetime.datetime(2015, 7, 25, 20, 17, 33, 271931),
|
||||
'mimetype': None,
|
||||
'name': 'a.ipynb',
|
||||
'path': 'foo/a.ipynb',
|
||||
'type': 'notebook',
|
||||
'writable': True
|
||||
}
|
||||
|
||||
|
||||
API Paths
|
||||
~~~~~~~~~
|
||||
.. _apipaths:
|
||||
|
||||
ContentsManager methods represent the locations of filesystem resources as
|
||||
**API-style paths**. Such paths are interpreted as relative to the root
|
||||
directory of the notebook server. For compatibility across systems, the
|
||||
following guarantees are made:
|
||||
|
||||
* Paths are always ``unicode``, not ``bytes``.
|
||||
* Paths are not URL-escaped.
|
||||
* Paths are always forward-slash (/) delimited, even on Windows.
|
||||
* Leading and trailing slashes are stripped. For example, ``/foo/bar/buzz/``
|
||||
becomes ``foo/bar/buzz``.
|
||||
* The empty string (``""``) represents the root directory.
|
||||
|
||||
|
||||
Writing a Custom ContentsManager
|
||||
--------------------------------
|
||||
|
||||
The default ContentsManager is designed for users running the notebook as an
|
||||
application on a personal computer. It stores notebooks as .ipynb files on the
|
||||
local filesystem, and it maps files and directories in the Notebook UI to files
|
||||
and directories on disk. It is possible to override how notebooks are stored
|
||||
by implementing your own custom subclass of ``ContentsManager``. For example,
|
||||
if you deploy the notebook in a context where you don't trust or don't have
|
||||
access to the filesystem of the notebook server, it's possible to write your
|
||||
own ContentsManager that stores notebooks and files in a database.
|
||||
|
||||
|
||||
Required Methods
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
A minimal complete implementation of a custom
|
||||
:class:`~manager.ContentsManager` must implement the following
|
||||
methods:
|
||||
|
||||
.. autosummary::
|
||||
ContentsManager.get
|
||||
ContentsManager.save
|
||||
ContentsManager.delete_file
|
||||
ContentsManager.rename_file
|
||||
ContentsManager.file_exists
|
||||
ContentsManager.dir_exists
|
||||
ContentsManager.is_hidden
|
||||
|
||||
You may be required to specify a Checkpoints object, as the default one,
|
||||
``FileCheckpoints``, could be incompatible with your custom
|
||||
ContentsManager.
|
||||
|
||||
|
||||
Chunked Saving
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
The contents API allows for "chunked" saving of files, i.e.
|
||||
saving/transmitting in partial pieces:
|
||||
|
||||
* This can only be used when the ``type`` of the model is ``file``.
|
||||
* The model should be as otherwise expected for
|
||||
:meth:`~manager.ContentsManager.save`, with an added field ``chunk``.
|
||||
* The value of ``chunk`` should be an integer starting at ``1``, and incrementing
|
||||
for each subsequent chunk, except for the final chunk, which should be
|
||||
indicated with a value of ``-1``.
|
||||
* The model returned from using :meth:`~manager.ContentsManager.save` with
|
||||
``chunk`` should be treated as unreliable for all chunks except the final one.
|
||||
* Any interaction with a file being saved in a chunked manner is unreliable
|
||||
until the final chunk has been saved. This includes directory listings.
|
||||
|
||||
|
||||
Customizing Checkpoints
|
||||
-----------------------
|
||||
.. currentmodule:: notebook.services.contents.checkpoints
|
||||
|
||||
Customized Checkpoint definitions allows behavior to be
|
||||
altered and extended.
|
||||
|
||||
The ``Checkpoints`` and ``GenericCheckpointsMixin`` classes
|
||||
(from :mod:`notebook.services.contents.checkpoints`)
|
||||
have reusable code and are intended to be used together,
|
||||
but require the following methods to be implemented.
|
||||
|
||||
.. autosummary::
|
||||
Checkpoints.rename_checkpoint
|
||||
Checkpoints.list_checkpoints
|
||||
Checkpoints.delete_checkpoint
|
||||
GenericCheckpointsMixin.create_file_checkpoint
|
||||
GenericCheckpointsMixin.create_notebook_checkpoint
|
||||
GenericCheckpointsMixin.get_file_checkpoint
|
||||
GenericCheckpointsMixin.get_notebook_checkpoint
|
||||
|
||||
No-op example
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Here is an example of a no-op checkpoints object - note the mixin
|
||||
comes first. The docstrings indicate what each method should do or
|
||||
return for a more complete implementation.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class NoOpCheckpoints(GenericCheckpointsMixin, Checkpoints):
|
||||
"""requires the following methods:"""
|
||||
def create_file_checkpoint(self, content, format, path):
|
||||
""" -> checkpoint model"""
|
||||
def create_notebook_checkpoint(self, nb, path):
|
||||
""" -> checkpoint model"""
|
||||
def get_file_checkpoint(self, checkpoint_id, path):
|
||||
""" -> {'type': 'file', 'content': <str>, 'format': {'text', 'base64'}}"""
|
||||
def get_notebook_checkpoint(self, checkpoint_id, path):
|
||||
""" -> {'type': 'notebook', 'content': <output of nbformat.read>}"""
|
||||
def delete_checkpoint(self, checkpoint_id, path):
|
||||
"""deletes a checkpoint for a file"""
|
||||
def list_checkpoints(self, path):
|
||||
"""returns a list of checkpoint models for a given file,
|
||||
default just does one per file
|
||||
"""
|
||||
return []
|
||||
def rename_checkpoint(self, checkpoint_id, old_path, new_path):
|
||||
"""renames checkpoint from old path to new path"""
|
||||
|
||||
See ``GenericFileCheckpoints`` in :mod:`notebook.services.contents.filecheckpoints`
|
||||
for a more complete example.
|
||||
|
||||
Testing
|
||||
-------
|
||||
.. currentmodule:: notebook.services.contents.tests
|
||||
|
||||
:mod:`notebook.services.contents.tests` includes several test suites written
|
||||
against the abstract Contents API. This means that an excellent way to test a
|
||||
new ContentsManager subclass is to subclass our tests to make them use your
|
||||
ContentsManager.
|
||||
|
||||
.. note::
|
||||
|
||||
PGContents_ is an example of a complete implementation of a custom
|
||||
``ContentsManager``. It stores notebooks and files in PostgreSQL_ and encodes
|
||||
directories as SQL relations. PGContents also provides an example of how to
|
||||
re-use the notebook's tests.
|
||||
|
||||
.. _NBFormat: https://nbformat.readthedocs.io/en/latest/index.html
|
||||
.. _PGContents: https://github.com/quantopian/pgcontents
|
||||
.. _PostgreSQL: https://www.postgresql.org/
|
@ -1,280 +1,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
|
||||
|
@ -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>`__
|
@ -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
|
||||
|
@ -1,91 +0,0 @@
|
||||
Customize keymaps
|
||||
=================
|
||||
|
||||
.. note::
|
||||
|
||||
Declarative Custom Keymaps is a provisional feature with unstable API
|
||||
which is not guaranteed to be kept in future versions of the notebook,
|
||||
and can be removed or changed without warnings.
|
||||
|
||||
The notebook shortcuts that are defined by jupyter both in edit mode and
|
||||
command mode are configurable in the frontend configuration file
|
||||
``~/.jupyter/nbconfig/notebook.json``. The modification of keyboard
|
||||
shortcuts suffers from several limitations, mainly that your Browser and OS
|
||||
might prevent certain shortcuts from working correctly. If this is the case,
|
||||
there is unfortunately not much that can be done. The second issue can arise
|
||||
with keyboards that have a layout different than US English. Again, even if
|
||||
we are aware of the issue, there is not much that can be done.
|
||||
|
||||
Shortcuts are also limited by the underlying library that handles code and
|
||||
text editing: CodeMirror. If some keyboard shortcuts are conflicting, the
|
||||
method described below might not work to create new keyboard shortcuts,
|
||||
especially in the ``edit`` mode of the notebook.
|
||||
|
||||
|
||||
The 4 sections of interest in ``~/.jupyter/nbconfig/notebook.json`` are the
|
||||
following:
|
||||
|
||||
- ``keys.command.unbind``
|
||||
- ``keys.edit.unbind``
|
||||
- ``keys.command.bind``
|
||||
- ``keys.edit.bind``
|
||||
|
||||
The first two sections describe which default keyboard shortcuts not to
|
||||
register at notebook startup time. These are mostly useful if you need to
|
||||
``unbind`` a default keyboard shortcut before binding it to a new
|
||||
``command``.
|
||||
|
||||
The first two sections apply respectively to the ``command`` and ``edit``
|
||||
mode of the notebook. They take a list of shortcuts to ``unbind``.
|
||||
|
||||
For example, to unbind the shortcut to split a cell at the position of the
|
||||
cursor (``Ctrl-Shift-Minus``) use the following:
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file ~/.jupyter/nbconfig/notebook.json
|
||||
|
||||
{
|
||||
"keys": {
|
||||
"edit": {
|
||||
"unbind": [
|
||||
"Ctrl-Shift-Minus"
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
The last two sections describe which new keyboard shortcuts to register
|
||||
at notebook startup time and which actions they trigger.
|
||||
|
||||
The last two sections apply respectively to the ``command`` and ``edit``
|
||||
mode of the notebook. They take a dictionary with shortcuts as ``keys`` and
|
||||
``commands`` name as value.
|
||||
|
||||
For example, to bind the shortcut ``G,G,G`` (Press G three time in a row) in
|
||||
command mode to the command that restarts the kernel and runs all cells, use
|
||||
the following:
|
||||
|
||||
|
||||
.. code:: javascript
|
||||
|
||||
// file ~/.jupyter/nbconfig/notebook.json
|
||||
|
||||
{
|
||||
"keys": {
|
||||
"command": {
|
||||
"bind": {
|
||||
"G,G,G":"jupyter-notebook:restart-kernel-and-run-all-cells"
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
The name of the available ``commands`` can be find by hovering over the
|
||||
right end of a row in the command palette.
|
@ -1,87 +0,0 @@
|
||||
File save hooks
|
||||
===============
|
||||
|
||||
You can configure functions that are run whenever a file is saved. There are
|
||||
two hooks available:
|
||||
|
||||
* ``ContentsManager.pre_save_hook`` runs on the API path and model with
|
||||
content. This can be used for things like stripping output that people don't
|
||||
like adding to VCS noise.
|
||||
* ``FileContentsManager.post_save_hook`` runs on the filesystem path and model
|
||||
without content. This could be used to commit changes after every save, for
|
||||
instance.
|
||||
|
||||
They are both called with keyword arguments:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pre_save_hook(model=model, path=path, contents_manager=cm)
|
||||
post_save_hook(model=model, os_path=os_path, contents_manager=cm)
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
These can both be added to :file:`jupyter_notebook_config.py`.
|
||||
|
||||
A pre-save hook for stripping output:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def scrub_output_pre_save(model, **kwargs):
|
||||
"""scrub output before saving notebooks"""
|
||||
# only run on notebooks
|
||||
if model['type'] != 'notebook':
|
||||
return
|
||||
# only run on nbformat v4
|
||||
if model['content']['nbformat'] != 4:
|
||||
return
|
||||
|
||||
for cell in model['content']['cells']:
|
||||
if cell['cell_type'] != 'code':
|
||||
continue
|
||||
cell['outputs'] = []
|
||||
cell['execution_count'] = None
|
||||
|
||||
c.FileContentsManager.pre_save_hook = scrub_output_pre_save
|
||||
|
||||
A post-save hook to make a script equivalent whenever the notebook is saved
|
||||
(replacing the ``--script`` option in older versions of the notebook):
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import io
|
||||
import os
|
||||
from notebook.utils import to_api_path
|
||||
|
||||
_script_exporter = None
|
||||
|
||||
def script_post_save(model, os_path, contents_manager, **kwargs):
|
||||
"""convert notebooks to Python script after save with nbconvert
|
||||
|
||||
replaces `jupyter notebook --script`
|
||||
"""
|
||||
from nbconvert.exporters.script import ScriptExporter
|
||||
|
||||
if model['type'] != 'notebook':
|
||||
return
|
||||
|
||||
global _script_exporter
|
||||
|
||||
if _script_exporter is None:
|
||||
_script_exporter = ScriptExporter(parent=contents_manager)
|
||||
|
||||
log = contents_manager.log
|
||||
|
||||
base, ext = os.path.splitext(os_path)
|
||||
script, resources = _script_exporter.from_filename(os_path)
|
||||
script_fname = base + resources.get('output_extension', '.txt')
|
||||
log.info("Saving script /%s", to_api_path(script_fname, contents_manager.root_dir))
|
||||
|
||||
with io.open(script_fname, 'w', encoding='utf-8') as f:
|
||||
f.write(script)
|
||||
|
||||
c.FileContentsManager.post_save_hook = script_post_save
|
||||
|
||||
|
||||
This could be a simple call to ``jupyter nbconvert --to script``, but spawning
|
||||
the subprocess every time is quite slow.
|
@ -1,91 +0,0 @@
|
||||
.. _frontend_config:
|
||||
|
||||
Configuring the notebook frontend
|
||||
=================================
|
||||
|
||||
.. note::
|
||||
|
||||
The ability to configure the notebook frontend UI and preferences is
|
||||
still a work in progress.
|
||||
|
||||
This document is a rough explanation on how you can persist some configuration
|
||||
options for the notebook JavaScript.
|
||||
|
||||
There is no exhaustive list of all the configuration options as most options
|
||||
are passed down to other libraries, which means that non valid
|
||||
configuration can be ignored without any error messages.
|
||||
|
||||
|
||||
How front end configuration works
|
||||
---------------------------------
|
||||
The frontend configuration system works as follows:
|
||||
|
||||
- get a handle of a configurable JavaScript object.
|
||||
- access its configuration attribute.
|
||||
- update its configuration attribute with a JSON patch.
|
||||
|
||||
|
||||
Example - Changing the notebook's default indentation
|
||||
-----------------------------------------------------
|
||||
This example explains how to change the default setting ``indentUnit``
|
||||
for CodeMirror Code Cells::
|
||||
|
||||
var cell = Jupyter.notebook.get_selected_cell();
|
||||
var config = cell.config;
|
||||
var patch = {
|
||||
CodeCell:{
|
||||
cm_config:{indentUnit:2}
|
||||
}
|
||||
}
|
||||
config.update(patch)
|
||||
|
||||
You can enter the previous snippet in your browser's JavaScript console once.
|
||||
Then reload the notebook page in your browser. Now, the preferred indent unit
|
||||
should be equal to two spaces. The custom setting persists and you do not need
|
||||
to reissue the patch on new notebooks.
|
||||
|
||||
``indentUnit``, used in this example, is one of the many `CodeMirror options
|
||||
<https://codemirror.net/doc/manual.html#option_indentUnit>`_ which are available
|
||||
for configuration.
|
||||
|
||||
You can similarly change the options of the file editor by entering the following
|
||||
snippet in the browser's Javascript console once (from a file editing page).::
|
||||
|
||||
var config = Jupyter.editor.config
|
||||
var patch = {
|
||||
Editor: {
|
||||
codemirror_options: {
|
||||
indentUnit: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
config.update(patch)
|
||||
|
||||
Example - Restoring the notebook's default indentation
|
||||
------------------------------------------------------
|
||||
If you want to restore a notebook frontend preference to its default value,
|
||||
you will enter a JSON patch with a ``null`` value for the preference setting.
|
||||
|
||||
For example, let's restore the indent setting ``indentUnit`` to its default of
|
||||
four spaces. Enter the following code snippet in your JavaScript console::
|
||||
|
||||
var cell = Jupyter.notebook.get_selected_cell();
|
||||
var config = cell.config;
|
||||
var patch = {
|
||||
CodeCell:{
|
||||
cm_config:{indentUnit: null} // only change here.
|
||||
}
|
||||
}
|
||||
config.update(patch)
|
||||
|
||||
Reload the notebook in your browser and the default indent should again be two
|
||||
spaces.
|
||||
|
||||
Persisting configuration settings
|
||||
---------------------------------
|
||||
Under the hood, Jupyter will persist the preferred configuration settings in
|
||||
``~/.jupyter/nbconfig/<section>.json``, with ``<section>``
|
||||
taking various value depending on the page where the configuration is issued.
|
||||
``<section>`` can take various values like ``notebook``, ``tree``, and
|
||||
``editor``. A ``common`` section contains configuration settings shared by all
|
||||
pages.
|
@ -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"]
|
Loading…
Reference in New Issue
Block a user