Sage for Windows 运行在一个名为 Cygwin 的 UNIX 模拟环境下。查看 sagemath/kernel.json,它包含:
{"display_name": "SageMath 8.2", "argv": ["/opt/sagemath-8.2/local/bin/sage", "--python", "-m", "sage.repl.ipython_kernel", "-f", "{connection_file}"]}
您可以在此处看到,它具有指向
sage
可执行文件的UNIX风格路径。这个路径只对在Sage的Cygwin环境下运行的其他程序有意义,对于本机Windows程序来说是无意义的。仅仅将其转换为等效的Windows路径也不起作用,因为
bin/sage
实际上是一个shell脚本。至少需要提供一个
Windows路径到Cygwin中带有的
bash
,并将
UNIX路径传递给
sage
可执行文件(与上面的路径相同)。如果没有登录shell,大多数所需的环境变量也不会设置,因此您可能需要使用
bash -l
。
因此,类似于以下内容:
{"display_name": "SageMath 8.2", "argv": ["C:\\Program Files\\SageMath 8.2\\runtime\\bin\\bash.exe", "-l", "/opt/sagemath-8.2/local/bin/sage", "--python", "-m", "sage.repl.ipython_kernel", "-f", "{connection_file}"]}
< p > < em >可能有效。 我不确定的一件事是
{connection_file}
参数是否会被正确处理。我还没有测试过。
更新:事实上,上述方法部分有效,但存在一些问题:传递的{connection_file}
参数是文件的绝对Windows路径。虽然Cygwin通常可以透明地将Windows路径转换为相应的UNIX路径,但这存在一个已知问题,即Cygwin上的Python的os.path
模块无法很好地处理Windows风格的路径,这会导致问题。
我遇到的另一个主要问题是
IPKernelApp
,这个类驱动通用的Jupyter内核,有一个线程来轮询内核的父进程(在本例中为笔记本服务器)是否已退出,以便在父进程关闭时适当地关闭内核。这就是内核如何知道在您关闭笔记本服务器时自动关闭的方式。
这是根据平台的不同方式完成的--Windows与UNIX-like。由于Sage的内核在Cygwin中运行,它选择了UNIX-like的轮询程序。然而,如果笔记本服务器恰好是本机Windows进程,比如在Windows本机Jupyter中运行Sage内核时,这是错误的。令人惊讶的是,
Windows的父级轮询程序 可以 在Cygwin上正常工作,因为它通过
ctypes
访问Windows API。因此,可以通过提供一个包装器给
IPKernelApp
来解决这个问题,该包装器强制使用
ParentPollerWindows
。
可能的解决方案看起来像这样:从SageMath Shell中执行:
$ cd "$SAGE_LOCAL"
$ mkdir -p ./share/jupyter/kernels/sagemath
$ cd ./share/jupyter/kernels/sagemath
$ cat <<_EOF_ > kernel-wrapper.sh
#!/bin/sh
here="$(dirname "$0")"
connection_file="$(cygpath -u -a "$1")"
exec /opt/sagemath-8.2/local/bin/sage --python "${here}/kernel-wrapper.py" -f "${connection_file}"
_EOF_
$ cat <<_EOF_ > kernel-wrapper.py
from ipykernel.kernelapp import IPKernelApp as OrigIPKernelApp
from ipykernel.parentpoller import ParentPollerWindows
from sage.repl.ipython_kernel.kernel import SageKernel
class IPKernelApp(OrigIPKernelApp):
"""
Although this kernel runs under Cygwin, its parent is a native Windows
process, so we force use of the ParentPollerWindows.
"""
def init_poller(self):
if self.interrupt or self.parent_handle:
self.poller = ParentPollerWindows(self.interrupt,
self.parent_handle)
IPKernelApp.launch_instance(kernel_class=SageKernel)
_EOF_
现在编辑
share\jupyter\kernels\sagemath
下的
kernel.json
文件,使其内容如下:
{"display_name": "SageMath 8.2", "argv": ["C:\\Program Files\\SageMath 8.2\\runtime\\bin\\bash.exe", "-l", "/opt/sagemath-8.2/local/share/jupyter/kernels/sagemath/kernel-wrapper.sh", "{connection_file}"]}
这里运行了kernel-wrapper.sh
,然后再运行kernel-wrapper.py
。(有一些简化可以消除完全需要kernel-wrapper.sh
的情况,但在包括PyCygwin的SageMath 8.3中会更容易。)
请确保将每个“8.2”更改为您Sage安装的适当“X.Y”版本。
更新:根据用户反馈进行了一些更新,但我还没有测试这些更改,因此请确保不要盲目复制/粘贴我的说明中的每个文件/目录路径都存在并且正确。
正如您所看到的,这并不是微不足道的,也不是通过设计意图可能实现的。但它可以完成。一旦内核本身启动并运行,就只需要通过TCP / IP套接字与其通信,因此在此之后没有太多魔法。我相信在Jupyter端和Sage端都可以进行一些小的改进,以促进将来这种事情的实现...
{connection_file}
没有被正确传递。让我看看是否能想到一个解决方法... - Iguananautjupyter run
传递命令给它,它就可以工作。所以也许还存在其他关于进程管理的混淆问题... - Iguananaut