自定义 Voilà#
您可以通过多种方式自定义 Voilà,以控制您创建的仪表板的外观和感觉。
切换到经典树页面#
Voilà 的默认树页面现在是一个基于 JupyterLab 的应用程序,使用文件浏览器小部件。
基于 Jinja 的树页面仍然受支持,但用户需要通过 --classic-tree
CLI 选项、VoilaConfiguration.classic_tree
配置或查询字符串中的 ?classic-tree=True
来激活它,例如
voila <path-to-notebook> --classic-tree
新的树页面支持 JupyterLab 自定义主题,对于经典树页面,仅支持亮色和暗色主题。
更改主题#
默认情况下,Voilà 使用亮色主题,但您可以通过传入以下选项将主题设置为暗色
voila <path-to-notebook> --theme=dark
或者通过传入查询参数 theme
,例如,一个 URL 像 https://:8867/voila/render/query-strings.ipynb?theme=dark
。
主题也可以在笔记本元数据中设置,通过手动编辑笔记本文件,或使用经典笔记本中的元数据编辑器,在 metadata/voila/theme
下。

希望禁用更改主题的系统管理员可以传入 --VoilaConfiguration.allow_theme_override=NO
或 --VoilaConfiguration.allow_theme_override=NOTEBOOK
来完全禁用更改主题,或仅允许从笔记本元数据中更改。
与 nbconvert 类似,Voilà 默认支持亮色和暗色主题,但您也可以使用自定义 JupyterLab 主题
pip install jupyterlab_miami_nights
voila <path-to-notebook> --theme="JupyterLab Miami Nights"
注意
主题参数是 JupyterLab 中显示的主题名称,而不是 Python 包的名称。如果此功能未来迁移到 nbconvert,从笔记本元数据更改主题的方式可能会有所改变。
警告
主题是“lab”模板特有的,不适用于“classic”模板。自定义 JupyterLab 主题仅适用于默认树页面,经典树页面仅支持亮色和暗色主题。
控制 nbconvert 模板#
Voilà 使用 nbconvert 将您的 Jupyter Notebook 转换为 HTML 仪表板。nbconvert 拥有丰富的模板系统,允许您自定义 Jupyter Notebook 转换为 HTML 的方式。
默认情况下,Voilà 将以笔记本遵循的相同线性方式渲染笔记本中的 HTML。如果您想使用不同的布局,可以通过创建新的 nbconvert 模板、将其注册到 Voilà,并从命令行调用它来实现,如下所示
voila <path-to-notebook> --template=<name-of-template>
例如,Voilà 包含另一个模板,它使用 JavaScript 库和替代的 <div>
布局,以允许用户拖放单元格。
例如,要使用 gridstack 模板,请使用以下命令
voila <path-to-notebook> --template=gridstack
或者通过传入查询参数 template
,例如,一个 URL 像 https://:8867/voila/render/query-strings.ipynb?template=material
(请注意,这需要安装 voila-material)。
模板也可以在笔记本元数据中设置,通过手动编辑笔记本文件,或使用经典笔记本中的元数据编辑器,在 metadata/voila/template
下。

