为 Jupyter 制作内核¶
‘内核’ 是一个运行并自省用户代码的程序。IPython 包括一个用于 Python 代码的内核,人们已经为 其他几种语言编写了内核。
在内核启动时,Jupyter 向内核传递一个连接文件。这指定了如何设置与前端的通信。
编写内核有三种选择:
你可以重用 IPython 内核机制来处理通信,而只需描述如何执行你的代码。如果目标语言可以由 Python 驱动,这就简单多了。详情见 制作简单的 Python 包装器内核。
你可以用你的目标语言实现内核机制。这在最初是比较麻烦的,但是如果使用你的内核的人是用他们熟悉的语言的话,他们可能会更愿意为你的内核做出贡献。
你可以使用 xeus 库,它是 Jupyter 内核协议的 C++ 实现。内核作者只需要在他们的实现中实现特定语言的逻辑(执行代码、自动补全 。。。。。。)。如果你的目标语言可以从 C 或 C++ 驱动,这是最简单的解决方案: 例如,如果它像大多数脚本语言一样有一个 C-API。请查看 xeus 文档 以了解更多细节。基于 xeus 的内核的例子包括:
连接文件¶
你的内核在启动时将得到一个连接文件的路径(参见 内核规范 了解如何为你的内核指定命令行参数)。这个文件只有当前用户可以访问,它将包含一个 JSON 字典,看起来像这样
{
"control_port": 50160,
"shell_port": 57503,
"transport": "tcp",
"signature_scheme": "hmac-sha256",
"stdin_port": 52597,
"hb_port": 42540,
"ip": "127.0.0.1",
"iopub_port": 40885,
"key": "a0436f6c-1916-498b-8eb9-e81ab9368e84"
}
transport
、ip
和五个 _port
字段指定了五个端口,内核应该使用 ZeroMQ 绑定。例如,在上面的例子中,shell 套接字的地址是
tcp://127.0.0.1:57503
对于每个启动的内核,新的端口是随机选择的。
signature_scheme
和 key
是用来对信息进行加密签名的,这样系统中的其他用户就不能发送代码到这个内核中运行。请参阅 Wire 协议,了解如何计算这个签名的细节。
处理消息¶
在读取连接文件并绑定到必要的套接字之后,内核应该进入一个事件循环,监听 hb(heartbeat)、control 和 shell 套接字。
Heartbeat 消息应立即在同一套接字上回显 —— 前端使用它来检查内核是否仍然存在。
control 和 shell 套接字上的信息应该被解析,并验证其签名。请参阅 Wire 协议 了解如何做到这一点。
内核将在 iopub 套接字上发送消息以显示输出,并在 stdin 套接字上发送消息以提示用户输入文本。
参见
- Jupyter 中的消息传递
不同的套接字和通过它们传递的消息的细节
- 为 IPython 创建语言内核
IHaskell 的作者的博文,一个 Haskell 内核
- simple_kernel
用 Python 实现内核机制的一个简单例子
内核规范¶
一个内核通过创建一个目录来向 IPython 标识自己,该目录的名称被用作内核的标识符。这些目录可以在很多地方创建:
Unix |
Windows |
|
---|---|---|
系统 |
|
|
环境 |
|
|
用户 |
|
|
用户位置优先于系统位置,名字的大小写被忽略,所以无论文件系统是否区分大小写,选择内核的方式都是一样的。由于 kernelspecs 出现在 URL 和其他地方,kernelspec 被要求有一个简单的名字,只包含 ASCII 字母、ASCII 数字和简单的分隔符:-
连字符,.
句点,_
下划线。
如果设置了 JUPYTER_PATH
环境变量,也可以搜索其他位置。
在内核目录内,目前有三种类型的文件被使用: kernel.json
、kernel.js
和 logo 图片文件。目前,没有使用其他文件,但将来可能会改变。
在该目录内,最重要的文件是 kernel.json。这应该是一个 JSON 序列化的字典,包含以下键和值:
argv:一个用于启动内核的命令行参数列表。任何参数中的文本
{connection_file}
将被替换成连接文件的路径。display_name: 内核的名字,它理应显示在 UI 上。与 API 中使用的内核名称不同,它可以包含任意的 unicode 字符。
language:内核的语言名称。当加载笔记本时,如果没有找到匹配的 kernelspec 键(可能在不同的机器上有所不同),将使用具有匹配的
language
的内核。这允许在任何 Python 或 Julia 内核上编写的笔记本与用户的 Python 或 Julia 内核正确关联,即使它们与作者的内核不在同一名称下。interrupt_mode (可选):可以是
signal
或message
,指定客户如何中断该内核上的单元执行,可以通过操作系统的信号设施(例如 POSIX 系统上的 SIGINT)发送一个中断signal
,或者在控制通道上发送一个interrupt_request
消息(见 Kernel interrupt)。如果没有指定,客户端将默认为signal
模式”env (可选):一个为内核设置的环境变量字典。这些将在内核启动前被添加到当前的环境变量中。现有的环境变量可以用
${<ENV_VAR>}
来引用,并将被替换成相应的值。管理员应该注意,使用${<ENV_VAR>}
可能会暴露敏感变量,应该只在受控情况下使用。metadata (可选):关于这个内核的额外属性的字典;被客户用来帮助选择内核。在这里添加的元数据应该为读取和写入该元数据的工具命名。
例如,IPython 的 kernel.json 文件是这样的
{
"argv": ["python3", "-m", "IPython.kernel",
"-f", "{connection_file}"],
"display_name": "Python 3",
"language": "python"
}
要查看可用的内核规格,运行
jupyter kernelspec list
用一个特定的内核来启动终端控制台或 Qt 控制台
jupyter console --kernel bash
jupyter qtconsole --kernel bash
notebook 在 ‘New’ 按钮的下拉菜单中为你提供可用的内核。