# desktop_flask **Repository Path**: chwu666/desktop_flask ## Basic Information - **Project Name**: desktop_flask - **Description**: No description available - **Primary Language**: Unknown - **License**: BSD-3-Clause - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-10-10 - **Last Updated**: 2026-10-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README [English](https://gitee.com/chwu666/desktop_flask/blob/main/README.md) · [简体中文](https://gitee.com/chwu666/desktop_flask/blob/main/README.zh-CN.md) # desktop_flask 在**桌面窗口**里跑 Flask 应用:**WebView2 负责渲染,进程内的 WSGI 桥负责服务, 全程不开任何 TCP 端口。** > **Vibe Coding(人与 AI 协作开发)—— 这个项目由人和 AI 一起写。** > 作者定需求、架构决策与验收标准,并负责真机验证;AI agent 写大部分代码、测试与文档。 > 这里没有"生成完就发"的东西:每个不那么显然的结论都在真机上量过,而且把量到的数字留了下来。 > 分工与代价见文末「**这个项目是怎么做出来的**」。 `desktop_flask` 是独立的扩展库 —— 它**不修改 Flask 的源码**。你照旧 `from flask import Flask`,只加一行: ```python from flask import Flask import desktop_flask app = Flask(__name__) desktop_flask.init_app(app) # ← 唯一的"接线"动作 @app.get("/") def get_index(): return render_template("index.html") if __name__ == "__main__": app.run_desktop(title="我的工具") ``` 也可以完全不注入: ```python desktop_flask.run_desktop(app, title="我的工具") ``` 命令行三种等价入口: ```console flask --app myapp desktop # 走 flask.commands 入口点 flask --app myapp desktop --self-check # 内建 WebView2 自检(S1–S19) desktop-flask myapp:app --title "我的工具" # 不依赖 Flask CLI 的启动器 python -m desktop_flask myapp:create_app() # 同一个东西 ``` > **状态:`0.1.0.dev2` —— 开发者预览版。** 桥内核、Flask 集成、WebView2 宿主与打包 > 这条链都已经在真机上跑通并验过,但在 `0.1.0` 之前接口与 `DESKTOP_*` 配置键**仍可能调**; > 针对桌面应用的专项优化还在后面。破坏性改动一定会写进 > [CHANGELOG.md](https://gitee.com/chwu666/desktop_flask/blob/main/CHANGELOG.md)。 ## 安装 ```console pip install desktop_flask ``` Windows 上这样就够了:`pythonnet` 已经是**基础依赖**(`sys_platform == 'win32'` 条件生效),装完 `run_desktop()` 直接能用。老的 `desktop_flask[desktop]` 写法仍然接受(它现在是兼容别名)。 * 运行时需要 WebView2 **Runtime**:Windows 11 自带;较老的 Windows 10 可能没有。 `run_desktop(install_runtime=True)` 会征求同意后帮你装。 * WebView2 的**托管 DLL 与 native loader 已经打进包里**,不需要另外下载。 * **目前只有 Windows**。macOS / Linux 没有后端 —— `run_desktop()` 会明确报错, 而不是偷偷开一个端口。 > ⚠️ 这个预发行版**不要**在依赖里写版本**区间**:`desktop_flask>=0.1` 解析不出来, > 因为请求里带了限定符时 pip 会排除预发行版。裸 `pip install desktop_flask` 与 > `pip install desktop_flask==0.1.0.dev2` 都能装上。等 `0.1.0` 正式版发布之后, > 版本区间才开始生效,升级也会自动转到正式版。 > 国内网络提示:装依赖可以先配个镜像 > (`pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple`)。 > 镜像同步 PyPI 有几分钟延迟,**刚发布的版本在镜像上可能暂时查不到**, > 那不是发布失败,等一会儿再试。 ## 它改了什么 | | 普通的 `app.run()` | `desktop_flask` | |---|---|---| | 传输层 | 127.0.0.1 上的 TCP socket | 直接调用 `app.wsgi_app(environ, start_response)` | | 别的进程能访问吗 | 能 | **不能** —— 请求根本不离开这个进程 | | 端口冲突 / 防火墙弹窗 | 可能 | 不可能 | | 页面 origin | `http://127.0.0.1:5000` | `df://app`(已注册的自定义 scheme,tuple origin) | | Cookie | Chromium 管 | 桥自己管的 cookie jar(自定义 scheme 上 Chromium 的 cookie 不工作) | | WebSocket / SSE | 支持 | **不支持** —— 实时通道用 `postMessage`(`desktop_flask.emit`) | | 后端业务代码 | —— | **一行不用改** | ## 把自己的应用打成 exe ```console pip install "desktop_flask[desktop,build]" desktop-flask-build app.py ``` 产出 `build/app.dist/`,**发这个目录**(不是只发 exe)。 **Nuitka 还需要一个 C 编译器**(MSVC 或 MinGW64)—— `pip` 装不了它,这是一个 **装不掉的**门槛。缺编译器时 `desktop-flask-build` 会在开始那十分钟编译之前就告诉你。 "构建成功却跑不起来"的两个坑、以及分发前的核对清单,见 [docs/zh/packaging.md](https://gitee.com/chwu666/desktop_flask/blob/main/docs/zh/packaging.md)。 ## 离线读文档 文档**随包分发**,不联网也能读: ```console desktop-flask-docs # 列出页面(默认中文,内容最全的一份) desktop-flask-docs --en # 英文页面 desktop-flask-docs packaging --cat # 直接打印某一篇 desktop-flask-docs --open # 用默认程序打开索引 ``` ## 里面有什么 | | | |---|---| | **传输层** | `WSGIBridge` —— 从 WebView2 的请求构造 WSGI `environ`,调 `app.wsgi_app`,收响应。没有 socket,没有端口。 | | **worker 池** | `DESKTOP_WORKERS`(默认 4)—— 请求处理不再占着 UI 线程。理由是**吞吐**,不是"窗口会冻住"(WebView2 渲染在自己的进程里)。 | | **静态资源缓存** | 内存 LRU(默认 32 MiB),严格按 HTTP 语义,支持 ETag / 304 复验。 | | **实时通道** | `postMessage` 消息总线(`desktop_flask.emit()` / `desktopFlask.invoke()`)—— WebSocket 的替代。 | | **单实例** | `DESKTOP_SINGLE_INSTANCE` —— 一把以 `userDataFolder` 为键的命名内核互斥体,把"第二份悄悄坏掉"变成一句看得懂的提示。 | | **Runtime 检测 + 引导安装** | 读注册表检测、给出能照做的指引;可选用 `DESKTOP_INSTALL_RUNTIME` 下载并静默安装微软的 Evergreen 引导安装器。 | | **崩溃报告** | `sys.excepthook` + `threading.excepthook` + `sys.unraisablehook` + `faulthandler` 四条钩子,报告里带环境块。 | | **`userDataFolder` 管理** | `%LOCALAPPDATA%\\WebView2`,可覆盖,`DESKTOP_APP_ID` 是身份旋钮。 | | **打包** | `desktop-flask-build` —— Nuitka standalone / onefile,自动带上 vendor 的 DLL、图标,以及那几个"不说就静默失败"的参数。 | | **图标** | `scripts/make_icon.py` 用纯 Python 画出来;exe 图标与窗口图标都用它。 | | **内建自检** | `--self-check` —— 在**你这台机器**上开一个真 WebView2 窗口跑 S1–S19。 | ## 文档 | 文档 | 内容 | |---|---| | [docs/zh/README.md](https://gitee.com/chwu666/desktop_flask/blob/main/docs/zh/README.md) | 中文文档索引([英文在这里](https://gitee.com/chwu666/desktop_flask/blob/main/docs/README.md)) | | [docs/zh/configuration.md](https://gitee.com/chwu666/desktop_flask/blob/main/docs/zh/configuration.md) | 每一个 `DESKTOP_*` 配置键、`init_app()` / `run_desktop()` / CLI 选项,以及默认值背后的理由 | | [docs/zh/troubleshooting.md](https://gitee.com/chwu666/desktop_flask/blob/main/docs/zh/troubleshooting.md) | 真的会遇到的失败,每条都配上"哪一招能修好" | | [docs/zh/packaging.md](https://gitee.com/chwu666/desktop_flask/blob/main/docs/zh/packaging.md) | 用 Nuitka 打包、为什么不是 PyInstaller、冻结路径的两个坑、分发清单 | | [docs/zh/python-3.14-free-threading.md](https://gitee.com/chwu666/desktop_flask/blob/main/docs/zh/python-3.14-free-threading.md) | R17:自由线程(PEP 703 / PEP 779)能拿到什么、拿不到什么,以及要动它之前该准备什么 | 面向实现者的设计笔记(架构、实测发现、ADR)住在开发这个库的**载体仓库**里: `DESKTOP_MODE_DESIGN.md` 与 `m0_spike/`、`m1_bridge/`、`m3_host/`、 `m4_engineering/` 下的结论文档。它们**不随这个包分发**。 ## 边界(设计取舍,不是 bug) * **没有 WebSocket。** 构造函数本身就拒绝在自定义 scheme 上开非 `http(s)`/`ws(s)` 的 URL。请用 `desktop_flask.emit()` / `desktopFlask.invoke()`。 * **没有 SSE / 流式响应。** WebView2 要求 body 在 deferral 完成之前全部就绪。 进度请走消息桥。 * **Cookie 是桥自己管的,不是 Chromium 的。** `document.cookie` 恒为空是**预期**行为; `localStorage` / `IndexedDB` 正常。 * **没有 Service Worker**(Blink 要求 http/https)。 * **目前只有 Windows**,接口是按"可扩展"设计的。 ## 这个项目是怎么做出来的(Vibe Coding) 这是一个**人与 AI 协作**的项目,并且把"哪部分是谁做的"写在明面上。 | 谁 | 做了什么 | |---|---| | **人类作者** | 定目标与需求;做架构决策(用自定义 `df://` scheme 而不是本地端口、Cookie 由桥自管、不做 WebSocket、用 Nuitka 而不是 PyInstaller);定验收标准;**所有真机验证**;发布 | | **AI agent** | 大部分实现代码、测试套件、中英双语文档、发布脚本 | 为什么仍然可以信它: * **没有哪一条是"看着对就留下"的。** 文档里每个不那么显然的结论都带着出处 —— 一次实测、一句官方文档原文,或者一次**失败的真机运行**逼出来的改法。最费时间的 那几个 WebView2 坑是**记下来**而不是藏起来的:`--include-data-dir` 静默丢掉 `.dll`;Nuitka 根本不设 `sys.frozen`;pythonnet 逐元素转换 `bytes` 比一次 `Marshal.Copy` 慢约 1000 倍。 * **闸门是真的。** 开发树 942 项测试、未修改的官方 Flask 环境 450 项、 `ruff` + `mypy --strict`,以及一个在真 Windows 桌面上通过内建自检 **15 通过 / 0 失败 / 4 信息项** 的打包产物。 * **边界是写出来的,不是糊过去的。** 仅 Windows、无 WebSocket/SSE、无流式响应、 Cookie 由桥自管 —— 全部列在「边界」一节里。 因此你该预期什么: * 注释与文档**密度异常高** —— AI 比人类更愿意写解释。在这个项目里这是有意的, 但你可能并不喜欢这种风格。 * 英文文档是译文:**中文是这个项目的一等公民**,也是内容更全的一份。 * 如果你发现一个"很像 AI 犯的"错误(自信地写错、或者从相似的场景照搬过来), 开 issue 是最快的修法 —— 这类报告正是这种开发方式最需要的。 ## 它不是 Flask 的一部分 `desktop_flask` 是独立的扩展库:**不是** Flask 的一部分,**与 Pallets 组织没有隶属 或背书关系**。它以微软自己的许可原样重分发 WebView2 的程序集。完整归属见 [LICENSE.txt](https://gitee.com/chwu666/desktop_flask/blob/main/LICENSE.txt)(BSD-3-Clause) 与 [NOTICE.md](https://gitee.com/chwu666/desktop_flask/blob/main/NOTICE.md)。 ## 目录结构 本目录**就是**这个库的仓库根 —— `src/`、`tests/`、`scripts/`、`docs/`、`examples/` 都是它的同级目录,所以整个目录可以直接推成一个独立仓库。 ``` src/desktop_flask/ 库本体 ├── bridge.py WSGI 桥:environ 构造、WSGI 调用、响应捕获 ├── adapter.py 让 url_for(_external=True) 产出 df:// URL ├── cookies.py 桥自管的 cookie jar ├── bodystore.py 请求体/响应体走内存还是临时文件 ├── cache.py 静态资源 LRU + HTTP 条件请求 ├── pipeline.py 缓存 + 桥(不碰宿主,可单测) ├── workers.py 请求线程池 + deferral 生命周期 ├── runtime.py 单实例 / 崩溃报告 / Runtime 检测与引导安装 ├── _frozen.py "这是不是打包产物"(Nuitka 不设 sys.frozen) ├── origin.py origin 策略(df://app、http://df.localhost) ├── compat.py 兼容层:legacy origin 转发、体改写、导航白名单 ├── messages.py postMessage 消息总线(WebSocket 的替代) ├── selfcheck.py 内建自检页 S1–S19 ├── server.py DebugHttpServer(仅开发用,会开端口) ├── integration.py init_app():运行时接线,不改 Flask ├── packaging.py desktop-flask-build(Nuitka) ├── docs.py desktop-flask-docs(随包文档) ├── cli.py `flask desktop` ├── run.py / __main__.py `desktop-flask` 启动器 ├── assets/ 图标(由 scripts/make_icon.py 生成) ├── _vendor/webview2/ WebView2 托管 DLL + native loader(真 vendor 进来的) └── platforms/windows.py WebView2 宿主(pythonnet + WinForms) docs/ 英文文档 docs/zh/ 中文文档 scripts/ fetch_webview2_dlls.py、make_icon.py、build_nuitka.py、frozen_probe.py tests/ 测试套件 examples/desktop_demo/ 能"看见"缓存 / worker / 消息桥的示例应用 examples/tutorial/ 官方 Flask 教程,零修改跑通(验收用) pyproject.toml 发行版元数据 LICENSE.txt / NOTICE.md 许可证与第三方归属 CHANGELOG.md 改了什么,以及版本策略 ```