希望禁用更改主题的系统管理员可以传入 --VoilaConfiguration.allow_template_override=NO` 或 ``--VoilaConfiguration.allow_template_override=NOTEBOOK
来完全禁用更改主题,或仅允许从笔记本元数据中更改。
注意
如果此功能未来迁移到 nbconvert,从笔记本元数据更改模板的方式可能会有所改变。
警告
“classic”模板正在被弃用,并将在 Voilà 1.0.0 中移除支持,替代方案是使用 voila --template lab --show-margins
来获得类似的外观。
创建您自己的模板#
您可以创建自己的 nbconvert 模板以用于 Voilà。这允许您控制仪表板的外观和感觉。
为了创建您自己的模板,首先请熟悉 Jinja、HTML 和 CSS。这些都用于创建自定义模板。有关更多信息,请参阅nbconvert 模板文档。例如,查看 nbconvert 基本 HTML 模板。
一些 voila/nbconvert 模板项目示例如下
Voilà 模板位于何处?#
所有 Voilà 模板都以文件夹形式存储,其中包含特定的配置/模板文件。这些文件夹可以存在于标准的 Jupyter 配置位置,在一个名为 voila/templates
的文件夹中。例如
~/.local/share/jupyter/voila/templates
~/path/to/env/dev/share/jupyter/voila/templates
/usr/local/share/jupyter/voila/templates
/usr/share/jupyter/voila/templates
Voilà 将在这些位置搜索一个文件夹,每个模板一个,文件夹名称定义了模板名称。
Voilà 模板结构#
在每个模板文件夹中,您可以提供自己的 nbconvert 模板、静态文件和 HTML 模板(用于 404 错误等页面)。例如,这是 Voilà 基本模板(称为“default”)的文件夹结构
tree path/to/env/share/jupyter/voila/templates/default/
├── nbconvert_templates
│ ├── base.tpl
│ └── voila.tpl
└── templates
├── 404.html
├── error.html
├── page.html
└── tree.html
要自定义 nbconvert 模板,请将其存储在名为 templatename/nbconvert_templates/voila.tpl
的文件夹中。在默认模板的情况下,我们还提供了一个 base.tpl
作为我们自定义模板的基础。名称 voila.tpl
是特殊的 - 您不能将自定义 nbconvert 命名为其他名称。
要自定义 HTML 页面模板,请将其存储在名为 templatename/templates/<name>.html
的文件夹中。这些是 Voilà 可以作为独立 HTML 提供服务的文件(例如,tree.html
模板定义了如何在 localhost:8866/voila/tree
中显示文件夹/文件)。您可以通过提供自己的同名 HTML 文件来覆盖默认值。
要配置您的 Voilà 模板,您应该在模板文件夹的根目录添加一个 config.json
文件。
警告
自定义树页面模板仅适用于经典树页面。
自定义模板示例#
为了展示如何创建您自己的自定义模板,让我们创建一个自己的 nbconvert 模板。我们将有两个目标
向 Voilà 仪表板添加一个显示“我们的出色模板”的
<h1>
标题。添加一个显示图像的自定义 404.html 页面。
首先,我们将在 ~/.local/share/jupyter/voila/templates
中创建一个名为 mytemplate
的文件夹
mkdir ~/.local/share/jupyter/voila/templates/mytemplate
cd ~/.local/share/jupyter/voila/templates/mytemplate
接下来,我们将复制 Voilà 的基本模板文件,然后对其进行修改
cp -r path/to/env/share/jupyter/voila/templates/default/nbconvert_templates ./
cp -r path/to/env/share/jupyter/voila/templates/default/templates ./
我们现在应该有一个这样的文件夹结构
tree .
├── nbconvert_templates
│ ├── base.tpl
│ └── voila.tpl
└── templates
├── 404.html
├── error.html
├── page.html
└── tree.html
现在,我们将编辑 nbconvert_templates/voila.tpl
以包含一个自定义 H1 标题。
以及 templates/tree.html
以包含一张图片。
最后,我们可以通过在 --template
参数中使用文件夹名称来告诉 Voilà 下次使用 Jupyter notebook 时使用此自定义模板
voila mynotebook.ipynb --template=mytemplate
结果应该是一个带有您的自定义修改的 Voilà 仪表板!
使用 Hooks 自定义 Voila#
Voila 提供了 hooks,允许您自定义其行为以适应您的特定需求。这些 hooks 使您能够在 Voila 执行的某些点注入自定义函数,从而控制笔记本执行和前端配置等方面。
目前,Voila 支持以下 hooks
prelaunch_hook:在执行之前访问和修改 Tornado 请求和笔记本。
page_config_hook:自定义 page_config 对象,该对象控制 Voila 前端配置。
访问 Tornado 请求 (prelaunch-hook
)#
在某些自定义设置中,当您需要访问 Tornado 请求对象以检查身份验证 cookie、访问请求头详细信息或在渲染之前修改笔记本时。您可以利用 prelaunch-hook
,它允许您注入一个函数来检查笔记本和请求,然后再执行它们。
警告
因为 prelaunch-hook
仅在收到新请求后但在笔记本执行之前运行,所以它与 preheated kernels
不兼容。
创建 hook 函数#
此 hook 的格式应为
def prelaunch_hook(req: tornado.web.RequestHandler,
notebook: nbformat.NotebookNode,
cwd: str) -> Optional[nbformat.NotebookNode]:
第一个参数将是对 tornado
RequestHandler
的引用,您可以通过它检查参数、标头等。第二个参数将是
NotebookNode
,您可以对其进行修改,例如注入单元格或进行其他笔记本级别的修改。最后一个参数是当前工作目录,如果您需要在磁盘上修改任何内容。
您的 hook 函数的返回值可以是
None
,也可以是NotebookNode
。
自定义页面配置对象 (page_config_hook
)#
page_config_hook 允许您自定义 page_config 对象,该对象控制 Voila 前端的各个方面。当您需要修改前端设置(例如静态资产的 URL 或其他配置参数)时,这非常有用。
默认情况下,Voila 使用以下 page_config
# Default page_config
page_config = {
"appVersion": __version__,
"appUrl": "voila/",
"themesUrl": "/voila/api/themes",
"baseUrl": base_url,
"terminalsAvailable": False,
"fullStaticUrl": url_path_join(base_url, "voila/static"),
"fullLabextensionsUrl": url_path_join(base_url, "voila/labextensions"),
"extensionConfig": voila_configuration.extension_config,
}
此 hook 的格式应为
def page_config_hook(
current_page_config: Dict[str, Any],
base_url: str,
settings: Dict[str, Any],
log: Logger,
voila_configuration: VoilaConfiguration,
notebook_path: str
) -> Dict[str, Any]:
将 hook 函数添加到 Voilà#
有两种方法可以将 hook 函数添加到 Voilà
使用
voila.py
配置文件
这是配置文件的示例。此文件需要放在您启动 Voilà 的目录中。
def prelaunch_hook_function(req, notebook, cwd):
"""Do your stuffs here"""
return notebook
def page_config_hook_function(current_page_config, **kwargs):
"""Modify the current_page_config"""
return new_page_config
c.VoilaConfiguration.prelaunch_hook = hook_function
c.VoilaConfiguration.page_config_hook = page_config_hook
从 Python 脚本启动 Voilà
这是一个自定义 prelaunch-hook
的示例,用于使用 papermill
执行笔记本,以及一个 page_config_hook
用于添加自定义 labextensions URL
def parameterize_with_papermill(req, notebook, cwd):
import tornado
# Grab parameters
parameters = req.get_argument("parameters", {})
# try to convert to dict if not e.g. string/unicode
if not isinstance(parameters, dict):
try:
parameters = tornado.escape.json_decode(parameters)
except ValueError:
parameters = None
# if passed and a dict, use papermill to inject parameters
if parameters and isinstance(parameters, dict):
from papermill.parameterize import parameterize_notebook
# setup for papermill
#
# these two blocks are done
# to avoid triggering errors
# in papermill's notebook
# loading logic
for cell in notebook.cells:
if 'tags' not in cell.metadata:
cell.metadata.tags = []
if "papermill" not in notebook.metadata:
notebook.metadata.papermill = {}
# Parameterize with papermill
return parameterize_notebook(notebook, parameters)
def page_config_hook(
current_page_config: Dict[str, Any],
base_url: str,
settings: Dict[str, Any],
log: Logger,
voila_configuration: VoilaConfiguration,
notebook_path: str
):
page_config['fullLabextensionsUrl'] = '/custom/labextensions_url'
return page_config
您可以同时使用这两个 hooks 来自定义笔记本执行和前端配置,将这些 hooks 添加到您的 Voilà
应用程序中
from voila.app import Voila
from voila.config import VoilaConfiguration
# customize config how you like
config = VoilaConfiguration()
# set the prelaunch hook
config.prelaunch_hook = parameterize_with_papermill
# set the page config hook
config.page_config_hook = page_config_hook
# create a voila instance
app = Voila()
# set the config
app.voila_configuration = config
# launch
app.start()
添加您自己的静态文件#
如果您创建自己的主题,您可能还希望定义和使用自己的静态文件,例如 CSS 和 JavaScript。要使用您自己的静态文件,请按照以下步骤操作
在您的模板旁边创建一个文件夹(例如,
mytemplate/static/
)。将您的静态文件放入此模板中。
在您的模板文件(例如
voila.tpl
)中,使用以下路径链接这些静态文件{{resources.base_url}}voila/static/<path-to-static-files>
当您调用
voila
时,通过使用--static
kwarg 或配置--VoilaConfiguration.static_root
来配置静态文件夹。
此配置中给定文件夹内的所有文件夹/文件都将复制到 {{resources.base_url}}voila/static/
。
例如,如果您在 static/css
中有一个名为 custom.css
的 CSS 文件,您将在模板中像这样链接它
<link rel="stylesheet" type="text/css" href="{{resources.base_url}}voila/static/css/custom.css"></link>
为 Jupyter Server 配置 Voilà#
当 voila
运行时,其功能的几个部分可以被控制。这既可以作为独立 CLI 的一部分完成,也可以与 Jupyter Server 一起完成。要配置 voila
在 Jupyter Server 运行时,请在调用运行 Jupyter 的命令(例如 Jupyter Lab 或 Jupyter Notebook)时使用以下模式
<jupyter-command> --VoilaConfiguration.<config-key>=<config-value>
例如,要从 Jupyter Lab 会话中控制 voila
使用的模板,请在启动服务器时使用以下命令
jupyter lab --VoilaConfiguration.template=distill
当用户通过访问 voila/
端点运行 voila
时,将使用此配置。
激活令牌认证#
通过使用 jupyter-server
2,Voilà 支持令牌认证,但默认禁用。
要使用自动生成的令牌启动 Voila
voila --token notebook.ipynb
要使用个性化令牌启动 Voila
voila --token=my-secret-token notebook.ipynb
提供静态文件#
与 JupyterLab 或经典笔记本服务器不同,voila
不提供笔记本目录中存在的所有文件。只有与允许列表中的一个正则表达式匹配且不与拒绝列表中的任何一个正则表达式匹配的文件才由 Voilà 提供
voila mydir --VoilaConfiguration.file_allowlist="['.*']" \
--VoilaConfiguration.file_denylist="['private.*', '.*\.(ipynb)']"
这将提供所有文件,除了以 private 开头的文件或笔记本文件
voila mydir --VoilaConfiguration.file_allowlist="['.*\.(png|jpg|gif|svg|mp4|avi|ogg)']"
将提供许多媒体文件,并且永远不会提供笔记本文件(这是默认的拒绝列表)。
运行脚本#
Voilà 可以通过配置文件扩展名如何映射到内核语言来运行文本(或脚本)文件
voila mydir --VoilaConfiguration.extension_language_mapping='{".py": "python", ".jl": "julia"}'
Voilà 将找到与指定语言匹配的内核,但也可以配置为每种语言使用特定的内核
voila mydir --VoilaConfiguration.extension_language_mapping='{".py": "python", ".jl": "julia"}'\
--VoilaConfiguration.language_kernel_mapping='{"python": "xpython"}'
在这种情况下,它将使用 xeus-python 内核来运行 .py
文件。
请注意,脚本将作为只有一个单元格的笔记本执行,这意味着只有最后一个表达式将作为输出打印。使用 Jupyter 显示机制输出任何文本或富输出,例如 Jupyter 小部件。对于 Python,这将是对 IPython.display.display
的调用。
使用 Jupytext 是支持脚本文件的另一种方式。安装 jupytext 后,Voilà 将把脚本文件视为笔记本,无需额外配置。
淘汰空闲内核#
每次将笔记本渲染给用户时,Voilà 都会启动一个新的 Jupyter 内核。在某些情况下,这可能导致更高的内存消耗。
Jupyter Server 提供了几个选项,可用于终止不再活动的内核。它们可以使用 Voilà 独立应用程序进行配置
voila --MappingKernelManager.cull_interval=60 --MappingKernelManager.cull_idle_timeout=120
服务器将定期检查空闲内核,在此示例中每 60 秒检查一次,如果内核已空闲超过 120 秒,则将其淘汰。
当 Voilà 作为服务器扩展使用时,也适用相同的参数
jupyter notebook --MappingKernelManager.cull_interval=60 --MappingKernelManager.cull_idle_timeout=120
还有 MappingKernelManager.cull_busy
和 MappingKernelManager.cull_connected
选项,用于淘汰繁忙的内核和具有活动连接的内核。
有关这些选项的更多信息,请查看 Jupyter Server 文档。
预热内核#
由于 Voilà 需要为每个连接启动一个新的 jupyter 内核并在该内核中执行请求的笔记本,这将导致在浏览器中显示小部件之前等待时间较长。为了减少此等待时间,尤其是对于大型笔记本,用户可以激活 Voilà 的预热内核选项。
警告
由于预热内核不会根据请求执行,此功能与 prelaunch-hook
功能不兼容。
此选项将启用两个功能
为每个笔记本启动一个内核池并保持待机状态,然后将笔记本在池中的每个内核中执行。当新客户端请求内核时,将使用此池中预热的内核,并异步启动另一个内核以补充池。
笔记本的 HTML 版本在每个预热的内核中渲染并存储,当客户端连接到 Voilà 时,在某些条件下,将提供缓存的 HTML 而不是重新渲染笔记本。
预热内核选项适用于任何内核管理器,默认情况下处于停用状态,通过设置 preheat_kernel = True
重新激活它。例如,使用此命令,对于 Voilà 启动的每个笔记本,将创建一个包含 5 个内核的池,并将其用于新连接。
voila --preheat_kernel=True --pool_size=5
预热内核的默认环境变量可以通过 VoilaKernelManager.default_env_variables
设置。例如,此命令
voila --preheat_kernel=True --VoilaKernelManager.default_env_variables='{"FOO": "BAR"}'
将在所有预热的内核中设置变量“FOO”。
如果池大小不符合用户要求,或者某些笔记本需要使用特定的环境变量...,则需要额外的设置。更改这些设置的最简单方法是在启动 Voilà 的目录中提供一个名为 voila.json
的文件。预热内核的设置(不需要预热内核的笔记本列表、池中内核数量、填充延迟、启动内核的环境变量等)可以在 VoilaKernelManager
类名下设置。
这是一个包含预热内核选项解释的设置示例。
# voila.json
{
"VoilaConfiguration": {
# Activate or deactivate preheat kernel option.
"preheat_kernel": true
},
"VoilaKernelManager": {
# A list of notebook name or regex patterns to exclude notebooks from using preheat kernel.
"preheat_denylist": [
"notebook-does-not-need-preheat.ipynb",
"^.*foo.*$",
...
],
# Configuration for kernel pools
"kernel_pools_config": {
# Setting for `voila.ipynb` notebook
"voila.ipynb": {
"pool_size": 3, # Size of pool
"kernel_env_variables": { # The environment variables used to start kernel for `voila.ipynb`
"foo2": "bar2"
}
},
# Setting for `test/sub-voila.ipynb` notebook
"test/sub-voila.ipynb": {
"pool_size": 1
},
...
# If a notebook does not have setting, it will use default setting
"default": {
"pool_size": 2,
"kernel_env_variables": {
"foo": "bar"
}
},
},
# Delay time in second before filling the kernel pool.
"fill_delay": 0
}
}
笔记本 HTML 将使用 VoilaConfiguration 或笔记本元数据中定义的模板和主题进行预渲染。如果满足以下条件,将使用预热的内核和缓存的 HTML
内核池中有可用的预热内核。
如果用户使用查询字符串覆盖模板/主题,它必须与用于预渲染笔记本的模板/主题匹配。
如果内核池为空或请求不符合这些条件,Voilà 将回退到启动正常内核并照常渲染笔记本。
部分预渲染笔记本#
为了受益于预热内核模式的加速,笔记本需要在用户实际连接到 Voilà 之前进行预渲染。但在许多实际情况下,笔记本需要一些用户特定数据才能正确渲染小部件,这使得预渲染成为不可能。为了克服这个限制,Voilà 提供了一个功能来处理提供用户数据最常用的方法:URL query string
。
注意
有关与 tornado 请求对象进行更高级交互的信息,请参阅 prelaunch-hook
功能。
在正常模式下,Voilà 用户可以通过 QUERY_STRING
环境变量在运行时获取 query string
import os
query_string = os.getenv('QUERY_STRING')
在预热内核模式下,用户可以从 voila.utils
中预置 wait_for_request
import os
from voila.utils import wait_for_request
wait_for_request()
query_string = os.getenv('QUERY_STRING')
wait_for_request
将在此单元格暂停预热内核中笔记本的执行,并等待实际用户连接到 Voilà,设置请求信息环境变量,然后继续执行剩余单元格。
如果 Voilà websocket 处理器未以默认协议(ws
)、默认 IP 地址(127.0.0.1
)默认端口(8866
)或带 URL 后缀启动,用户需要通过环境变量 VOILA_WS_PROTOCOL
、VOILA_APP_IP
、VOILA_APP_PORT
和 VOILA_WS_BASE_URL
提供这些值。设置这些变量的一种方法是在 voila.json
配置文件中,例如
# voila.json
{
...
"VoilaKernelManager": {
"kernel_pools_config": {
"foo.ipynb": {
"kernel_env_variables": {
"VOILA_APP_IP": "192.168.1.1",
"VOILA_APP_PORT": "6789",
"VOILA_WS_PROTOCOL": "wss"
}
}
},
...
}
}
此外,您还可以使用以下命令设置这些值
voila --preheat_kernel=True --VoilaKernelManager.default_env_variables='{"VOILA_WS_PROTOCOL":"wss","VOILA_APP_IP":"192.168.1.1"}'
单元格执行超时#
默认情况下,Voilà 没有执行超时,这意味着 Voilà 执行和渲染笔记本所需的时间没有限制。如果您有潜在的长时间运行单元格,您可能希望设置单元格执行超时,以便如果执行笔记本所需时间超出预期,您的仪表板用户将收到错误。例如
voila --VoilaExecutor.timeout=30 your_notebook.ipynb
有了此设置,如果任何单元格运行时间超过 30 秒,将引发 TimeoutError
。您可以使用 VoilaExecutor.timeout_func
和 VoilaExecutor.interrupt_on_timeout
选项进一步自定义此行为。
自定义 Voilà 预览小部件#
通过使用 JupyterLab 的布局自定义系统,用户可以配置 Voilà 预览小部件的位置,以便将其打开到 main
以外的其他区域。
Voila Preview
是预览小部件的设置键。例如,以下配置将在 JupyterLab 的右侧面板中打开此小部件
"layout": {
"multiple": {
"Voila Preview": { "area": "right" }
}
}
自定义 kernel_spec_manager 类#
默认情况下,Voilà 使用 jupyter_client
的 KernelSpecManager。要更改此类别,请按如下方式将其添加到您的配置中
import CustomKernelSpecManager
c.VoilaConfiguration.kernel_spec_manager_class = CustomKernelSpecManager
内核 startup_timeout#
默认情况下,Voilà 的内核启动时间宽限期为 60 秒。可以按秒进行调整
voila --VoilaExecutor.startup_timeout=60
让 voila 尝试解决最适合的内核规范#
默认情况下,Voilà 将尝试根据可用环境解析出最合适的内核规范。您可以按如下方式禁用此功能
c.VoilaConfiguration.attempt_fix_notebook = False
更改仪表板渲染技术#
Voilà 中有两种仪表板渲染技术
阻塞渲染(默认):在这种方法中,Voilà 在显示仪表板之前完成整个笔记本的执行。它非常适合轻量级笔记本,因为在执行完成之前会显示加载旋转器。
渐进式渲染(Voilà 0.6 中引入):使用此方法,仪表板会立即出现,单元格输出填充占位符。这些输出会随着每个单元格被内核执行而更新。
要使用 CLI 启动渐进式渲染模式的 Voilà
voila ... --progressive_rendering=True
或使用 voila.json
文件
# voila.json
{
...
"VoilaConfiguration": {
"progressive_rendering": true,
...
}
}
警告
渐进式渲染模式与预热内核功能不兼容。
Voila 的自定义 labextension#
自 Voila 0.5.0 起,它现在可以加载 JupyterLab 扩展。允许加载自定义 mimetype 扩展、JupyterLab 主题等。
Voila 将加载 {PREFIX}/share/jupyter/labextensions
下的所有扩展,就像 JupyterLab 和 Jupyter Notebook v7 一样。
它也有自己的 labextensions 路径,您可以用于:{PREFIX}/share/jupyter/voila/labextensions
。在那里安装扩展将使这些扩展仅对 Voila 可用。如果您不想用仅适用于 Voila 的扩展污染 JupyterLab 或 Jupyter Notebook,这非常有用。