跳转到主要内容

为pyodide提供中断处理。

项目描述

pyodide_interrupts

这是一个允许在Pyodide内部处理中断的包。Pyodide没有抢占式多任务处理。此包可启用在Pyodide中处理键盘中断。

它定义了一个上下文处理程序 check_interrupts(callback, interval),该处理程序会在每 interval 条指令后调用 callback

简单示例

>>> def callback(): print("check")
... with check_interrupts(callback, 10):
...    for i in range(50):
...       print(i, end=",")

0,1,check
2,3,4,5,6,check
7,8,9,10,11,check
12,13,14,15,16,check
17,18,19,20,21,check
22,23,24,25,26,check
27,28,29,30,31,check
32,33,34,35,36,check
37,38,39,40,41,check
42,43,44,45,46,check
47,48,49,check

使用草图

在实际使用中,我使用了以下回调

def check_for_interrupt(interrupt_buffer):
    def helper():
        if interrupt_buffer() == 0:
            return
        raise KeyboardInterrupt()
    return helper

interrupt_buffer 是一个围绕 SharedArrayBuffer 的 JavaScript 包装器。在主线程上

let uuid = uuid();
let interrupt_buffer = new Int32Array(new SharedArrayBuffer(4));
pyodide_worker.postMessage({"cmd" : "execute_python", code, interrupt_buffer, uuid});
let result = await responsePromise(uuid);
// If user cancels, write a nonzero value into our SAB, this will signal pyodide to quit execution of code.
onUserCancel(() => { interrupt_buffer[0] = 2; });

在pyodide工作线程上

self.messages = {};
function handleExecutePython(msg){
    // Wrap interrupt buffer in a function that gets its value
    // Pyodide Python <==> Javascript bindings don't understand how to get values out of the SAB directly.
    msg.interrupt_buffer = function(){
        return msg.interrupt_buffer[0]; 
    };
    messages[msg.uuid] = msg;
    self.pyodide.globals["handle_message"](uuid);
}

然后是pyodide代码

from js import messages, postMessage
def handle_message(uuid):
    msg = dict(messages[uuid])
    del messages[uuid]
    # Here would use msg["cmd"] to look up handling in a dispatch.
    interrupt_buffer = msg["interrupt_buffer"]
    # check_for_interrupt will raise a KeyboardInterrupt if "onUserCancel" handler is executed on main thread.
    with check_interrupts(check_for_interrupt(interrupt_buffer), 10_000):
        result = run_code(code)
    postMessage({"cmd" : "execute_pyodide_result", "result" : result, "uuid" : uuid })

def run_code(code):
    # Parse code into ast, handle errors, get result out, etc here

SharedArrayBuffer 运作的安全要求

我引用了来自 MDN关于SharedArrayBuffer的文档

作为基本要求,您的文档需要在安全上下文中。

对于顶级文档,需要设置两个标题以实现跨源隔离您的站点

Cross-Origin-Opener-Policy的值为same-origin(保护您的源免受攻击者侵害)Cross-Origin-Embedder-Policy的值为require-corp(保护受害者免受您的源侵害)

Cross-Origin-Opener-Policy: same-origin Cross-Origin-Embedder-Policy: require-corp

要检查跨源隔离是否成功,您可以通过测试window和worker上下文中可用的crossOriginIsolated属性来进行

构建

为了构建本地使用的副本,我建议创建一个虚拟环境,然后在该虚拟环境中使用 pip install .。为了上传到 PyPI,我们必须构建适用于 manylinux ABI 的包以确保二进制文件与大多数系统兼容。许多 manylinux 存储库为我们提供了带有合适旧版本 CentOS 的 Docker 镜像,我们可以使用这些镜像来构建这些包。要构建,运行 sudo ./docker_build_wheels.sh注意:第一次运行时,这将下载一个 ~300mb 的 Docker 镜像。请注意,您需要安装 Docker 才能使用此功能。生成的 wheel 文件将位于 dist 目录中,并适合上传到 PyPI。

[0.1.0] (2020-07-25)

项目详情


下载文件

下载您平台上的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源分布

pyodide-interrupts-0.1.1.tar.gz (4.9 kB 查看哈希)

上传时间

构建分布

pyodide_interrupts-0.1.1-cp39-cp39-manylinux1_x86_64.whl (16.3 kB 查看哈希)

上传时间 CPython 3.9

pyodide_interrupts-0.1.1-cp38-cp38-manylinux1_x86_64.whl (15.6 kB 查看哈希)

上传时间 CPython 3.8

pyodide_interrupts-0.1.1-cp37-cp37m-manylinux1_x86_64.whl (14.9 kB 查看哈希)

上传时间 CPython 3.7m

pyodide_interrupts-0.1.1-cp36-cp36m-manylinux1_x86_64.whl (14.9 kB 查看哈希)

上传时间 CPython 3.6m

pyodide_interrupts-0.1.1-cp35-cp35m-manylinux1_x86_64.whl (14.8 kB 查看哈希)

上传时间 CPython 3.5m

由以下支持