自定义 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://127.0.0.1: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://127.0.0.1:8867/voila/render/query-strings.ipynb?template=material
(注意,这需要安装 voila-material)。
模板也可以在笔记本元数据中设置,在 metadata/voila/template
下,通过手动编辑笔记本文件,或使用例如经典笔记本中的元数据编辑器。
想要禁用更改主题的系统管理员可以传递 --VoilaConfiguration.allow_template_override=NO` or ``--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
以包含图像。
最后,我们可以告诉 Voilà 在下次我们在 Jupyter 笔记本上使用它时使用此自定义模板,方法是在 --template
参数中使用文件夹的名称
voila mynotebook.ipynb --template=mytemplate
结果应该是带有您自定义修改的 Voilà 仪表板!
访问 tornado 请求(prelaunch-hook
)#
在某些自定义设置中,当您需要访问 tornado 请求对象以检查身份验证 cookie、访问有关请求标头的详细信息或在渲染之前修改笔记本时。您可以利用 prelaunch-hook
,它允许您注入一个函数来在执行它们之前检查笔记本和请求。
警告
因为 prelaunch-hook
仅在收到新请求后但在笔记本执行之前运行,它与 preheated kernels
不兼容。
创建钩子函数#
此钩子的格式应为
def hook(req: tornado.web.RequestHandler,
notebook: nbformat.NotebookNode,
cwd: str) -> Optional[nbformat.NotebookNode]:
第一个参数将是对 tornado
RequestHandler
的引用,您可以使用它来检查参数、标头等。第二个参数将是
NotebookNode
,您可以对其进行修改,例如注入单元格或进行其他笔记本级修改。最后一个参数是当前工作目录,如果您需要修改磁盘上的任何内容。
钩子函数的返回值可以是
None
,也可以是NotebookNode
。
将钩子函数添加到 Voilà#
有两种方法可以将钩子函数添加到 Voilà
使用
voila.py
配置文件
以下是一个配置文件示例。此文件需要放置在您启动 Voilà 的目录中。
def hook_function(req, notebook, cwd):
"""Do your stuffs here"""
return notebook
c.Voila.prelaunch_hook = hook_function
从 Python 脚本启动 Voilà
以下是一个使用 papermill
执行笔记本的自定义 prelaunch-hook
示例
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)
要将此钩子添加到您的 Voilà
应用程序中
from voila.app import Voila
from voila.config import VoilaConfiguration
# customize config how you like
config = VoilaConfiguration()
# create a voila instance
app = Voila()
# set the config
app.voila_configuration = config
# set the prelaunch hook
app.prelaunch_hook = parameterize_with_papermill
# 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”。
如果池的大小不符合用户的要求,或者一些笔记本需要使用特定的环境变量……,则需要额外的设置。更改这些设置的最简单方法是在包含笔记本的同一文件夹中提供一个名为 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
。
注意
有关与龙卷风请求对象的更高级交互,请参阅 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" }
}
}