On Windows `delete_cache` ages cached files by creation time, so reusing a cached copy does not keep it alive (and `test_save_file_to_cache_

这个报错通常出现在 Windows 上启用了 delete_cache 的 Gradio 应用,或直接运行 test_save_file_to_cache_refreshes_ctime_of_a_reused_copy 时。优先排查缓存文件过期判定是否仍基于 st_ctime :Windows 的

快速结论:这个报错通常出现在 Windows 上启用了 delete_cache 的 Gradio 应用,或直接运行 test_save_file_to_cache_refreshes_ctime_of_a_reused_copy 时。优先排查缓存文件过期判定是否仍基于 st_ctime:Windows 的 st_ctime 是创建时间,os.utime 只改 mtime,导致“复用缓存副本”的时间刷新没有生效。

适用环境:Gradio 6.30.0(main at b3c050c)、Python 3.11.9、Windows 11;评论复现环境为 Windows 11 / Python 3.11.14。Issue 未确认 CUDA、显卡或 PyTorch 相关问题。

最快修复方案:暂无确认的一步修复方案。Issue 中的首选思路是可优先尝试的:把 delete_files_created_by_app 中按 lstat().st_ctime 判断文件年龄改为按 max(st_ctime, st_mtime) 判断,并同步修正 processing_utils.py 中过时的 delete_cache ages files by ctime 注释;该思路已在 Issue 提交者及评论者的本地 Windows 环境中验证修复了复现测试。

注意事项:该修改尚未在本 Issue 讨论中合并到上游;Issue 提交者的分支未开 PR,评论者分支可用于 cherry-pick。修复后 test/test_processing_utils.py 仍有三个失败(两个依赖网络的 @pytest.mark.flaky 测试、一个依赖 ffmpeg 版本的 MD5 测试),据评论者说明它们在干净的 main 上同样失败,属于既有问题,不是此修复引入。

问题场景

用户在 Windows 上使用 Gradio,并通过 delete_cache 启用临时文件/缓存清理。Gradio 在 processing_utils.save_file_to_cache 中复用已有缓存副本时会调用 os.utime(...),原意是刷新文件时间,避免仍在被提供的缓存文件被 route_utils.delete_files_created_by_app 按“创建后经过的时长”误删。但在 Windows 上这个刷新实际无效。

同一问题也直接表现为仓库自带测试在 Windows 上失败:

pytest test/test_processing_utils.py::TestTempFileManagement::test_save_file_to_cache_refreshes_ctime_of_a_reused_copy

Issue 说明 Windows CI 只在打 windows-tests 标签时运行,且配置为 continue-on-error,所以该失败没有在 CI 中暴露。

报错原文

E   AssertionError: assert 1792903437845489300 > 1792903437845489300

关联的代码语义是 delete_cache ages cached files by creation time;Issue 标题中的完整描述为:On Windows delete_cache ages cached files by creation time, so reusing a cached copy does not keep it alive (and test_save_file_to_cache_refreshes_ctime_of_a_reused_copy fails)。

原因分析

根因是不同平台对 st_ctime 的语义不一致:

  • route_utils.delete_files_created_by_app 使用 lstat().st_ctime 判断文件年龄。
  • 在 POSIX 上,os.utime 会更新 ctime,因此 save_file_to_cache 的刷新是有效的,缓存副本被复用时其年龄会重新计算。
  • 在 Windows 上,st_ctime 表示文件创建时间,os.utime 只改变 mtime,因此刷新不改变删除逻辑看到的时间。结果是仍在被提供的缓存副本可能按其首次使用的时间被删除。

评论中进一步指出:如果只改为按 mtime 计算年龄,会引入新的问题——shutil.copy2 会把源文件的旧 mtime 复制给新缓存文件,一个长期未修改的源文件刚生成缓存副本时就可能被当成“过期”立即删除。因此 Issue 与评论共同倾向的修法是取 max(st_ctime, st_mtime):POSIX 上 st_ctime 不早于 st_mtime,行为不变;Windows 上 os.utime 的刷新能生效;同时避免仅按 mtime 判断导致新副本被误删。

环境排查

  • 确认操作系统是否为 Windows(Issue 仅在 Windows 上复现),以及是否启用了 delete_cache 参数。
  • 确认 Gradio 版本与提交:Issue 为 6.30.0,main at b3c050c。
  • 确认 Python 版本:Issue 为 3.11.9,评论复现为 3.11.14。
  • 确认是否直接运行 test_save_file_to_cache_refreshes_ctime_of_a_reused_copy,以及该测试在当前代码上是否仍按 st_ctime 断言。
  • 检查 route_utils.delete_files_created_by_app 是否仍使用 lstat().st_ctime 计算年龄。
  • 检查 processing_utils.py 中是否仍保留 delete_cache ages files by ctime 注释;若有,说明可能尚未应用修复。
  • Issue 未涉及 CUDA、显卡、PyTorch 或节点依赖版本,无需按这些项目排查。

解决步骤

  1. 定位 route_utils.delete_files_created_by_app 中读取文件时间的位置,确认使用的是 lstat().st_ctime。
  2. 可优先尝试 Issue 提出的修改:将年龄判断改为基于 max(st_ctime, st_mtime)(如代码中已使用纳秒字段,则对应 max(st_ctime_ns, st_mtime_ns)),使 Windows 上 os.utime 刷新后的 mtime 能够参与过期判断,同时保留 POSIX 上原有的 ctime 语义。
  3. 同步更新 processing_utils.py 中 delete_cache ages files by ctime 的过时注释,避免误导后续排查。
  4. 修正测试:把 test_save_file_to_cache_refreshes_ctime_of_a_reused_copy 改为断言删除逻辑实际使用的值,即 max(st_ctime_ns, st_mtime_ns),而不是单独断言 ctime。
  5. 补充端到端测试:评论中新增的三个测试可作为参考——keeps_a_reused_copy(Windows 上未修复时应失败)、deletes_a_stale_copy(未复用的旧副本仍会被清理)、keeps_a_fresh_copy_of_an_old_file(防止仅按 mtime 判断导致旧源文件的新副本被误删)。
  6. 如果不想直接改代码,可参考 Issue 中给出的可 cherry-pick 分支:提交者为 Sonica-B:gradio:fix/cache-file-age-uses-mtime-on-windows(1 个提交,2 个文件,+30 −4),评论者为 ApexYash11:fix/cache-file-age-windows(1 个提交,3 个文件,+82 −8)。两者均未合入上游,使用前需自行评估。

验证方法

在 Windows 上重新运行受影响测试,确认不再出现 assert ... > ... 失败:

pytest test/test_processing_utils.py::TestTempFileManagement::test_save_file_to_cache_refreshes_ctime_of_a_reused_copy
pytest test/test_processing_utils.py

按评论者验证结果,修复后新回归测试应在未修改的删除逻辑上失败、在应用 max(st_ctime, st_mtime) 后通过;keeps_a_reused_copy 应在 Windows 上验证复用缓存副本不被 delete_files_created_by_app(blocks, age=0.5) 删除,同时 deletes_a_stale_copy 验证未被复用的旧缓存仍会被清理。若 test/test_processing_utils.py 仍有失败,先确认是否为评论中提到的两个 @pytest.mark.flaky 网络测试和 ffmpeg 版本相关的 MD5 测试,并对照未修改的 main 是否同样失败。代码风格可运行 ruff check 与 ruff format --check(评论使用 0.12.9,依据 test/requirements.txt)。

参考来源

gradio-app/gradio #14009

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 34619

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注