- 前端
- 开发工具
【免费下载链接】pyscript
An open source platform for Python in the browser. https://pyscript.net Docs: https://docs.pyscript.net/ Try it: https://pyscript.com/ Community: https://discord.gg/HxvBtukrg2
PyScript Bridge(npm 包名@pyscript/bridge,位于仓库 bridge/ 目录)是 PyScript 生态中面向纯 JavaScript 开发者的轻量桥接层:它允许你在原生 JS 模块中import一个与.py文件"互为化身"的模块,并直接await调用其中的 Python 函数。本文将以 bridge/README.md 为骨架,结合 bridge/index.js 的源码实现与 bridge/test/ 测试用例,讲解其核心用法、全部配置项、底层原理与本地/远程测试方法,读完即可在自己的页面中把 Python 工具函数接入 JS 调用链。
一、核心概念:.js文件与它的.py化身
Bridge 的基本设计是同名配对:你维护一个test.js,同时在相同目录下维护一个test.py,JS 中导出一个由bridge(import.meta.url, options)生成的代理对象,代理对象上的任意属性(如func_a、func_b)都会在首次被访问时,被翻译成对test.py中同名函数的异步调用。
README 中最简示例(主线程场景)如下:
// main thread const { ffi: { func_a, func_b } } = await import('./test.js');其中test.js通过 ESM CDN 引入 bridge 并绑定自己的模块地址:
// test.js import bridge from 'https://esm.run/@pyscript/bridge'; export const ffi = bridge(import.meta.url, { type: 'mpy', worker: false });对应的test.py只需是普通 Python 模块:
# test.py def func_a(value): print(f"hello {value}") def func_b(): import sys return sys.version调用时,await func_a("world")会在 Python 侧打印hello world,await func_b()会返回 Python 解释器的sys.version字符串。注意:bridge()的返回值是异步代理,导出的字段只有在被访问(get)时才触发一次性的"引导 + 执行"流程,并且同一字段多次调用只初始化一次解释环境。
二、选项(Options)全解
bridge(url, options)的第二个参数支持以下选项,默认值以源码 bridge/index.js 与 README 为准:
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
pyscript | string | null | 要自动加载的 PyScript 发布版本号(如"2025.8.1")。若页面中尚未存在@pyscript/core,bridge 会按该版本从 PyScript releases 拉取core.css与core.js;若未指定版本,将回退到开发者频道(developers' channel)的构建产物,仅供开发者内部调试使用(见 bridge/index.js 的 CDN 分支)。 |
type | "py" \| "mpy" | "py" | 解释器类型。py引导 Pyodide,mpy引导 MicroPython。 |
worker | boolean | true | 是否在 Web Worker 中引导解释器。为true时隔离主线程,避免阻塞 UI;测试页test.js默认显式传false以便在主线程观察行为。 |
config | string \| Object | null | 字符串(配置文件 URL,会被fetch后解析为 JSON)或 PyScript 兼容的 JS 字面量配置对象。可用于引导额外文件(files)等。一旦指定config,worker会被隐式置为true,避免在主线程上出现多份配置互相冲突。 |
env | string | null | 共享环境标识。多个在不同时间加载的模块传入相同env,即可复用同一 Python 环境(解释器与全局状态)。 |
serviceWorker | string | null | 可选 Service Worker 作为回退通道(源码支持,见 bridge/index.js,对应属性service-worker)。 |
其中"config隐式开启 worker"在实现中被明确落实:bridge/index.js 使用script.toggleAttribute('worker', !!config || !!worker),只要有config就强制挂上worker属性;core/src/config.js 中script[type][config]:not([worker])被判定为配置冲突(Ambiguous config VS config attribute/Unable to use different configs on main),从主线程解析侧印证了这一设计约束。
2.1config的 URL 规范化
源码中的normalize()(bridge/index.js)负责把配置处理成 JSON 字符串:
- 若
config是字符串,则视为 URL,fetch后解析为 JSON; - 若配置里含
files对象,则把相对路径基于模块文件 URL 解析为绝对 URL(new URL(key, base)),以{开头的键保持原样; - 最终序列化为 JSON 字符串写入
<script>的config属性。
2.2env与多模块共享环境
env选项最终被写成 script 的env属性(bridge/index.js)。它的典型场景是:页面中先后 import 多个 bridge 模块,只要传入同一个env值,它们就共享同一个 Python 运行时环境,避免重复引导解释器、重复加载依赖包。
三、底层原理:三级缓存、代理与事件握手
虽然bridge()用起来只是一个函数,但 bridge/index.js 内部实现了一整套"懒加载"机制,值得了解:
- 第一级缓存(按文件缓存代理):
cache以协议+主机+路径转换出的.py文件 URL(pathname.replace(/\.m?js(?:\/\+\w+)?$/, '.py'))为键,每个.py文件全局只生成一个Proxy实例。 - 第二级缓存(按字段缓存回调):代理的
get拦截器为每个被访问的字段创建一个唯一的异步回调,后续访问直接复用,Promise只建一次。 - 第三级缓存(按引导惰性加载):字段首次被调用时才
fetch对应.py源码,并用Date.now()拼出唯一上下文标识__pyscript_<模块名><时间戳>,把 Python 代码与以下尾缀拼接后,注入一个<script type="py|mpy">元素:
from pyscript import window as <唯一名> from pyscript.ffi import to_js as <唯一名>to_ts <唯一名>.dispatchEvent(<唯一名>.CustomEvent.new("...", {"detail": {...}})) del <唯一名> del <唯一名>to_ts- 该脚本要么在 worker 中执行(
worker属性),要么在主线程执行(type为py/mpy); - 执行完成后通过
CustomEvent把调用期间被访问过的导出字段字典({"detail": {"func_a": func_a, ...}})回传给 JS 侧; - JS 侧在
globalThis上用{ once: true }监听该唯一事件名,resolve(event.detail)后立刻script.remove(),保持 DOM 干净(见 bridge/index.js); - 若页面尚未引入
@pyscript/core,bridge 会先注入core.css并动态importcore.js(bridge/index.js)。
从源码结构看,这套"按需拉取 Python 源码 → 动态注入脚本 → 事件握手回传导出"的设计,让 JS 侧完全不需要手写任何py-script标签或解释器初始化代码。
四、本地测试:npx mini-coi起服务
README 提供了开箱即用的测试方式。在bridge/目录内执行:
npx mini-coi .然后浏览器访问http://localhost:8080/test/,页面会输出:
PyScript Bridge ------------------ no configmini-coi是一个本地静态服务器(仓库 core/tests/manual/service-worker/mini-coi.js 中亦有同名单文件),它会为响应附加Cross-Origin-Opener-Policy/Cross-Origin-Embedder-Policy等头,这正是 worker 模式与共享内存场景(如SharedArrayBuffer)所要求的跨源隔离前提。
测试页 bridge/test/index.html 通过importmap把https://esm.run/@pyscript/bridge映射到本地../index.js,再动态 import bridge/test/test.js。该test.js使用的默认参数为:
pyscript="2025.8.1"type="mpy"worker=falseconfig=undefinedenv=undefined
因此默认输出中的no config来自 bridge/test/test.py 的version()函数:它尝试from sys_version import version,失败时回退为lambda: "no config"——而sys_version.py恰好由config选项引入。
五、用查询参数切换各种变体
bridge/test/test.js 从location.search读取参数,因此无需改代码即可覆盖全部组合:
| 查询参数 | 效果 |
|---|---|
?type=py | 把type从默认mpy换成py(引导 Pyodide) |
?worker | 只要出现该参数(searchParams.has("worker")),worker即为true |
?config | 传入一个内联配置对象,其files指向同目录的./sys_version.py |
?env=xxx | 设置共享环境标识 |
例如组合变体:
http://localhost:8080/test/?type=py&worker&config此时输出变为:
PyScript Bridge ------------------ 3.12.7 (main, May 15 2025, 18:47:24) ...含义是:用 Pyodide(type=py)、在 Worker 中运行(worker),并通过config引导了sys_version.py,所以version()成功 import 到了 Python 版本号。README 特别提醒:一旦使用config,worker属性恒为true(由实现强制,见上文toggleAttribute)。
另一个可观察点是 bridge/test/test.py 会打印运行位置:
from pyscript import config, RUNNING_IN_WORKER type = config["type"] print(f"{type}-script", RUNNING_IN_WORKER and "worker" or "main")即每次调用都会在浏览器控制台输出形如mpy-script main或py-script worker的标记,可用于核对解释器类型与运行线程是否符合预期。
六、远程/CDN 使用示例
仓库还提供了完全基于 CDN 的远程演示页 bridge/test/remote/index.html:它用importmap把@pyscript/bridge与其test/test.js都指向esm.run上的@latest版本,并在页面里显式引入https://pyscript.net/releases/2025.5.1/core.css与core.js,随后直接await import(cdn_test)并调用test_func/test_other/version。这个页面印证了两点:一是bridge的产物是纯 ESM(bridge/package.json 中"type": "module",入口index.js),可以直接被浏览器原生模块系统解析;二是页面可以提前引入 PyScript Core,此时 bridge 会跳过自动加载流程。
七、小结
| 要点 | 结论 |
|---|---|
| 定位 | 纯 JS 侧导入并调用 Python 工具的桥接模块,npm 包@pyscript/bridge(当前版本 0.2.2,见 bridge/package.json) |
| 用法 | export const ffi = bridge(import.meta.url, { ... }),然后await ffi.xxx(args) |
| 核心选项 | pyscript/type/worker/config/env/serviceWorker |
| 强制约束 | 指定config时worker恒为true,避免主线程多配置冲突 |
| 底层机制 | 三级缓存 + Proxy 懒加载 + 动态<script>注入 +CustomEvent握手回传导出 |
| 本地验证 | npx mini-coi .后访问http://localhost:8080/test/,用查询参数切换解释器、线程与配置 |
对于希望在现有纯 JS 应用中"零改造"接入 PyScript 能力的团队,@pyscript/bridge提供了一个把.py文件当作可 import 模块的直通方案;其测试页与源码中层层缓存的设计,也可作为理解 PyScript Core 引导流程的入门切口。
- 前端
- 开发工具
【免费下载链接】pyscript
An open source platform for Python in the browser. https://pyscript.net Docs: https://docs.pyscript.net/ Try it: https://pyscript.com/ Community: https://discord.gg/HxvBtukrg2
相关推荐
cppimport 使用指南:直接从 Python 导入 C++
cppimport 使用指南:直接从 Python 导入 C++ cppimport 是一个强大的工具,允许开发者无缝地在 Python 程序中直接导入并执行
Hermes Static Hermes FFI 实战:用 Typed JavaScript 直接调用 C 函数
Hermes Static Hermes FFI 实战:用 Typed JavaScript 直接调用 C 函数 本指南围绕 Hermes 仓库中的 examp
语言运行时编译器移动开发Python-Skill Bridge:终极指南让Python无缝调用Virtuoso Skill函数
Python Skill Bridge:终极指南让Python无缝调用Virtuoso Skill函数 在现代EDA设计流程中,工程师们经常面临一个关键痛点:如
开发工具硬件开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考