fsspec ZIP close 后目标仍不存在,检查 simplecache 外层提交
fsspec 2026.9.0 的 ZIP close 只结束归档层,simplecache 外层文件可能仍未提交。显式管理外层上下文,先完成 ZIP,再关闭并从目标重新读取校验。
通过 simplecache:: 写入 ZIP,调用 ZipFileSystem.close() 没报错,目标路径却仍不存在;删除对象、等垃圾回收后,文件反而出现。这可能是归档与外层缓存文件只关闭了一层。
fsspec 2026.9.0 的正式 ZipFileSystem.close() 只关闭内部 zipfile.ZipFile。若外层文件由 ZIP 文件系统自己打开,退出它的上下文则留在析构路径。simplecache 写入文件要在关闭时提交目标,因而持有已经关闭的 ZIP 文件系统对象,仍可能拖住这一步。
由应用显式管理外层文件
应用可以先打开外层写入上下文,再让 ZIP 写入器使用这个文件对象,并保证先写完 ZIP 尾部,再退出外层上下文。以下示例使用独立的内存目标,不会写云端对象;cache_dir 应传入应用自有、可清理的缓存目录:
import io
import uuid
import zipfile
import fsspec
def make_archive(cache_dir: str) -> bytes:
name = f"archive-{uuid.uuid4().hex}.zip"
with fsspec.open(
f"simplecache::memory://{name}",
"wb",
simplecache={"cache_storage": cache_dir},
) as outer:
with zipfile.ZipFile(outer, mode="w") as archive:
archive.writestr("data.jsonl", b'{"text":"example"}\n')
memory = fsspec.filesystem("memory")
payload = memory.cat_file(name)
with zipfile.ZipFile(io.BytesIO(payload), "r") as archive:
assert archive.read("data.jsonl") == b'{"text":"example"}\n'
return payload
这里用标准库 ZIP 写入器建立清楚的所有权:它关闭时先完成 ZIP 目录,外层 fsspec.open 随后关闭缓存文件并提交目标。若业务需要 ZipFileSystem 的其他接口,也应保持相同的关闭顺序,并确认文件对象究竟由谁负责关闭。
不要先关闭外层文件再让 ZIP 写入器写目录;那会把提交时机问题变成不完整归档或已关闭文件错误。也不要把 del fs; gc.collect() 当成提交协议,引用关系和垃圾回收时机都不是可靠的业务边界。
close 成功后,仍要核对真正的目标
检查目标文件存在,并从目标后端重新读取归档,核对成员名和内容;只在本地 simplecache 路径找到文件,不能证明上传已经发生。远程对象存储的权限、网络失败和重试策略仍可能让提交失败,内存示例不覆盖这些条件。
写入业务目标时,应使用独立的新对象名,验证完成后再按后端支持的方式切换引用,避免为了重试覆盖唯一的有效副本。保留外层关闭阶段抛出的异常,不能只看 ZIP 内层关闭是否成功。
升级 fsspec 后,也应在对象仍被持有的条件下检查目标可读,而不是等待析构恰好替应用完成关闭。


