如何在 tauri 中使用 python 实现后端

介绍如何在 Tauri 应用中使用 PyTauri 集成 Python 后端,充分利用 Python 的 AI 生态优势,并详细说明项目搭建、IPC 通信和打包发布流程。

发布于 约 2 分钟

PyTauri 可以在 Tauri 中使用 Python 实现后端。

一定会有人质疑,我有 Rust 不用为什么要用 Python?

Tauri 中使用 Python 最大的一个场景是集成 Python 的 AI 的生态。虽然我们也可以在 Tauri 中使用tch-rsonnx等,但是难度上终究不是一个等级的。使用 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 就可以了。