如何在 tauri 中使用 python 实现后端
介绍如何在 Tauri 应用中使用 PyTauri 集成 Python 后端,充分利用 Python 的 AI 生态优势,并详细说明项目搭建、IPC 通信和打包发布流程。
PyTauri 可以在 Tauri 中使用 Python 实现后端。
一定会有人质疑,我有 Rust 不用为什么要用 Python?
Tauri 中使用 Python 最大的一个场景是集成 Python 的 AI 的生态。虽然我们也可以在 Tauri 中使用tch-rs,onnx等,但是难度上终究不是一个等级的。使用 Python 做 AI 推理实在是太简单了。
由于生态问题,很多 AI 模型其实很难通过 Rust 来集成到 Tauri 中。
(那为什么不用 pywebview 呢 ?:》 )
PyTauri 使用 PyO3 重新绑定了 Tauri 的 Rust API,实现了 Python 和 JavaScript 的交互。具体来说,在运行时阶段:
- Tauri 前端通过 IPC 调用发送请求到 Rust 后端
- Rust 后端通过 invoke_handler 将调用转发到 Python 函数
- Python 处理业务逻辑并通过扩展模块调用 Tauri API
- 结果通过异步 Future 机制返回给前端
uvx copier copy https://github.com/pytauri/create-pytauri-app .
uv venv --python-preference only-system
source .venv/bin/activate
uv pip install -e src-tauri
如果单纯地使用uv sync 会得到如下类似的错误:
error: linking with `cc` failed: exit status: 1
|
= note: "cc" "-Wl,-exported_symbols_list" "-Wl,/var/folders/m4/nln9zc4j1ln3ctqj_q_gd3s80000gn/T/rustcIvgbkZ/list" "/var/folders/m4/nln9zc4j1ln3ctqj_q_gd3s80000gn/T/rustcIvgbkZ/symbols.o" "<130 object files omitted>" "/Users/xxx/.cache/target/debug/deps/"CoreGraphics"
...
...
"-framework" "CloudKit" "-framework" "QuartzCore" "-framework" "Foundation" "-framework" "CoreFoundation" "-lobjc" "-framework" "Foundation" "-lpython3.12" "-liconv" "-lSystem" "-lc" "-lm" "-arch" "arm64" "-mmacosx-version-min=11.0.0" "-L" "/Users/xxx/.cache/target/debug/build/objc2-exception-helper-c628b52639d8b792/out" "-L" "/install/lib" "-o" "/Users/xxx/.cache/target/debug/deps/libmatting_lib.dylib" "-Wl,-dead_strip" "-dynamiclib" "-nodefaultlibs"
= note: some arguments are omitted. use `--verbose` to show all linker arguments
= note: ld: warning: ignoring duplicate libraries: '-lSystem'
ld: warning: search path '/install/lib' not found
ld: library 'python3.12' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: could not compile `matting` (lib) due to 1 previous error
因此 --python-preference only-system 是必要的。使用 uv 的管理 Python 可能会导致找不到动态库。
IPC
由于 PyTauri 导出了 Rust API,因此前端可以调用 Python 定义的 Commands。
from pytauri import AppHandle, Commands
commands = Commands()
@commands.command()
async def command(in_body: bytes) -> bytes: ...
在 Tauri 中,Rust 定义的参数名默认以下划线in_body连接,这是 Rust 的规范。在前端调用时,参数名会以驼峰式 inBody来满足 JavaScript 的规范。
PyTauri 目前的版本有点奇怪,由于 Python 大多也是以下划线命名的,但是在前端调用中,参数名需要保持一致,也就是in_body。
import { pyInvoke } from "tauri-plugin-pytauri-api";
const output = await pyInvoke<string>("command", { in_body: ...});
从 python 调用前端
PyTauri 可以使用通道来快速传递有序数据。主要用于一些流式传输操作, 例如下载进度、子进程输出和 WebSocket 消息。
from pytauri.ipc import Channel, JavaScriptChannelId
Msg = RootModel[str]
@commands.command()
async def command(
in_body: JavaScriptChannelId[Msg], webview_window: WebviewWindow
) -> None:
channel: Channel[Msg] = body.channel_on(webview_window.as_ref_webview())
channel.send_model(Msg("发送数据"))
前端使用
const channel = new Channel<string>((msg) => console.log(msg));
await pyInvoke("command", channel);
当然,Pytauri 也导出了事件系统。
其他地方差别不大。当然,使用 Pytauri 我们依然可以使用 Rust 来开发。
打包
打包需要嵌入 Python 实例。Pytauri 使用的是 uv 官方打包的python-build-standalone 作为分发实例。
下载解压到 src-tauri/pyembed 。
├── src-tauri/pyembed/python
├── bin/
├── include/
├── lib/
└── share/
把项目依赖安装到这个分发的实例环境中:
export PYTAURI_STANDALONE="1"
# tauri-app 是你的项目包名
uv pip install \
--exact \
--python="./src-tauri/pyembed/python/bin/python3" \
--reinstall-package=tauri-app \
./src-tauri
指定 pyo3 使用的Python 运行时:
export PYO3_PYTHON=$(realpath ./src-tauri/pyembed/python/bin/python3)
打包:
pnpm -- tauri build --config="src-tauri/tauri.bundle.json" -- --profile bundle-release
另外,其实 pywebview 就是 python 版的 tauri。如果你只是想用 python 的生态,并且没有多少代码必须使用 rust ,那么 pywebview 就可以了。